From d2ff816b0c27e513f14527bbb188d5a758dd85f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 14 Sep 2026 19:26:26 +0200 Subject: [PATCH] feat(client): implement ArcadiaApiClient entry point, services, factories, logging and observability --- src/ArcadiaApiClient.php | 41 +++++ src/Factory/ApiClientFactory.php | 93 ++++++++++ src/Factory/ServiceFactory.php | 93 ++++++++++ src/Logger/ApiLoggerMiddleware.php | 185 +++++++++++++++++++ src/Observability/ObservabilityOption.php | 30 ++++ src/Service/ArcadiaBrandService.php | 51 ++++++ src/Service/ArcadiaEmployeeService.php | 175 ++++++++++++++++++ src/Service/ArcadiaIncoService.php | 210 ++++++++++++++++++++++ src/Service/BrandService.php | 32 ++++ src/Service/EmployeeService.php | 140 ++++++--------- src/Service/IncoService.php | 184 +++++++------------ 11 files changed, 1026 insertions(+), 208 deletions(-) create mode 100644 src/ArcadiaApiClient.php create mode 100644 src/Factory/ApiClientFactory.php create mode 100644 src/Factory/ServiceFactory.php create mode 100644 src/Logger/ApiLoggerMiddleware.php create mode 100644 src/Observability/ObservabilityOption.php create mode 100644 src/Service/ArcadiaBrandService.php create mode 100644 src/Service/ArcadiaEmployeeService.php create mode 100644 src/Service/ArcadiaIncoService.php create mode 100644 src/Service/BrandService.php diff --git a/src/ArcadiaApiClient.php b/src/ArcadiaApiClient.php new file mode 100644 index 0000000..370ef82 --- /dev/null +++ b/src/ArcadiaApiClient.php @@ -0,0 +1,41 @@ +apiConfig); + $apiClient = $apiClientFactory->createClient(); + + $this->serviceFactory = new ServiceFactory($apiClient); + } + + public function employees(): EmployeeService + { + return $this->serviceFactory->createEmployeeService(); + } + + public function incoSheets(): IncoService + { + return $this->serviceFactory->createIncoService(); + } + + public function brands(): BrandService + { + return $this->serviceFactory->createBrandService(); + } +} diff --git a/src/Factory/ApiClientFactory.php b/src/Factory/ApiClientFactory.php new file mode 100644 index 0000000..a0d0811 --- /dev/null +++ b/src/Factory/ApiClientFactory.php @@ -0,0 +1,93 @@ +setProductName($libProperties->productName); + $uaOptions->setProductVersion($libProperties->productVersion); + + $handlerStack = KiotaClientFactory::getDefaultHandlerStack(); + + if ($this->apiConfig->logger !== null) { + $handlerStack->push( + ApiLoggerMiddleware::log($this->apiConfig->logger), + ApiLoggerMiddleware::HANDLER_NAME + ); + } + + $handlerStack->push( + ApiMandatoryHeadersMiddleware::signRequest( + apiKey: $this->apiConfig->apiKey, + withSecret: $this->apiConfig->secret + ), + ApiMandatoryHeadersMiddleware::HANDLER_NAME + ); + + $handlerStack->push( + KiotaMiddleware::userAgent($uaOptions), + UserAgentHandler::HANDLER_NAME + ); + + $handlerStack->push( + KiotaMiddleware::retry( + new RetryOption( + maxRetries: $this->apiConfig->maxRetries, + delaySeconds: $this->apiConfig->retryDelaySeconds + ) + ), + RetryHandler::HANDLER_NAME + ); + + $guzzleClient = new Client([ + 'base_uri' => $this->apiConfig->endpoint, + 'timeout' => $this->apiConfig->curlTimeOut, + 'connect_timeout' => min(5.0, $this->apiConfig->curlTimeOut), + 'handler' => $handlerStack, + 'verify' => ! $this->apiConfig->ignoreSelfSignedCert, + ]); + + $observabilityOption = new ObservabilityOption( + $libProperties, + $this->apiConfig->tracer + ); + + $adapter = DefaultRequestAdapter::builder() + ->withGuzzleClient($guzzleClient) + ->withObservabilityOption($observabilityOption) + ->build(); + + $adapter->setBaseUrl($this->apiConfig->endpoint->__toString()); + + return new ApiClient($adapter); + } +} diff --git a/src/Factory/ServiceFactory.php b/src/Factory/ServiceFactory.php new file mode 100644 index 0000000..ceea7dd --- /dev/null +++ b/src/Factory/ServiceFactory.php @@ -0,0 +1,93 @@ +employeeService ??= new ArcadiaEmployeeService( + new EmployeeApiRepository( + $this->apiClient, + new EmployeeHydrator() + ) + ); + } + + public function createIncoService(): IncoService + { + return $this->incoService ??= new ArcadiaIncoService( + new IncoApiRepository( + $this->apiClient, + new IncoHydrator() + ), + new IncoShortApiRepository( + $this->apiClient, + new IncoShortHydrator() + ), + new IncoCompoApiRepository( + $this->apiClient, + new IncoCompoHydrator() + ) + ); + } + + public function createBrandService(): BrandService + { + return $this->brandService ??= new ArcadiaBrandService( + new BrandApiRepository( + $this->apiClient, + new BrandHydrator() + ) + ); + } + + public function employeeService(): EmployeeService + { + return $this->createEmployeeService(); + } + + public function incoService(): IncoService + { + return $this->createIncoService(); + } + + public function brandService(): BrandService + { + return $this->createBrandService(); + } +} diff --git a/src/Logger/ApiLoggerMiddleware.php b/src/Logger/ApiLoggerMiddleware.php new file mode 100644 index 0000000..8cf2146 --- /dev/null +++ b/src/Logger/ApiLoggerMiddleware.php @@ -0,0 +1,185 @@ +): PromiseInterface $nextHandler Next handler to invoke. + */ + public function __construct( + private $nextHandler, + private readonly LoggerInterface $logger, + ) { + } + + /** @param array $options */ + public function __invoke(RequestInterface $request, array $options): PromiseInterface + { + $fn = $this->nextHandler; + $startTime = hrtime(true); + + return $fn($request, $options)->then( + function (ResponseInterface $response) use ($request, $startTime): ResponseInterface { + $durationMs = (hrtime(true) - $startTime) / 1_000_000; + $this->logResponse($request, $response, $durationMs); + + return $response; + }, + function (\Throwable $reason) use ($request, $startTime): PromiseInterface { + $durationMs = (hrtime(true) - $startTime) / 1_000_000; + $this->logFailure($request, $reason, $durationMs); + + return Create::rejectionFor($reason); + } + ); + } + + private function logResponse(RequestInterface $request, ResponseInterface $response, float $durationMs): void + { + $statusCode = $response->getStatusCode(); + $roundedDuration = round($durationMs, 2); + + $context = [ + 'method' => $request->getMethod(), + 'uri' => (string) $request->getUri(), + 'status_code' => $statusCode, + 'duration_ms' => $roundedDuration, + 'headers' => $this->sanitizeHeaders($request->getHeaders()), + ]; + + $message = sprintf( + 'Arcadia API [%s] %s -> %d %s (%.2fms)', + $request->getMethod(), + (string) $request->getUri(), + $statusCode, + $response->getReasonPhrase(), + $roundedDuration + ); + + if ($statusCode >= 500) { + $this->logger->error($message, $context); + } elseif ($statusCode >= 400) { + $this->logger->warning($message, $context); + } else { + $this->logger->info($message, $context); + } + } + + private function logFailure(RequestInterface $request, \Throwable $reason, float $durationMs): void + { + $roundedDuration = round($durationMs, 2); + + $context = [ + 'method' => $request->getMethod(), + 'uri' => (string) $request->getUri(), + 'duration_ms' => $roundedDuration, + 'headers' => $this->sanitizeHeaders($request->getHeaders()), + 'error_class' => $reason::class, + 'error_message' => $reason->getMessage(), + ]; + + if ($reason instanceof RequestException && $reason->getResponse() !== null) { + $response = $reason->getResponse(); + $statusCode = $response->getStatusCode(); + $context['status_code'] = $statusCode; + + $message = sprintf( + 'Arcadia API [%s] %s -> %d %s (%.2fms)', + $request->getMethod(), + (string) $request->getUri(), + $statusCode, + $response->getReasonPhrase(), + $roundedDuration + ); + + if ($statusCode >= 500) { + $this->logger->error($message, $context); + } else { + $this->logger->warning($message, $context); + } + + return; + } + + $message = sprintf( + 'Arcadia API [%s] %s -> Failed: %s (%.2fms)', + $request->getMethod(), + (string) $request->getUri(), + $reason->getMessage(), + $roundedDuration + ); + + $this->logger->error($message, $context); + } + + /** + * @param array> $headers + * @return array + */ + private function sanitizeHeaders(array $headers): array + { + $sanitized = []; + + foreach ($headers as $name => $values) { + $stringName = (string) $name; + $value = implode(', ', $values); + + if ($this->isSensitiveHeader($stringName)) { + $sanitized[$stringName] = strlen($value) > 8 + ? substr($value, 0, 4) . '***[REDACTED]' + : '[REDACTED]'; + } else { + $sanitized[$stringName] = $value; + } + } + + return $sanitized; + } + + private function isSensitiveHeader(string $headerName): bool + { + foreach (self::SENSITIVE_HEADERS as $sensitive) { + if (strcasecmp($headerName, $sensitive) === 0) { + return true; + } + } + + return false; + } +} diff --git a/src/Observability/ObservabilityOption.php b/src/Observability/ObservabilityOption.php new file mode 100644 index 0000000..63d5450 --- /dev/null +++ b/src/Observability/ObservabilityOption.php @@ -0,0 +1,30 @@ +getTracer( + $libProperties->productName, + $libProperties->productVersion + ); + + self::setTracer($tracer); + } + + public static function getTracerInstrumentationName(): string + { + return LibProperties::PACKAGE_NAME; + } +} diff --git a/src/Service/ArcadiaBrandService.php b/src/Service/ArcadiaBrandService.php new file mode 100644 index 0000000..3f82e24 --- /dev/null +++ b/src/Service/ArcadiaBrandService.php @@ -0,0 +1,51 @@ +|ApiError + */ + public function getAll( + Pagination $pagination + ): PageOf | ApiError { + return $this->brandRepository->selectAll($pagination); + } + + /** + * @return StreamOf + */ + public function streamAll( + PageSize $pageSize = new PageSize() + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAll($pagination), + $pageSize, + ); + } + + public function findBy(BrandCode $code): Brand | null | ApiError + { + return $this->brandRepository->find($code); + } +} diff --git a/src/Service/ArcadiaEmployeeService.php b/src/Service/ArcadiaEmployeeService.php new file mode 100644 index 0000000..5de436d --- /dev/null +++ b/src/Service/ArcadiaEmployeeService.php @@ -0,0 +1,175 @@ + */ + public function streamAll( + PageSize $pageSize = new PageSize(), + ?EmployeeSorter $sorter = null, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAll($pagination, $sorter), + $pageSize, + ); + } + + /** @return StreamOf */ + public function streamAllSince( + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllSince($pagination, $since, $sortDirection), + $pageSize, + ); + } + + /** + * @param SiteId[]|SiteId $sites + * @return StreamOf + */ + public function streamAllOfSitesSince( + array | SiteId $sites, + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllOfSitesSince($pagination, $sites, $since, $sortDirection), + $pageSize, + ); + } + + /** + * @param SiteId[]|SiteId $sites + * @return StreamOf + */ + public function streamAllOfSites( + array | SiteId $sites, + PageSize $pageSize = new PageSize(), + ?EmployeeSorter $sorter = null, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllOfSites($pagination, $sites, $sorter), + $pageSize, + ); + } + + /** @return PageOf|ApiError */ + public function getAllSince( + Pagination $pagination, + \DateTimeInterface $since, + \SortDirection $sortDirection = \SortDirection::Descending, + ): PageOf | ApiError { + return $this->employeeRepository->selectAllSince( + $pagination, + $since, + $sortDirection + ); + } + + /** @return PageOf|ApiError */ + public function getAll( + Pagination $pagination, + ?EmployeeSorter $sorter = null, + ): PageOf | ApiError { + return $this->employeeRepository->selectAll( + $pagination, + $sorter, + ); + } + + /** + * @param SiteId[]|SiteId $sites + * @return PageOf|ApiError + */ + public function getAllOfSitesSince( + Pagination $pagination, + array | SiteId $sites, + \DateTimeInterface $since, + \SortDirection $sortDirection = \SortDirection::Descending, + ): PageOf | ApiError { + $sites = is_array($sites) ? $sites : [$sites]; + + if (count($sites) < 1) { + throw new \InvalidArgumentException('At least 1 site is required'); + } + + foreach ($sites as $site) { + if (! ($site instanceof SiteId)) { + throw new \InvalidArgumentException('Expected instance of ' . SiteId::class); + } + } + + return $this->employeeRepository->selectAllHavingSitesSince( + $pagination, + $since, + $sites, + $sortDirection, + ); + } + + /** + * @param SiteId[]|SiteId $sites + * @return PageOf|ApiError + */ + public function getAllOfSites( + Pagination $pagination, + array | SiteId $sites, + ?EmployeeSorter $sorter = null, + ): PageOf | ApiError { + $sites = is_array($sites) ? $sites : [$sites]; + + if (count($sites) < 1) { + throw new \InvalidArgumentException('At least 1 site is required'); + } + + foreach ($sites as $site) { + if (! ($site instanceof SiteId)) { + throw new \InvalidArgumentException('Expected instance of ' . SiteId::class); + } + } + + if ($sorter !== null) { + return $this->employeeRepository->selectAllHavingSites( + $pagination, + $sites, + $sorter + ); + } + + return $this->employeeRepository->selectAllHavingSites( + $pagination, + $sites, + ); + } + + public function findBy(ArcadiaEmployeeId $id): Employee | null | ApiError + { + return $this->employeeRepository->find($id); + } +} diff --git a/src/Service/ArcadiaIncoService.php b/src/Service/ArcadiaIncoService.php new file mode 100644 index 0000000..7ebdf2f --- /dev/null +++ b/src/Service/ArcadiaIncoService.php @@ -0,0 +1,210 @@ + */ + public function streamAll( + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAll($pagination, $sorter), + $pageSize, + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return StreamOf + */ + public function streamAllOfBrands( + array | BrandCode $brands, + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllOfBrands($pagination, $brands, $sorter), + $pageSize, + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return StreamOf + */ + public function streamAllOfBrandsSince( + array | BrandCode $brands, + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllOfBrandsSince($pagination, $brands, $since, $sortDirection), + $pageSize, + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return StreamOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort> + */ + public function streamAllShortOfBrands( + array | BrandCode $brands, + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf { + return new StreamOf( + fn (Pagination $pagination) => $this->getAllShortOfBrands($pagination, $brands, $sorter), + $pageSize, + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return PageOf|ApiError + */ + public function getAllOfBrandsSince( + Pagination $pagination, + array | BrandCode $brands, + \DateTimeInterface $since, + \SortDirection $sortDirection = \SortDirection::Descending + ): PageOf | ApiError { + return $this->incoRepository->selectAllHavingBrandsSince( + $pagination, + $since, + $this->validateBrands($brands), + $sortDirection + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return PageOf|ApiError + */ + public function getAllOfBrands( + Pagination $pagination, + array | BrandCode $brands, + ?IncoSorter $sorter = null + ): PageOf | ApiError { + return $this->incoRepository->selectAllHavingBrands( + $pagination, + $this->validateBrands($brands), + $sorter + ); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort>|ApiError + */ + public function getAllShortOfBrands( + Pagination $pagination, + array | BrandCode $brands, + ?IncoSorter $sorter = null + ): PageOf | ApiError { + return $this->incoShortRepository->selectAllHavingBrands( + $pagination, + $this->validateBrands($brands), + $sorter + ); + } + + /** @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco>|ApiError */ + public function getAll( + Pagination $pagination, + ?IncoSorter $sorter = null + ): PageOf | ApiError { + return $this->incoRepository->selectAll( + $pagination, + $sorter + ); + } + + /** + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco>|ApiError + */ + public function findByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf | ApiError + { + return $this->incoRepository->findWhere( + $incoIdLike, + $this->validateBrands($onBrands), + Pagination::firstPage(50) + ); + } + + /** + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort>|ApiError + */ + public function findShortByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf | ApiError + { + return $this->incoShortRepository->findWhere( + $incoIdLike, + $this->validateBrands($onBrands), + Pagination::firstPage(50) + ); + } + + public function findBy(IncoId $id): Inco | null | ApiError + { + return $this->incoRepository->find($id); + } + + public function findCompo(IncoId | IncoCompoId $id): IncoCompo | null | ApiError + { + if ($id instanceof IncoCompoId) { + return $this->incoCompoRepository->find($id); + } + + return $this->incoCompoRepository->findFor($id); + } + + /** + * @param BrandCode[]|BrandCode $brands + * @return BrandCode[] + */ + private function validateBrands(array | BrandCode $brands): array + { + $list = is_array($brands) ? $brands : [$brands]; + + if ($list === []) { + throw new \InvalidArgumentException('At least 1 brand code is required'); + } + + foreach ($list as $brand) { + if (! ($brand instanceof BrandCode)) { + throw new \InvalidArgumentException('Expected instance of ' . BrandCode::class); + } + } + + return $list; + } +} diff --git a/src/Service/BrandService.php b/src/Service/BrandService.php new file mode 100644 index 0000000..4f70339 --- /dev/null +++ b/src/Service/BrandService.php @@ -0,0 +1,32 @@ +|ApiError + */ + public function getAll( + Pagination $pagination + ): PageOf | ApiError; + + /** + * @return StreamOf + */ + public function streamAll( + PageSize $pageSize = new PageSize() + ): StreamOf; + + public function findBy(BrandCode $code): Brand | null | ApiError; +} diff --git a/src/Service/EmployeeService.php b/src/Service/EmployeeService.php index 408f0f2..8d94135 100644 --- a/src/Service/EmployeeService.php +++ b/src/Service/EmployeeService.php @@ -4,113 +4,85 @@ declare(strict_types=1); namespace Uwil\ArcadiaApiPhpClient\Service; +use Uwil\ArcadiaApiPhpClient\Error\ApiError; use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Employee; use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf; -use Uwil\ArcadiaApiPhpClient\Lib\ApiQueryBuilder\Direction; +use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSorter; use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ArcadiaEmployeeId; -use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\EmployeeSorter; use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\SiteId; +use Uwil\ArcadiaApiPhpClient\Lib\PageSize; use Uwil\ArcadiaApiPhpClient\Lib\Pagination; -use Uwil\ArcadiaApiPhpClient\Repository\EmployeeRepository; +use Uwil\ArcadiaApiPhpClient\Lib\Pagination\StreamOf; -final class EmployeeService +interface EmployeeService { - public function __construct( - private readonly EmployeeRepository $employeeRepository - ) { - } - - /** @return PageOf */ - public function getAllSince( - Pagination $pagination, - \DateTimeInterface $since, - Direction $sortDirection = Direction::DESC, - ): PageOf { - return $this->employeeRepository->selectAllSince( - $pagination, - $since, - $sortDirection - ); - } - - /** @return PageOf */ - public function getAll( - Pagination $pagination, + /** @return StreamOf */ + public function streamAll( + PageSize $pageSize = new PageSize(), ?EmployeeSorter $sorter = null, - ): PageOf { - return $this->employeeRepository->selectAll( - $pagination, - $sorter, - ); - } + ): StreamOf; + + /** @return StreamOf */ + public function streamAllSince( + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf; /** * @param SiteId[]|SiteId $sites - * @return PageOf + * @return StreamOf + */ + public function streamAllOfSitesSince( + array | SiteId $sites, + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf; + + /** + * @param SiteId[]|SiteId $sites + * @return StreamOf + */ + public function streamAllOfSites( + array | SiteId $sites, + PageSize $pageSize = new PageSize(), + ?EmployeeSorter $sorter = null, + ): StreamOf; + + /** @return PageOf|ApiError */ + public function getAllSince( + Pagination $pagination, + \DateTimeInterface $since, + \SortDirection $sortDirection = \SortDirection::Descending, + ): PageOf | ApiError; + + /** @return PageOf|ApiError */ + public function getAll( + Pagination $pagination, + ?EmployeeSorter $sorter = null, + ): PageOf | ApiError; + + /** + * @param SiteId[]|SiteId $sites + * @return PageOf|ApiError */ public function getAllOfSitesSince( Pagination $pagination, array | SiteId $sites, \DateTimeInterface $since, - Direction $sortDirection = Direction::DESC, - ): PageOf { - $sites = is_array($sites) ? $sites : [$sites]; - - if (count($sites) < 1) { - throw new \InvalidArgumentException('At least 1 site is required'); - } - - foreach ($sites as $site) { - if (! ($site instanceof SiteId)) { - throw new \InvalidArgumentException('Expected instance of ' . SiteId::class); - } - } - - return $this->employeeRepository->selectAllHavingSitesSince( - $pagination, - $since, - $sites, - $sortDirection, - ); - } + \SortDirection $sortDirection = \SortDirection::Descending, + ): PageOf | ApiError; /** * @param SiteId[]|SiteId $sites - * @return PageOf + * @return PageOf|ApiError */ public function getAllOfSites( Pagination $pagination, array | SiteId $sites, ?EmployeeSorter $sorter = null, - ): PageOf { - $sites = is_array($sites) ? $sites : [$sites]; + ): PageOf | ApiError; - if (count($sites) < 1) { - throw new \InvalidArgumentException('At least 1 site is required'); - } - - foreach ($sites as $site) { - if (! ($site instanceof SiteId)) { - throw new \InvalidArgumentException('Expected instance of ' . SiteId::class); - } - } - - if ($sorter !== null) { - return $this->employeeRepository->selectAllHavingSites( - $pagination, - $sites, - $sorter - ); - } - - return $this->employeeRepository->selectAllHavingSites( - $pagination, - $sites, - ); - } - - public function findBy(ArcadiaEmployeeId $id): ?Employee - { - return $this->employeeRepository->find($id); - } + public function findBy(ArcadiaEmployeeId $id): Employee | null | ApiError; } diff --git a/src/Service/IncoService.php b/src/Service/IncoService.php index 6a0eb67..01ba606 100644 --- a/src/Service/IncoService.php +++ b/src/Service/IncoService.php @@ -4,169 +4,105 @@ declare(strict_types=1); namespace Uwil\ArcadiaApiPhpClient\Service; +use Uwil\ArcadiaApiPhpClient\Error\ApiError; use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco; use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo; use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf; -use Uwil\ArcadiaApiPhpClient\Lib\ApiQueryBuilder\Direction; +use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\IncoSorter; use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode; use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId; use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId; -use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoSorter; +use Uwil\ArcadiaApiPhpClient\Lib\PageSize; use Uwil\ArcadiaApiPhpClient\Lib\Pagination; -use Uwil\ArcadiaApiPhpClient\Repository\IncoCompoRepository; -use Uwil\ArcadiaApiPhpClient\Repository\IncoRepository; -use Uwil\ArcadiaApiPhpClient\Repository\IncoShortRepository; +use Uwil\ArcadiaApiPhpClient\Lib\Pagination\StreamOf; -final class IncoService +interface IncoService { - public function __construct( - private readonly IncoRepository $incoRepository, - private readonly IncoShortRepository $incoShortRepository, - private readonly IncoCompoRepository $incoCompoRepository, - ) { - } + /** @return StreamOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco> */ + public function streamAll( + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf; /** * @param BrandCode[]|BrandCode $brands - * @return PageOf + * @return StreamOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco> + */ + public function streamAllOfBrands( + array | BrandCode $brands, + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf; + + /** + * @param BrandCode[]|BrandCode $brands + * @return StreamOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco> + */ + public function streamAllOfBrandsSince( + array | BrandCode $brands, + \DateTimeInterface $since, + PageSize $pageSize = new PageSize(), + \SortDirection $sortDirection = \SortDirection::Descending, + ): StreamOf; + + /** + * @param BrandCode[]|BrandCode $brands + * @return StreamOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort> + */ + public function streamAllShortOfBrands( + array | BrandCode $brands, + PageSize $pageSize = new PageSize(), + ?IncoSorter $sorter = null, + ): StreamOf; + + /** + * @param BrandCode[]|BrandCode $brands + * @return PageOf|ApiError */ public function getAllOfBrandsSince( Pagination $pagination, array | BrandCode $brands, \DateTimeInterface $since, - Direction $sortDirection = Direction::DESC - ): PageOf { - $brands = is_array($brands) ? $brands : [$brands]; - - if (count($brands) < 1) { - throw new \InvalidArgumentException('At least 1 brand code is required'); - } - - foreach ($brands as $brand) { - if (! ($brand instanceof BrandCode)) { - throw new \InvalidArgumentException('Expected instance of ' . BrandCode::class); - } - } - - return $this->incoRepository->selectAllHavingBrandsSince( - $pagination, - $since, - $brands, - $sortDirection - ); - } + \SortDirection $sortDirection = \SortDirection::Descending + ): PageOf | ApiError; /** * @param BrandCode[]|BrandCode $brands - * @return PageOf + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco>|ApiError */ public function getAllOfBrands( Pagination $pagination, array | BrandCode $brands, ?IncoSorter $sorter = null - ): PageOf { - $brands = is_array($brands) ? $brands : [$brands]; - - if (count($brands) < 1) { - throw new \InvalidArgumentException('At least 1 brand code is required'); - } - - foreach ($brands as $brand) { - if (! ($brand instanceof BrandCode)) { - throw new \InvalidArgumentException('Expected instance of ' . BrandCode::class); - } - } - - return $this->incoRepository->selectAllHavingBrands( - $pagination, - $brands, - $sorter - ); - } + ): PageOf | ApiError; /** * @param BrandCode[]|BrandCode $brands - * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort> + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort>|ApiError */ public function getAllShortOfBrands( Pagination $pagination, array | BrandCode $brands, ?IncoSorter $sorter = null - ): PageOf { - $brands = is_array($brands) ? $brands : [$brands]; + ): PageOf | ApiError; - if (count($brands) < 1) { - throw new \InvalidArgumentException('At least 1 brand code is required'); - } - - foreach ($brands as $brand) { - if (! ($brand instanceof BrandCode)) { - throw new \InvalidArgumentException('Expected instance of ' . BrandCode::class); - } - } - - return $this->incoShortRepository->selectAllHavingBrands( - $pagination, - $brands, - $sorter - ); - } - - /** @return PageOf */ + /** @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco>|ApiError */ public function getAll( Pagination $pagination, ?IncoSorter $sorter = null - ): PageOf { + ): PageOf | ApiError; - return $this->incoRepository->selectAll( - $pagination, - $sorter - ); - } + /** + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco>|ApiError + */ + public function findByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf | ApiError; - /** @return PageOf */ - public function findByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf - { - $pagination = Pagination::firstPage(50); + /** + * @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort>|ApiError + */ + public function findShortByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf | ApiError; - if (count($onBrands) < 1) { - throw new \InvalidArgumentException('At least 1 brand code is required'); - } + public function findBy(IncoId $id): Inco | null | ApiError; - return $this->incoRepository->findWhere( - $incoIdLike, - $onBrands, - $pagination - ); - } - - /** @return PageOf<\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoShort> */ - public function findShortByIncoIdLike(string $incoIdLike, BrandCode ...$onBrands): PageOf - { - $pagination = Pagination::firstPage(50); - - if (count($onBrands) < 1) { - throw new \InvalidArgumentException('At least 1 brand code is required'); - } - - return $this->incoShortRepository->findWhere( - $incoIdLike, - $onBrands, - $pagination - ); - } - - public function findBy(IncoId $id): ?Inco - { - return $this->incoRepository->find($id); - } - - public function findCompo(IncoId | IncoCompoId $id): ?IncoCompo - { - if ($id instanceof IncoCompoId) { - return $this->incoCompoRepository->find($id); - } - - return $this->incoCompoRepository->findFor($id); - } + public function findCompo(IncoId | IncoCompoId $id): IncoCompo | null | ApiError; }