Skip to content

Commit

Permalink
IBX-1341: Fixed inject valid CredentialsResolver (#88)
Browse files Browse the repository at this point in the history
* IBX-1341: Fixed inject valid CredentialsResolver

* Added testThrowExportCredentialsNotFoundException
  • Loading branch information
ciastektk authored Oct 29, 2021
1 parent 064d76c commit c7334eb
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/bundle/Resources/config/services/factory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
2 changes: 1 addition & 1 deletion src/lib/Config/CredentialsResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ interface CredentialsResolverInterface
*/
public function getCredentials(?string $siteAccess = null): ?Credentials;

public function hasCredentials(): bool;
public function hasCredentials(?string $siteAccess = null): bool;
}
48 changes: 27 additions & 21 deletions src/lib/Factory/ConfigurableExportParametersFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@

final class ConfigurableExportParametersFactory extends ExportParametersFactoryDecorator
{
private const REQUIRED_OPTIONS = [
'customerId',
'licenseKey',
'siteaccess',
];

/** @var \EzSystems\EzRecommendationClient\Config\CredentialsResolverInterface */
private $credentialsResolver;

Expand Down Expand Up @@ -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']);
Expand Down Expand Up @@ -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 [];
}
}
297 changes: 297 additions & 0 deletions tests/lib/Factory/Export/ConfigurableExportParametersFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\PersonalizationClient\Factory\Export;

use eZ\Publish\Core\MVC\ConfigResolverInterface;
use EzSystems\EzRecommendationClient\Config\CredentialsResolverInterface;
use EzSystems\EzRecommendationClient\Exception\ExportCredentialsNotFoundException;
use EzSystems\EzRecommendationClient\Exception\MissingExportParameterException;
use EzSystems\EzRecommendationClient\Factory\ConfigurableExportParametersFactory;
use EzSystems\EzRecommendationClient\Factory\ExportParametersFactoryInterface;
use EzSystems\EzRecommendationClient\Helper\SiteAccessHelper;
use EzSystems\EzRecommendationClient\Value\Config\EzRecommendationClientCredentials;
use EzSystems\EzRecommendationClient\Value\ExportParameters;
use PHPUnit\Framework\TestCase;

/**
* @covers \EzSystems\EzRecommendationClient\Factory\ConfigurableExportParametersFactory
*/
final class ConfigurableExportParametersFactoryTest extends TestCase
{
/** @var \EzSystems\EzRecommendationClient\Factory\ConfigurableExportParametersFactory */
private $parametersFactory;

/** @var \EzSystems\EzRecommendationClient\Factory\ExportParametersFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */
private $innerParametersFactory;

/** @var \EzSystems\EzRecommendationClient\Config\CredentialsResolverInterface|\PHPUnit\Framework\MockObject\MockObject */
private $credentialsResolver;

/** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */
private $configResolver;

/** @var \EzSystems\EzRecommendationClient\Helper\SiteAccessHelper|\PHPUnit\Framework\MockObject\MockObject */
private $siteAccessHelper;

/**
* @phpstan-var array{
* customerId: string,
* licenseKey: string,
* contentTypeIdList: string,
* languages: string,
* siteaccess: string,
* webHook: string,
* host: string,
* pageSize: string,
* }
*/
private $options;

public function setUp(): void
{
$this->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<string, string|int> $parameters
* @param array<string> $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{
* array<string, string|int>,
* array<string>
* }>
*/
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);
}
}

0 comments on commit c7334eb

Please sign in to comment.