diff --git a/ChangeLog b/ChangeLog index b9da96a..ad28c62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-10-31 Francois Suter (Idéative) + + * Introduce connection information, resolves #33 + 2024-07-20 Francois Suter (Idéative) * Introduce call context , resolves #30 diff --git a/Classes/Controller/TestingController.php b/Classes/Controller/TestingController.php index 46192de..f6c951e 100644 --- a/Classes/Controller/TestingController.php +++ b/Classes/Controller/TestingController.php @@ -126,7 +126,7 @@ public function defaultAction(): ResponseInterface 'format' => $arguments['format'], 'testResult' => $this->performTest( $arguments['service'], - $arguments['parameters'], + $parameters, (int)$arguments['format'] ), ] @@ -179,11 +179,12 @@ protected function performTest(string $type, string $parameters = '', int $forma try { $service->getCallContext()->add('svconnector', ['function' => 'testing module']); $parsedParameters = json_decode($parameters, true, 512, JSON_THROW_ON_ERROR); + $service->setParameters($parsedParameters); // Call the right "fetcher" depending on chosen format $result = match ($format) { - 1 => $service->fetchArray($parsedParameters), - 2 => $service->fetchXML($parsedParameters), - default => $service->fetchRaw($parsedParameters), + 1 => $service->fetchArray(), + 2 => $service->fetchXML(), + default => $service->fetchRaw(), }; // If the result is empty, issue an information message if (empty($result)) { diff --git a/Classes/Domain/Model/Dto/AbstractDataObject.php b/Classes/Domain/Model/Dto/AbstractDataObject.php new file mode 100644 index 0000000..9a68834 --- /dev/null +++ b/Classes/Domain/Model/Dto/AbstractDataObject.php @@ -0,0 +1,62 @@ +data; + } + + /** + * @throws NoSuchDataException + */ + public function getForKey(string $key): ?array + { + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + throw new NoSuchDataException( + sprintf('No value found in context for key "%s".', $key), + 1721494427 + ); + } + + public function set(array $data): void + { + $this->data = $data; + } + + public function add(string $key, mixed $value): void + { + $this->data[$key] = $value; + } + + public function reset(): void + { + $this->data = []; + } +} diff --git a/Classes/Domain/Model/Dto/CallContext.php b/Classes/Domain/Model/Dto/CallContext.php index 36826d9..6843ad8 100644 --- a/Classes/Domain/Model/Dto/CallContext.php +++ b/Classes/Domain/Model/Dto/CallContext.php @@ -17,49 +17,10 @@ namespace Cobweb\Svconnector\Domain\Model\Dto; -use Cobweb\Svconnector\Exception\NoSuchContextException; - /** * DTO class for managing a call context. * * A call context can be made up of a collection of information, each piece of information being stored * with a specific associative key in the context array. */ -class CallContext -{ - protected array $context = []; - - public function get(): array - { - return $this->context; - } - - /** - * @throws NoSuchContextException - */ - public function getForKey(string $key): ?array - { - if (array_key_exists($key, $this->context)) { - return $this->context[$key]; - } - throw new NoSuchContextException( - sprintf('No value found in context for key "%s".', $key), - 1721494427 - ); - } - - public function set(array $context): void - { - $this->context = $context; - } - - public function add(string $key, array $value): void - { - $this->context[$key] = $value; - } - - public function reset(): void - { - $this->context = []; - } -} +class CallContext extends AbstractDataObject {} diff --git a/Classes/Domain/Model/Dto/ConnectionInformation.php b/Classes/Domain/Model/Dto/ConnectionInformation.php new file mode 100644 index 0000000..4f9fba6 --- /dev/null +++ b/Classes/Domain/Model/Dto/ConnectionInformation.php @@ -0,0 +1,27 @@ +connector = $connector; + } + + public function getConnector(): ConnectorBase + { + return $this->connector; + } +} diff --git a/Classes/Event/ProcessParametersEvent.php b/Classes/Event/ProcessParametersEvent.php new file mode 100644 index 0000000..c830dc2 --- /dev/null +++ b/Classes/Event/ProcessParametersEvent.php @@ -0,0 +1,39 @@ +parameters = $parameters; + } + + public function getParameters(): array + { + return $this->parameters; + } + + public function setParameters(array $parameters): void + { + $this->parameters = $parameters; + } + +} diff --git a/Classes/Exception/NoSuchContextException.php b/Classes/Exception/NoSuchDataException.php similarity index 85% rename from Classes/Exception/NoSuchContextException.php rename to Classes/Exception/NoSuchDataException.php index 9699192..7a03315 100644 --- a/Classes/Exception/NoSuchContextException.php +++ b/Classes/Exception/NoSuchDataException.php @@ -18,6 +18,6 @@ */ /** - * Exception to throw when trying to access an element which does not exist in the current call context + * Exception to throw when trying to access an element which does not exist in a data object */ -class NoSuchContextException extends ConnectorException {} +class NoSuchDataException extends ConnectorException {} diff --git a/Classes/Registry/ConnectorRegistry.php b/Classes/Registry/ConnectorRegistry.php index a912acd..7ad4767 100644 --- a/Classes/Registry/ConnectorRegistry.php +++ b/Classes/Registry/ConnectorRegistry.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Cobweb\Svconnector\Registry; - /* * This file is part of the TYPO3 CMS project. * @@ -17,6 +15,8 @@ * The TYPO3 project - inspiring people to share! */ +namespace Cobweb\Svconnector\Registry; + use Cobweb\Svconnector\Exception\UnknownServiceException; use Cobweb\Svconnector\Service\ConnectorBase; @@ -52,6 +52,7 @@ public function __construct(iterable $connectors) 1671361286 ); } + $connector->initialize(); $this->connectors[$type] = $connector; } } @@ -70,13 +71,17 @@ public function getAllServices(): iterable * Returns a connector service object for the requested type * * @param string $type Type of connector service + * @param array $parameters Parameters for the connector * @return ConnectorBase * @throws UnknownServiceException */ - public function getServiceForType(string $type): ConnectorBase + public function getServiceForType(string $type, array $parameters = []): ConnectorBase { if (isset($this->connectors[$type])) { - return $this->connectors[$type]; + /** @var ConnectorBase $connector */ + $connector = $this->connectors[$type]; + $connector->setParameters($parameters); + return $connector; } throw new UnknownServiceException( sprintf( diff --git a/Classes/Service/ConnectorBase.php b/Classes/Service/ConnectorBase.php index 6a98f14..746f0df 100644 --- a/Classes/Service/ConnectorBase.php +++ b/Classes/Service/ConnectorBase.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Cobweb\Svconnector\Service; - /* * This file is part of the TYPO3 CMS project. * @@ -17,13 +15,20 @@ * The TYPO3 project - inspiring people to share! */ +namespace Cobweb\Svconnector\Service; + use Cobweb\Svconnector\Domain\Model\Dto\CallContext; +use Cobweb\Svconnector\Domain\Model\Dto\ConnectionInformation; +use Cobweb\Svconnector\Event\InitializeConnectorEvent; +use Cobweb\Svconnector\Event\ProcessParametersEvent; use Cobweb\Svconnector\Exception\ConnectorRuntimeException; +use Cobweb\Svconnector\Utility\ParameterParser; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Charset\CharsetConverter; +use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; use TYPO3\CMS\Core\Http\ApplicationType; use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Localization\LanguageServiceFactory; @@ -46,12 +51,19 @@ abstract class ConnectorBase implements LoggerAwareInterface, ConnectorServiceIn use LoggerAwareTrait; protected string $extensionKey = 'svconnector'; + // Information set from outside the connector service, to give context about where it is being called from protected CallContext $callContext; - + // Information about the connection that the service tries to establish + protected ConnectionInformation $connectionInformation; + protected EventDispatcher $eventDispatcher; protected LanguageService $languageService; + protected array $parameters = []; public function __construct() { + $this->eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class); + $this->callContext = new CallContext(); + $this->connectionInformation = new ConnectionInformation(); $this->initializeLanguageService(); } @@ -70,15 +82,48 @@ abstract public function getType(): string; abstract public function getName(): string; /** - * Returns the current call context object - * - * @return CallContext + * Perform any necessary initialization of the service and fire an event to allow + * for custom handling in specific cases + */ + public function initialize(): void + { + $this->eventDispatcher->dispatch( + new InitializeConnectorEvent($this) + ); + } + + /** + * Set the connector parameters, process them and fire an event for custom manipulation + */ + public function setParameters(array $parameters): void + { + $parameterParser = GeneralUtility::makeInstance(ParameterParser::class); + $parameters = $parameterParser->parse( + $parameters, + $this->connectionInformation->get() + ); + $event = $this->eventDispatcher->dispatch( + new ProcessParametersEvent($parameters) + ); + $this->parameters = $event->getParameters(); + } + + /** + * Return the current call context object */ public function getCallContext(): CallContext { return $this->callContext; } + /** + * Return the current connection information + */ + public function getConnectionInformation(): ConnectionInformation + { + return $this->connectionInformation; + } + /** * Verifies that the connection is functional * Returns false if not. This base implementation always returns false, @@ -115,6 +160,14 @@ public function getSampleConfiguration(): string */ public function checkConfiguration(array $parameters = []): array { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + // Base deprecation code that can be called by all inheriting classes + if (count(func_get_args()) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + return [ ContextualFeedbackSeverity::NOTICE->value => [], ContextualFeedbackSeverity::WARNING->value => [], @@ -156,7 +209,17 @@ public function logConfigurationCheck(array $problems): void * @param array $parameters Parameters for the call * @return mixed Server response */ - abstract public function fetchRaw(array $parameters = []); + public function fetchRaw(array $parameters = []) + { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + // Base deprecation code that can be called by all inheriting classes + if (count(func_get_args()) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + return ''; + } /** * This method calls the query and returns the results from the response as an XML structure. @@ -171,7 +234,17 @@ abstract public function fetchRaw(array $parameters = []); * @param array $parameters Parameters for the call * @return string XML structure */ - abstract public function fetchXML(array $parameters = []): string; + public function fetchXML(array $parameters = []): string + { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + // Base deprecation code that can be called by all inheriting classes + if (count(func_get_args()) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + return ''; + } /** * This method calls the query and returns the results from the response as a PHP array. @@ -186,7 +259,17 @@ abstract public function fetchXML(array $parameters = []): string; * @param array $parameters Parameters for the call * @return array PHP array */ - abstract public function fetchArray(array $parameters = []): array; + public function fetchArray(array $parameters = []): array + { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + // Base deprecation code that can be called by all inheriting classes + if (count(func_get_args()) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + return []; + } /** * This method can be called to perform specific operations at some point after @@ -197,13 +280,20 @@ abstract public function fetchArray(array $parameters = []): array; * @param mixed $status Some form of status can be passed as argument * The nature of that status will depend on which process is calling this method */ - public function postProcessOperations(array $parameters, $status): void + public function postProcessOperations(array $parameters, mixed $status): void { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + if (count($parameters) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + $hooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->extensionKey]['postProcessOperations'] ?? null; if (is_array($hooks)) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->extensionKey]['postProcessOperations'] as $className) { $processor = GeneralUtility::makeInstance($className); - $processor->postProcessOperations($parameters, $status, $this); + $processor->postProcessOperations($this->parameters, $status, $this); } } } @@ -224,7 +314,17 @@ public function postProcessOperations(array $parameters, $status): void * * @return mixed Server response */ - abstract protected function query(array $parameters = []); + protected function query(array $parameters = []) + { + // Temporary code while passing parameters array is deprecated and before + // method signature is changed entirely (in the next major version) + // Base deprecation code that can be called by all inheriting classes + if (count(func_get_args()) > 0) { + $this->triggerDeprecation('fetchRaw()'); + $this->parameters = $parameters; + } + return ''; + } /** * This method should be used by all connector services when they encounter a fatal error. @@ -336,4 +436,21 @@ protected function getBackendUser(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } + + /** + * @internal + * @deprecated Will be removed in next major version + */ + public function triggerDeprecation(string $method): void + { + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $caller = end($backtrace); + $callerLocation = sprintf('file %s, line %d', $caller['file'], $caller['line']); + + trigger_error(sprintf( + 'Passing parameters as argument to %s is deprecated. Pass arguments when getting service from registry instead. Location: %s', + $method, + $callerLocation, + ), E_USER_DEPRECATED); + } } diff --git a/Classes/Service/ConnectorServiceInterface.php b/Classes/Service/ConnectorServiceInterface.php index fbe6700..b026d9b 100644 --- a/Classes/Service/ConnectorServiceInterface.php +++ b/Classes/Service/ConnectorServiceInterface.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Cobweb\Svconnector\Service; - /* * This file is part of the TYPO3 CMS project. * @@ -17,7 +15,10 @@ * The TYPO3 project - inspiring people to share! */ +namespace Cobweb\Svconnector\Service; + use Cobweb\Svconnector\Domain\Model\Dto\CallContext; +use Cobweb\Svconnector\Domain\Model\Dto\ConnectionInformation; /** * Defines the methods that represent the Connector Service API @@ -38,6 +39,11 @@ public function getType(): string; */ public function getName(): string; + /** + * Performs any necessary initialization for the service. Called automatically by the connector registry. + */ + public function initialize(): void; + /** * Verifies that the connection is functional, returns false if not. * @@ -45,6 +51,11 @@ public function getName(): string; */ public function isAvailable(): bool; + /** + * Set the connector parameters + */ + public function setParameters(array $parameters): void; + /** * Returns the sample configuration for the service, if any * @@ -61,12 +72,19 @@ public function getSampleConfiguration(): string; public function checkConfiguration(array $parameters = []): array; /** - * Returns the call context object + * Return the call context object * * @return CallContext */ public function getCallContext(): CallContext; + /** + * Return the connection information object + * + * @return ConnectionInformation + */ + public function getConnectionInformation(): ConnectionInformation; + /** * Calls the query and returns the results from the response as is. * diff --git a/Classes/Utility/ParameterParser.php b/Classes/Utility/ParameterParser.php new file mode 100644 index 0000000..1ec3c72 --- /dev/null +++ b/Classes/Utility/ParameterParser.php @@ -0,0 +1,87 @@ + $parameter) { + if (is_array($parameter)) { + $parsedParameters[$key] = $this->parse($parameter, $data); + } else { + $parsedParameters[$key] = $this->substitute($parameter, $data); + } + } + return $parsedParameters; + } + + /** + * Search for "{...}" patterns inside a string and replace with data found in an array + * + * The values in the array may in a deeper dimension. An array path is used, with "." + * as a separator. + */ + public function substitute(mixed $parameter, array $data): mixed + { + // If parameter is not a string, do not try to substitute anything + if (!is_string($parameter)) { + return $parameter; + } + + // Early return if the string is empty + if (empty($parameter)) { + return ''; + } + + $variables = preg_match_all('/{(.*?)}/', $parameter, $matches); + // If the matching failed or there were no matches, return string as + if ($variables === false || count($matches[1]) === 0) { + return $parameter; + } + + $searches = []; + $replacements = []; + // For each match, try to find a replacement value in the data array + foreach ($matches[1] as $match) { + try { + $replacements[] = ArrayUtility::getValueByPath($data, $match, '.'); + $searches[] = '{' . $match . '}'; + } catch (\Throwable $exception) { + // Do nothing, variable was not matched and will not be substituted + } + } + return str_replace($searches, $replacements, $parameter); + } +} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index e452739..bda3a83 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -11,7 +11,7 @@ services: Cobweb\Svconnector\Utility\FileUtility: public: true - # Linktype registry + # Connector services registry Cobweb\Svconnector\Registry\ConnectorRegistry: public: true arguments: diff --git a/Documentation/Developers/ConnectorApi/Index.rst b/Documentation/Developers/ConnectorApi/Index.rst index 05e9ef3..ba1fe3d 100644 --- a/Documentation/Developers/ConnectorApi/Index.rst +++ b/Documentation/Developers/ConnectorApi/Index.rst @@ -22,7 +22,7 @@ than reimplementing boilerplate code. getType() This method returns the type of the connector service. The type is - representative of this kind of data it can handle, e.g. :code:`csv` + representative of the kind of data it can handle, e.g. :code:`csv` for the "svconnector_csv" service. Input @@ -42,20 +42,26 @@ getName() string -getName() - This method returns the current :ref:`call context object `. +initialize() + This method performs any necessary initialization for the connector service. It is + called automatically by the registry when instanciating the service. The base class + :php:`\Cobweb\Svconnector\Service\ConnectorBase` contains a default implementation which + fires an event allowing custom initialization processes. If you should need to implement + your own version of the `initialize()` method in your connector service, make sure to call + the parent method or to dispatch the :php`\Cobweb\Svconnector\Event\InitializeConnectorEvent`, + since users of your service will expect that. Input none Output - string + void isAvailable() This method can be called when a connector service is about to be used to check if it is available or not. It is expected to return a boolean value - accordiningly. + accordingly. Input none @@ -92,6 +98,27 @@ checkConfiguration() array of errors, warnings or notices +getCallContext() + This method returns the current :ref:`call context object `. + + Input + none + + Output + :php:`\Cobweb\Svconnector\Domain\Model\Dto\CallContext` + + +getConnectionInformation() + This method returns any connection information that may have been set by + the connector or events called by the connector. + + Input + none + + Output + php:`\Cobweb\Svconnector\Domain\Model\Dto\ConnectionInformation` + + logConfigurationCheck() This method is used to cleanly report all configuration issues by logging them using the TYPO3 logging API. This is not automatically done by :php:`checkConfiguration()` @@ -115,7 +142,7 @@ query() programming practice to use it. Input - array of parameters + none Output mixed (result from the distant source) @@ -126,7 +153,7 @@ fetchRaw() distant source as is, without transformation. Input - array of parameters + none Output mixed (result from the distant source) @@ -137,7 +164,7 @@ fetchXML() distant source transformed into a XML structure (as a string). Input - array of parameters + none Output string (XML structure) @@ -148,7 +175,7 @@ fetchArray() distant source transformed into a PHP array. Input - array of parameters + none Output array @@ -164,7 +191,7 @@ postProcessOperations() It doesn't do anything by itself, but just calls events (or hooks). Input - array of parameters and a status + array of parameters (deprecated, pass an empty array instead) and a status Output void @@ -192,3 +219,45 @@ Example usage: // Get some data $context = $service->getCallContext()->get(); + + +.. _developers-api-connection-information: + +Connection information API +"""""""""""""""""""""""""" + +The connection information is meant to contain data relative to the current +connection to the third-party service being accessed by the connector service. +For example, this could be an authentication token retrieved during the service +initialization (i.e. when the :code:`initialize()` method was called). + +Assuming the following data is set: + +.. code-block:: php + + // Set some data + $service->getConnectionInformation()->add('token', ['sso' => '$$XyZSecureCode42']); + +it is later used to substitute variables in the connector parameters. The parameters are parsed +when getting a service from the registry. Considering the following parameters: + +.. code-block:: php + + [ + 'headers' => [ + 'token' => '{token.sso}' + ] + ] + +the service would then have the following concrete parameters for usage: + +.. code-block:: php + + [ + 'headers' => [ + 'token' => '$$XyZSecureCode42' + ] + ] + +A :php:`\Cobweb\Svconnector\Event\ProcessParametersEvent` event is fired after this +parsing, allowing for further manipulation of the connector parameters. diff --git a/Documentation/Developers/Events/Index.rst b/Documentation/Developers/Events/Index.rst new file mode 100644 index 0000000..e76fb33 --- /dev/null +++ b/Documentation/Developers/Events/Index.rst @@ -0,0 +1,36 @@ +.. ================================================== +.. FOR YOUR INFORMATION +.. -------------------------------------------------- +.. -*- coding: utf-8 -*- with BOM. + +.. include:: /Includes.rst.txt + + +.. _developers-events: + +Events +^^^^^^ + +The "svconnector" extension provides a number of events which can be used by the concrete +connector implementations. Except is special cases - since all connector services +share a common way of working - it should not be necessary to develop custom events. + + +.. _developers-events-initialize: + +\Cobweb\Svconnector\Event\InitializeConnectorEvent +"""""""""""""""""""""""""""""""""""""""""""""""""" + +This event is fired by the `initialize()` method. It is meant to perform custom +initializations needed by specific uses of the service, and can store results in +the :ref`connection information object ` for +dynamic usage in the connector parameters. + + +.. _developers-events-parameters: + +\Cobweb\Svconnector\Event\ProcessParametersEvent +"""""""""""""""""""""""""""""""""""""""""""""""" + +This event is fired after :ref`parameters have been parsed ` +and allows for further manipulation of the connector parameters. diff --git a/Documentation/Developers/Hooks/Index.rst b/Documentation/Developers/Hooks/Index.rst index ec7cba1..99956ea 100644 --- a/Documentation/Developers/Hooks/Index.rst +++ b/Documentation/Developers/Hooks/Index.rst @@ -27,6 +27,10 @@ processParameters the :code:`query()` method and a back-reference to the calling connector object. + .. warning:: + + This hook is deprecated. Use the :php:`\Cobweb\Svconnector\Event\ProcessParametersEvent` instead. + processResponse This hook is designed to process the data inside the :code:`query()` method, just as it is returned from the distant source. diff --git a/Documentation/Developers/Index.rst b/Documentation/Developers/Index.rst index decca4c..6313974 100644 --- a/Documentation/Developers/Index.rst +++ b/Documentation/Developers/Index.rst @@ -26,6 +26,7 @@ indications about how to implement your own connector services. Implementing/Index Exceptions/Index Hooks/Index + Events/Index Utilities/Index Sample/Index diff --git a/Documentation/Installation/Index.rst b/Documentation/Installation/Index.rst index eea6f46..5a170f8 100644 --- a/Documentation/Installation/Index.rst +++ b/Documentation/Installation/Index.rst @@ -17,6 +17,31 @@ extension must be installed since it provides the base class from which all connector services inherit. +.. _installation-updating-600: + +Updating to 6.0.0 +^^^^^^^^^^^^^^^^^ + +Version 6.0.0 adds support for TYPO3 13 and drops support for TYPO3 11. + +It introduces one new important method to the base API: :code:`initialize()`, which +is called by the registry when it collects all available services. +:ref:`Read more about it in the Developer's Guide `. + +Also new is the availability of a :ref:`call context ` which - as its name implies - +contains information about the context in which the service is being used. +Another object called :ref:`connection information ` may contain data than can +be used to dynamically change the parameters passed to the service. + +Most importantly, passing the connector parameters in the :code:`fetch*()` methods +has been deprecated. Instead, parameters must be passed when getting the connector +from the registry: + +.. code-block:: php + + $service = $connectorRegistry->getServiceForType('json', $parameters); + + .. _installation-updating-500: Updating to 5.0.0 diff --git a/Tests/Unit/Domain/Model/Dto/CallContextTest.php b/Tests/Unit/Domain/Model/Dto/CallContextTest.php index 40a7dce..78e05da 100644 --- a/Tests/Unit/Domain/Model/Dto/CallContextTest.php +++ b/Tests/Unit/Domain/Model/Dto/CallContextTest.php @@ -18,7 +18,7 @@ namespace Cobweb\Svconnector\Tests\Unit\Domain\Model\Dto; use Cobweb\Svconnector\Domain\Model\Dto\CallContext; -use Cobweb\Svconnector\Exception\NoSuchContextException; +use Cobweb\Svconnector\Exception\NoSuchDataException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -63,7 +63,7 @@ public function getForKeyReturnsExpectedElement(): void #[Test] public function getForKeyWithUnknownKeyThrowsException(): void { - $this->expectException(NoSuchContextException::class); + $this->expectException(NoSuchDataException::class); self::assertSame( [ 'location' => 'here', diff --git a/Tests/Unit/Utility/ParameterParserTest.php b/Tests/Unit/Utility/ParameterParserTest.php new file mode 100644 index 0000000..ed4d2d6 --- /dev/null +++ b/Tests/Unit/Utility/ParameterParserTest.php @@ -0,0 +1,145 @@ +subject = new ParameterParser(); + } + + public static function parseProvider(): array + { + $data = [ + 'token' => 'bar', + 'sso' => [ + 'token' => 'foo', + ], + ]; + return [ + 'empty array' => [ + 'parameters' => [], + 'data' => $data, + 'result' => [], + ], + 'nothing to substitute' => [ + 'parameters' => [ + 'hello' => 'world', + ], + 'data' => $data, + 'result' => [ + 'hello' => 'world', + ], + ], + 'with substitutions' => [ + 'parameters' => [ + 'auth1' => 'https://example.com/auth/{token}/', + 'auth2' => [ + 'uri' => 'https://example.com/auth/{sso.token}', + ], + ], + 'data' => $data, + 'result' => [ + 'auth1' => 'https://example.com/auth/bar/', + 'auth2' => [ + 'uri' => 'https://example.com/auth/foo', + ], + ], + ], + ]; + } + + #[Test] #[DataProvider('parseProvider')] + public function parseRecursivelySubstitutes(array $parameters, array $data, array $result): void + { + self::assertSame( + $result, + $this->subject->parse($parameters, $data) + ); + } + + public static function substituteProvider(): array + { + $data = [ + 'token' => 'bar', + 'sso' => [ + 'token' => 'foo', + ], + ]; + return [ + 'empty string' => [ + 'parameter' => '', + 'data' => $data, + 'result' => '', + ], + 'nothing to substitute' => [ + 'parameter' => 'Hello world', + 'data' => $data, + 'result' => 'Hello world', + ], + 'not a string - integer' => [ + 'parameter' => 1, + 'data' => $data, + 'result' => 1, + ], + 'not a string - boolean' => [ + 'parameter' => false, + 'data' => $data, + 'result' => false, + ], + 'with one-dimensional substitution' => [ + 'parameter' => 'https://example.com/auth/{token}/', + 'data' => $data, + 'result' => 'https://example.com/auth/bar/', + ], + 'with two-dimensional substitution' => [ + 'parameter' => 'https://example.com/auth/{sso.token}', + 'data' => $data, + 'result' => 'https://example.com/auth/foo', + ], + 'with back-to-back substitution' => [ + 'parameter' => 'https://example.com/auth/{sso.token}{token}/login', + 'data' => $data, + 'result' => 'https://example.com/auth/foobar/login', + ], + 'with non-matching data' => [ + 'parameter' => 'https://example.com/auth/{hooray}/', + 'data' => $data, + 'result' => 'https://example.com/auth/{hooray}/', + ], + ]; + } + + #[Test] #[DataProvider('substituteProvider')] + public function substituteSubstitutesAvailableVariables(mixed $parameter, array $data, mixed $result): void + { + self::assertEquals( + $result, + $this->subject->substitute($parameter, $data) + ); + } +}