mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-25 10:59:35 +00:00
Allow to cache amazon search results to reduce API calls
This commit is contained in:
parent
258289482b
commit
87919eb445
2 changed files with 60 additions and 5 deletions
|
|
@ -30,6 +30,7 @@ use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
|
|||
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||
use App\Settings\InfoProviderSystem\BuerklinSettings;
|
||||
use App\Settings\InfoProviderSystem\CanopySettings;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\When;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
|
|
@ -45,7 +46,8 @@ class CanopyProvider implements InfoProviderInterface
|
|||
|
||||
public const DISTRIBUTOR_NAME = 'Amazon';
|
||||
|
||||
public function __construct(private readonly CanopySettings $settings, private readonly HttpClientInterface $httpClient)
|
||||
public function __construct(private readonly CanopySettings $settings,
|
||||
private readonly HttpClientInterface $httpClient, private readonly CacheItemPoolInterface $partInfoCache)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -76,6 +78,39 @@ class CanopyProvider implements InfoProviderInterface
|
|||
return "https://www.amazon.{$this->settings->domain}/dp/{$asin}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given part to the cache.
|
||||
* Everytime this function is called, the cache is overwritten.
|
||||
* @param PartDetailDTO $part
|
||||
* @return void
|
||||
*/
|
||||
private function saveToCache(PartDetailDTO $part): void
|
||||
{
|
||||
$key = 'canopy_part_'.$part->provider_id;
|
||||
|
||||
$item = $this->partInfoCache->getItem($key);
|
||||
$item->set($part);
|
||||
$item->expiresAfter(3600 * 24); //Cache for 1 day
|
||||
$this->partInfoCache->save($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a from the cache, or null if it was not cached yet.
|
||||
* @param string $id
|
||||
* @return PartDetailDTO|null
|
||||
*/
|
||||
private function getFromCache(string $id): ?PartDetailDTO
|
||||
{
|
||||
$key = 'canopy_part_'.$id;
|
||||
|
||||
$item = $this->partInfoCache->getItem($key);
|
||||
if ($item->isHit()) {
|
||||
return $item->get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function searchByKeyword(string $keyword): array
|
||||
{
|
||||
$response = $this->httpClient->request('GET', self::SEARCH_API_URL, [
|
||||
|
|
@ -93,14 +128,20 @@ class CanopyProvider implements InfoProviderInterface
|
|||
|
||||
$out = [];
|
||||
foreach ($results as $result) {
|
||||
$out[] = new SearchResultDTO(
|
||||
|
||||
|
||||
$dto = new PartDetailDTO(
|
||||
provider_key: $this->getProviderKey(),
|
||||
provider_id: $result['asin'],
|
||||
name: $result["title"],
|
||||
description: "",
|
||||
preview_image_url: $result["mainImageUrl"] ?? null,
|
||||
provider_url: $this->productPageFromASIN($result['asin']),
|
||||
vendor_infos: [$this->priceToPurchaseInfo($result['price'], $result['asin'])]
|
||||
);
|
||||
|
||||
$out[] = $dto;
|
||||
$this->saveToCache($dto);
|
||||
}
|
||||
|
||||
return $out;
|
||||
|
|
@ -125,11 +166,15 @@ class CanopyProvider implements InfoProviderInterface
|
|||
return $notes;
|
||||
}
|
||||
|
||||
private function priceToPurchaseInfo(array $price, string $asin): PurchaseInfoDTO
|
||||
private function priceToPurchaseInfo(?array $price, string $asin): PurchaseInfoDTO
|
||||
{
|
||||
$priceDto = new PriceDTO(minimum_discount_amount: 1, price: (string) $price['value'], currency_iso_code: $price['currency'], includes_tax: true);
|
||||
$priceDtos = [];
|
||||
if ($price !== null) {
|
||||
$priceDtos[] = new PriceDTO(minimum_discount_amount: 1, price: (string) $price['value'], currency_iso_code: $price['currency'], includes_tax: true);
|
||||
}
|
||||
|
||||
return new PurchaseInfoDTO(self::DISTRIBUTOR_NAME, order_number: $asin, prices: [$priceDto], product_url: $this->productPageFromASIN($asin));
|
||||
|
||||
return new PurchaseInfoDTO(self::DISTRIBUTOR_NAME, order_number: $asin, prices: $priceDtos, product_url: $this->productPageFromASIN($asin));
|
||||
}
|
||||
|
||||
public function getDetails(string $id): PartDetailDTO
|
||||
|
|
@ -139,6 +184,11 @@ class CanopyProvider implements InfoProviderInterface
|
|||
throw new \InvalidArgumentException("The id must be a valid ASIN (10 characters, letters and numbers)");
|
||||
}
|
||||
|
||||
//Use cached details if available and the settings allow it, to avoid unnecessary API requests, since the search results already contain most of the details
|
||||
if(!$this->settings->alwaysGetDetails && ($cached = $this->getFromCache($id)) !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$response = $this->httpClient->request('GET', self::DETAIL_API_URL, [
|
||||
'query' => [
|
||||
'asin' => $id,
|
||||
|
|
|
|||
|
|
@ -43,4 +43,9 @@ class CanopySettings
|
|||
public ?string $apiKey = null;
|
||||
|
||||
public string $domain = "de";
|
||||
|
||||
/**
|
||||
* @var bool If true, the provider will always retrieve details for a part, resulting in an additional API request
|
||||
*/
|
||||
public bool $alwaysGetDetails = false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue