mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-25 02:49: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\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||||
use App\Settings\InfoProviderSystem\BuerklinSettings;
|
use App\Settings\InfoProviderSystem\BuerklinSettings;
|
||||||
use App\Settings\InfoProviderSystem\CanopySettings;
|
use App\Settings\InfoProviderSystem\CanopySettings;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\When;
|
use Symfony\Component\DependencyInjection\Attribute\When;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
|
@ -45,7 +46,8 @@ class CanopyProvider implements InfoProviderInterface
|
||||||
|
|
||||||
public const DISTRIBUTOR_NAME = 'Amazon';
|
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}";
|
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
|
public function searchByKeyword(string $keyword): array
|
||||||
{
|
{
|
||||||
$response = $this->httpClient->request('GET', self::SEARCH_API_URL, [
|
$response = $this->httpClient->request('GET', self::SEARCH_API_URL, [
|
||||||
|
|
@ -93,14 +128,20 @@ class CanopyProvider implements InfoProviderInterface
|
||||||
|
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($results as $result) {
|
foreach ($results as $result) {
|
||||||
$out[] = new SearchResultDTO(
|
|
||||||
|
|
||||||
|
$dto = new PartDetailDTO(
|
||||||
provider_key: $this->getProviderKey(),
|
provider_key: $this->getProviderKey(),
|
||||||
provider_id: $result['asin'],
|
provider_id: $result['asin'],
|
||||||
name: $result["title"],
|
name: $result["title"],
|
||||||
description: "",
|
description: "",
|
||||||
preview_image_url: $result["mainImageUrl"] ?? null,
|
preview_image_url: $result["mainImageUrl"] ?? null,
|
||||||
provider_url: $this->productPageFromASIN($result['asin']),
|
provider_url: $this->productPageFromASIN($result['asin']),
|
||||||
|
vendor_infos: [$this->priceToPurchaseInfo($result['price'], $result['asin'])]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$out[] = $dto;
|
||||||
|
$this->saveToCache($dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $out;
|
return $out;
|
||||||
|
|
@ -125,11 +166,15 @@ class CanopyProvider implements InfoProviderInterface
|
||||||
return $notes;
|
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
|
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)");
|
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, [
|
$response = $this->httpClient->request('GET', self::DETAIL_API_URL, [
|
||||||
'query' => [
|
'query' => [
|
||||||
'asin' => $id,
|
'asin' => $id,
|
||||||
|
|
|
||||||
|
|
@ -43,4 +43,9 @@ class CanopySettings
|
||||||
public ?string $apiKey = null;
|
public ?string $apiKey = null;
|
||||||
|
|
||||||
public string $domain = "de";
|
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