diff --git a/src/bundle/Resources/config/services/factory.yaml b/src/bundle/Resources/config/services/factory.yaml index 87651350..33527c77 100644 --- a/src/bundle/Resources/config/services/factory.yaml +++ b/src/bundle/Resources/config/services/factory.yaml @@ -12,4 +12,4 @@ services: EzSystems\EzRecommendationClient\Factory\ConfigurableExportParametersFactory: arguments: $innerService: '@EzSystems\EzRecommendationClient\Factory\ExportParametersFactory' - $credentialsResolver: '@EzSystems\EzRecommendationClient\Config\ExportCredentialsResolver' + $credentialsResolver: '@EzSystems\EzRecommendationClient\Config\EzRecommendationClientCredentialsResolver' diff --git a/src/lib/Config/CredentialsResolverInterface.php b/src/lib/Config/CredentialsResolverInterface.php index e102f662..6d869086 100644 --- a/src/lib/Config/CredentialsResolverInterface.php +++ b/src/lib/Config/CredentialsResolverInterface.php @@ -17,5 +17,5 @@ interface CredentialsResolverInterface */ public function getCredentials(?string $siteAccess = null): ?Credentials; - public function hasCredentials(): bool; + public function hasCredentials(?string $siteAccess = null): bool; } diff --git a/src/lib/Factory/ConfigurableExportParametersFactory.php b/src/lib/Factory/ConfigurableExportParametersFactory.php index f1444417..40fb50a0 100644 --- a/src/lib/Factory/ConfigurableExportParametersFactory.php +++ b/src/lib/Factory/ConfigurableExportParametersFactory.php @@ -20,6 +20,12 @@ final class ConfigurableExportParametersFactory extends ExportParametersFactoryDecorator { + private const REQUIRED_OPTIONS = [ + 'customerId', + 'licenseKey', + 'siteaccess', + ]; + /** @var \EzSystems\EzRecommendationClient\Config\CredentialsResolverInterface */ private $credentialsResolver; @@ -51,28 +57,29 @@ public function __construct( */ public function create(array $properties = []): ExportParameters { - if (!empty($this->getMissingRequiredOptions($properties))) { + $missingRequiredOptions = $this->getMissingRequiredOptions($properties); + if (!empty($missingRequiredOptions)) { throw new MissingExportParameterException(sprintf( 'Required parameters %s are missing', - implode(', ', $this->getMissingRequiredOptions($properties)) + implode(', ', $missingRequiredOptions) )); } $properties['siteaccess'] = $properties['siteaccess'] ?? $this->getSiteAccess(); if (!isset($properties['customerId']) && !isset($properties['licenseKey'])) { - /** @var \EzSystems\EzRecommendationClient\Value\Config\ExportCredentials $credentials */ + /** @var \EzSystems\EzRecommendationClient\Value\Config\EzRecommendationClientCredentials $credentials */ $credentials = $this->credentialsResolver->getCredentials($properties['siteaccess']); - if (!$this->credentialsResolver->hasCredentials()) { + if (!$this->credentialsResolver->hasCredentials($properties['siteaccess'])) { throw new ExportCredentialsNotFoundException(sprintf( 'Recommendation client export credentials are not set for siteAccess: %s', $properties['siteaccess'] )); } - $properties['customerId'] = $credentials->getLogin(); - $properties['licenseKey'] = $credentials->getPassword(); + $properties['customerId'] = $credentials->getCustomerId(); + $properties['licenseKey'] = $credentials->getLicenseKey(); } $properties['host'] = $properties['host'] ?? $this->getHostUri($properties['siteaccess']); @@ -107,24 +114,23 @@ private function getWebHook(int $customerId, string $siteAccess): string ) . sprintf(Notifier::ENDPOINT_PATH, $customerId); } + /** + * Returns missing required options + * If one of required options has been passed to command then user must pass all options + * If all required options are missing then options will be automatically taken from configuration. + */ private function getMissingRequiredOptions(array $options): array { - $missingOptions = []; - - if (isset($options['customerId']) || isset($options['licenseKey'])) { - if (!isset($options['customerId'])) { - $missingOptions[] = 'customerId'; - } - - if (!isset($options['licenseKey'])) { - $missingOptions[] = 'licenseKey'; - } - - if (!isset($options['siteaccess'])) { - $missingOptions[] = 'siteaccess'; - } + $missingRequiredOptions = array_diff(self::REQUIRED_OPTIONS, array_keys( + array_filter($options, static function (?string $option = null): bool { + return null !== $option; + }) + )); + + if (!empty(array_diff(self::REQUIRED_OPTIONS, $missingRequiredOptions))) { + return array_intersect(self::REQUIRED_OPTIONS, $missingRequiredOptions); } - return $missingOptions; + return []; } } diff --git a/tests/lib/Factory/Export/ConfigurableExportParametersFactoryTest.php b/tests/lib/Factory/Export/ConfigurableExportParametersFactoryTest.php new file mode 100644 index 00000000..408f7ec5 --- /dev/null +++ b/tests/lib/Factory/Export/ConfigurableExportParametersFactoryTest.php @@ -0,0 +1,297 @@ +innerParametersFactory = $this->createMock(ExportParametersFactoryInterface::class); + $this->credentialsResolver = $this->createMock(CredentialsResolverInterface::class); + $this->configResolver = $this->createMock(ConfigResolverInterface::class); + $this->siteAccessHelper = $this->createMock(SiteAccessHelper::class); + $this->parametersFactory = new ConfigurableExportParametersFactory( + $this->innerParametersFactory, + $this->credentialsResolver, + $this->configResolver, + $this->siteAccessHelper, + ); + $this->options = [ + 'customerId' => '12345', + 'licenseKey' => '12345-12345-12345-12345', + 'siteaccess' => 'test', + 'contentTypeIdList' => 'article, product, blog', + 'languages' => 'eng-GB', + 'webHook' => 'https://reco-engine.com/api/12345/items', + 'host' => 'https://127.0.0.1', + 'pageSize' => '500', + ]; + } + + public function testCreateFromAllOptions(): void + { + $exportParameters = new ExportParameters($this->options); + + $this->configureInnerParameterFactoryServiceToReturnExportCredentials($exportParameters); + + self::assertEquals( + $exportParameters, + $this->parametersFactory->create($this->options) + ); + } + + public function testCreateWithAutocomplete(): void + { + $exportParameters = new ExportParameters($this->options); + $siteAccess = 'test'; + $options = [ + 'customerId' => null, + 'licenseKey' => null, + 'siteaccess' => null, + 'contentTypeIdList' => 'article, product, blog', + 'languages' => 'eng-GB', + 'pageSize' => '500', + 'webHook' => null, + 'host' => null, + ]; + + $this->configureSiteAccessHelperToReturnSiteAccessName($siteAccess); + $this->configureCredentialsResolverToReturnIfCredentialsAreConfiguredForSiteAccess($siteAccess, true); + $this->configureCredentialsResolverToReturnRecommendationClientCredentials( + $siteAccess, + 12345, + '12345-12345-12345-12345' + ); + $this->configureConfigResolverToReturnHostUriAndApiNotifierUri($siteAccess); + $this->configureInnerParameterFactoryServiceToReturnExportCredentials($exportParameters); + + self::assertEquals( + $exportParameters, + $this->parametersFactory->create($options) + ); + } + + /** + * @param array $parameters + * @param array $missingParameters + * + * @dataProvider provideDataForTestThrowMissingExportParameterException + */ + public function testThrowMissingExportParameterException( + array $parameters, + array $missingParameters + ): void { + $this->expectException(MissingExportParameterException::class); + $this->expectExceptionMessage( + sprintf('Required parameters %s are missing', implode(', ', $missingParameters)) + ); + + $this->parametersFactory->create($parameters); + } + + public function testThrowExportCredentialsNotFoundException(): void + { + $siteAccess = 'foo'; + + $this->configureSiteAccessHelperToReturnSiteAccessName($siteAccess); + $this->configureCredentialsResolverToReturnIfCredentialsAreConfiguredForSiteAccess( + $siteAccess, + false + ); + + $this->expectException(ExportCredentialsNotFoundException::class); + $this->expectExceptionMessage('Recommendation client export credentials are not set for siteAccess: foo'); + + $this->parametersFactory->create( + [ + 'customerId' => null, + 'licenseKey' => null, + 'siteaccess' => null, + ] + ); + } + + /** + * @phpstan-return iterable, + * array + * }> + */ + public function provideDataForTestThrowMissingExportParameterException(): iterable + { + yield [ + [ + 'siteaccess' => 'site', + ], + [ + 'customerId', + 'licenseKey', + ], + ]; + + yield [ + [ + 'customerId' => 12345, + ], + [ + 'licenseKey', + 'siteaccess', + ], + ]; + + yield [ + [ + 'licenseKey' => '12345-12345-12345-12345', + ], + [ + 'customerId', + 'siteaccess', + ], + ]; + + yield [ + [ + 'siteaccess' => 'site', + 'customerId' => 12345, + ], + [ + 'licenseKey', + ], + ]; + + yield [ + [ + 'siteaccess' => 'site', + 'licenseKey' => '12345-12345-12345-12345', + ], + [ + 'customerId', + ], + ]; + + yield [ + [ + 'customerId' => 12345, + 'licenseKey' => '12345-12345-12345-12345', + ], + [ + 'siteaccess', + ], + ]; + } + + private function configureCredentialsResolverToReturnIfCredentialsAreConfiguredForSiteAccess( + string $siteAccess, + bool $hasCredentials + ): void { + $this->credentialsResolver + ->expects(self::atLeastOnce()) + ->method('hasCredentials') + ->with($siteAccess) + ->willReturn($hasCredentials); + } + + private function configureCredentialsResolverToReturnRecommendationClientCredentials( + string $siteAccess, + int $customerId, + string $licenseKey + ): void { + $this->credentialsResolver + ->expects(self::once()) + ->method('getCredentials') + ->with($siteAccess) + ->willReturn( + new EzRecommendationClientCredentials( + [ + 'customerId' => $customerId, + 'licenseKey' => $licenseKey, + ] + ) + ); + } + + private function configureConfigResolverToReturnHostUriAndApiNotifierUri(string $siteAccess): void + { + $this->configResolver + ->expects(self::atLeastOnce()) + ->method('getParameter') + ->willReturnMap( + [ + ['host_uri', 'ezrecommendation', $siteAccess, 'https://127.0.0.1'], + ['api.notifier.endpoint', 'ezrecommendation', $siteAccess, 'https://reco-engine.com'], + ] + ); + } + + private function configureSiteAccessHelperToReturnSiteAccessName(string $siteAccess): void + { + $helper = $this->siteAccessHelper; + + /** @var \PHPUnit\Framework\MockObject\MockObject $helper */ + $helper + ->expects(self::atLeastOnce()) + ->method('getSiteAccesses') + ->willReturn( + [$siteAccess] + ); + } + + private function configureInnerParameterFactoryServiceToReturnExportCredentials( + ExportParameters $exportParameters + ): void { + $this->innerParametersFactory + ->expects(self::once()) + ->method('create') + ->with($this->options) + ->willReturn($exportParameters); + } +}