mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-05-10 15:12:12 +00:00
Allow to pass options to circumvent caching of info provider results / force fresh
Some checks are pending
Build assets artifact / Build assets artifact (push) Waiting to run
Docker Image Build / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / merge (push) Blocked by required conditions
Docker Image Build (FrankenPHP) / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / merge (push) Blocked by required conditions
Static analysis / Static analysis (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Waiting to run
Some checks are pending
Build assets artifact / Build assets artifact (push) Waiting to run
Docker Image Build / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / merge (push) Blocked by required conditions
Docker Image Build (FrankenPHP) / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / merge (push) Blocked by required conditions
Static analysis / Static analysis (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Waiting to run
This commit is contained in:
parent
f13413a104
commit
4137bde194
7 changed files with 64 additions and 13 deletions
|
|
@ -40,6 +40,7 @@ use App\Services\Attachments\AttachmentSubmitHandler;
|
|||
use App\Services\Attachments\PartPreviewGenerator;
|
||||
use App\Services\EntityMergers\Mergers\PartMerger;
|
||||
use App\Services\InfoProviderSystem\PartInfoRetriever;
|
||||
use App\Services\InfoProviderSystem\Providers\InfoProviderInterface;
|
||||
use App\Services\LogSystem\EventCommentHelper;
|
||||
use App\Services\LogSystem\HistoryHelper;
|
||||
use App\Services\LogSystem\TimeTravel;
|
||||
|
|
@ -283,7 +284,10 @@ final class PartController extends AbstractController
|
|||
{
|
||||
$this->denyAccessUnlessGranted('@info_providers.create_parts');
|
||||
|
||||
$dto = $infoRetriever->getDetails($providerKey, $providerId);
|
||||
//Force info providers to not use cache, when retrieving part details for creating a new part, because otherwise we might end up with outdated information
|
||||
$no_cache = $request->query->getBoolean('no_cache', false);
|
||||
|
||||
$dto = $infoRetriever->getDetails($providerKey, $providerId, [InfoProviderInterface::OPTION_NO_CACHE => $no_cache]);
|
||||
$new_part = $infoRetriever->dtoToPart($dto);
|
||||
|
||||
if ($new_part->getCategory() === null || $new_part->getCategory()->getID() === null) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ final class PartInfoRetriever
|
|||
* Search for a keyword in the given providers. The results can be cached
|
||||
* @param string[]|InfoProviderInterface[] $providers A list of providers to search in, either as provider keys or as provider instances
|
||||
* @param string $keyword The keyword to search for
|
||||
* @param array<string, mixed> $options An associative array of options which can be used to modify the search behavior. The supported options depend on the provider and should be documented in the provider's documentation.
|
||||
* @return SearchResultDTO[] The search results
|
||||
* @throws InfoProviderNotActiveException if any of the given providers is not active
|
||||
* @throws ClientException if any of the providers throws an exception during the search
|
||||
|
|
@ -60,7 +61,7 @@ final class PartInfoRetriever
|
|||
* @throws TransportException if any of the providers throws an exception during the search
|
||||
* @throws OAuthReconnectRequiredException if any of the providers throws an exception during the search that indicates that the OAuth token needs to be refreshed
|
||||
*/
|
||||
public function searchByKeyword(string $keyword, array $providers): array
|
||||
public function searchByKeyword(string $keyword, array $providers, array $options = []): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
|
|
@ -89,15 +90,31 @@ final class PartInfoRetriever
|
|||
* Search for a keyword in the given provider. The result is cached for 7 days.
|
||||
* @return SearchResultDTO[]
|
||||
*/
|
||||
protected function searchInProvider(InfoProviderInterface $provider, string $keyword): array
|
||||
protected function searchInProvider(InfoProviderInterface $provider, string $keyword, array $options = []): array
|
||||
{
|
||||
//Generate key and escape reserved characters from the provider id
|
||||
$escaped_keyword = hash('xxh3', $keyword);
|
||||
return $this->partInfoCache->get("search_{$provider->getProviderKey()}_{$escaped_keyword}", function (ItemInterface $item) use ($provider, $keyword) {
|
||||
|
||||
$no_cache = $options[InfoProviderInterface::OPTION_NO_CACHE] ?? false;
|
||||
|
||||
//Exclude the no_cache option from the options hash, since it should not affect the cache key, as it only determines whether to bypass the cache or not, but does not change the actual search results
|
||||
$options_without_cache = $options;
|
||||
unset($options_without_cache[InfoProviderInterface::OPTION_NO_CACHE]);
|
||||
//Generate a hash for the options, to ensure that different options result in different cache entries
|
||||
$options_hash = hash('xxh3', json_encode($options_without_cache, JSON_THROW_ON_ERROR));
|
||||
|
||||
$cache_key = "search_{$provider->getProviderKey()}_{$escaped_keyword}_{$options_hash}";
|
||||
|
||||
//If no_cache is set, bypass the cache and get fresh results from the provider
|
||||
if ($no_cache) {
|
||||
$this->partInfoCache->delete($cache_key);
|
||||
}
|
||||
|
||||
return $this->partInfoCache->get($cache_key, function (ItemInterface $item) use ($provider, $keyword, $options) {
|
||||
//Set the expiration time
|
||||
$item->expiresAfter(!$this->debugMode ? self::CACHE_RESULT_EXPIRATION : 10);
|
||||
|
||||
return $provider->searchByKeyword($keyword);
|
||||
return $provider->searchByKeyword($keyword, $options);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -106,10 +123,11 @@ final class PartInfoRetriever
|
|||
* The result is cached for 4 days.
|
||||
* @param string $provider_key
|
||||
* @param string $part_id
|
||||
* @param array<string, mixed> $options An associative array of options which can be used to modify the search behavior. The supported options depend on the provider and should be documented in the provider's documentation.
|
||||
* @return PartDetailDTO
|
||||
* @throws InfoProviderNotActiveException if the the given providers is not active
|
||||
*/
|
||||
public function getDetails(string $provider_key, string $part_id): PartDetailDTO
|
||||
public function getDetails(string $provider_key, string $part_id, array $options = []): PartDetailDTO
|
||||
{
|
||||
$provider = $this->provider_registry->getProviderByKey($provider_key);
|
||||
|
||||
|
|
@ -118,13 +136,26 @@ final class PartInfoRetriever
|
|||
throw InfoProviderNotActiveException::fromProvider($provider);
|
||||
}
|
||||
|
||||
//Exclude the no_cache option from the options hash, since it should not affect the cache key, as it only determines whether to bypass the cache or not, but does not change the actual search results
|
||||
$options_without_cache = $options;
|
||||
unset($options_without_cache[InfoProviderInterface::OPTION_NO_CACHE]);
|
||||
//Generate a hash for the options, to ensure that different options result in different cache entries
|
||||
$options_hash = hash('xxh3', json_encode($options_without_cache, JSON_THROW_ON_ERROR));
|
||||
|
||||
//Generate key and escape reserved characters from the provider id
|
||||
$escaped_part_id = hash('xxh3', $part_id);
|
||||
return $this->partInfoCache->get("details_{$provider_key}_{$escaped_part_id}", function (ItemInterface $item) use ($provider, $part_id) {
|
||||
$cache_key = "details_{$provider_key}_{$escaped_part_id}_{$options_hash}";
|
||||
|
||||
//Delete the cache entry if no_cache is set, to ensure that the next get call will fetch fresh data from the provider, instead of returning stale data from the cache.
|
||||
if ($options[InfoProviderInterface::OPTION_NO_CACHE] ?? false) {
|
||||
$this->partInfoCache->delete($cache_key);
|
||||
}
|
||||
|
||||
return $this->partInfoCache->get($cache_key, function (ItemInterface $item) use ($provider, $part_id, $options) {
|
||||
//Set the expiration time
|
||||
$item->expiresAfter(!$this->debugMode ? self::CACHE_DETAIL_EXPIRATION : 10);
|
||||
|
||||
return $provider->getDetails($part_id);
|
||||
return $provider->getDetails($part_id, $options);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class BuerklinProvider implements BatchInfoProviderInterface, URLHandlerInfoProv
|
|||
];
|
||||
}
|
||||
|
||||
private function getProduct(string $code): array
|
||||
private function getProduct(string $code, bool $use_cache = true): array
|
||||
{
|
||||
$code = strtoupper(trim($code));
|
||||
if ($code === '') {
|
||||
|
|
@ -132,6 +132,11 @@ class BuerklinProvider implements BatchInfoProviderInterface, URLHandlerInfoProv
|
|||
md5($code . '|' . $this->settings->language . '|' . $this->settings->currency)
|
||||
);
|
||||
|
||||
if (!$use_cache) {
|
||||
$this->partInfoCache->deleteItem($cacheKey);
|
||||
unset($this->productCache[$cacheKey]);
|
||||
}
|
||||
|
||||
if (isset($this->productCache[$cacheKey])) {
|
||||
return $this->productCache[$cacheKey];
|
||||
}
|
||||
|
|
@ -488,7 +493,7 @@ class BuerklinProvider implements BatchInfoProviderInterface, URLHandlerInfoProv
|
|||
|
||||
// Fallback: try direct lookup by code
|
||||
try {
|
||||
$product = $this->getProduct($keyword);
|
||||
$product = $this->getProduct($keyword, use_cache: !($options[self::OPTION_NO_CACHE] ?? false));
|
||||
return [$this->getPartDetail($product)];
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
|
|
@ -498,7 +503,8 @@ class BuerklinProvider implements BatchInfoProviderInterface, URLHandlerInfoProv
|
|||
public function getDetails(string $id, array $options = []): PartDetailDTO
|
||||
{
|
||||
// Detail endpoint is /products/{code}/
|
||||
$response = $this->getProduct($id);
|
||||
//By default use cache for details, but allow bypassing cache with option (e.g. for refresh)
|
||||
$response = $this->getProduct($id, use_cache: !($options[self::OPTION_NO_CACHE] ?? false));
|
||||
|
||||
return $this->getPartDetail($response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,8 +184,10 @@ class CanopyProvider implements InfoProviderInterface
|
|||
throw new \InvalidArgumentException("The id must be a valid ASIN (10 characters, letters and numbers)");
|
||||
}
|
||||
|
||||
$do_not_cache = ($options[self::OPTION_NO_CACHE] ?? false) || $this->settings->alwaysGetDetails;
|
||||
|
||||
//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) {
|
||||
if(!$do_not_cache && ($cached = $this->getFromCache($id)) !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
|||
|
||||
interface InfoProviderInterface
|
||||
{
|
||||
public const OPTION_NO_CACHE = 'no_cache'; // if set to true, the provider should not use any cache and retrieve fresh data from the source
|
||||
|
||||
/**
|
||||
* Get information about this provider
|
||||
|
|
|
|||
|
|
@ -424,6 +424,11 @@ class OEMSecretsProvider implements InfoProviderInterface
|
|||
public function getDetails(string $id, array $options = []): PartDetailDTO
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($id);
|
||||
|
||||
if ($options[self::OPTION_NO_CACHE] ?? false) {
|
||||
$this->partInfoCache->deleteItem($cacheKey);
|
||||
}
|
||||
|
||||
$cacheItem = $this->partInfoCache->getItem($cacheKey);
|
||||
|
||||
if ($cacheItem->isHit()) {
|
||||
|
|
|
|||
|
|
@ -369,9 +369,11 @@ class OctopartProvider implements InfoProviderInterface
|
|||
|
||||
public function getDetails(string $id, array $options = []): PartDetailDTO
|
||||
{
|
||||
$no_cache = $options[self::OPTION_NO_CACHE] ?? false;
|
||||
|
||||
//Check if we have the part cached
|
||||
$cached = $this->getFromCache($id);
|
||||
if ($cached !== null) {
|
||||
if (!$no_cache && $cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue