From 61e4a0da5288f77550a8be0fd894e22a954891f9 Mon Sep 17 00:00:00 2001 From: zerai Date: Sun, 22 Sep 2024 14:09:28 +0200 Subject: [PATCH] MetadataUpdaterWorkflow improved. --- .../MetadataUpdateCommand.php | 4 +- ...dapterForReadingExternalMetadataSource.php | 92 +++++++- .../StatelessWorkflowProcess.php | 216 ------------------ ...dapterForReadingExternalMetadataSource.php | 14 +- .../ForReadingExternalMetadataSource.php | 2 +- .../ModuleMetadataUpdateAbortedWithError.php} | 6 +- ...dataUpdateCompletedWithInvalidMetadata.php | 24 ++ ...leMetadataUpdateCompletedWithMetadata.php} | 4 +- ...MetadataUpdateCompletedWithoutMetadata.php | 24 ++ .../Process/MetadataSynchronizerWorkflow.php | 163 +++++++++++++ .../Core/Process/MetadataUpdaterWorkflow.php | 127 ---------- ...data.php => StartModuleMetadataUpdate.php} | 2 +- .../ValueObject}/ComposerJsonFile.php | 22 +- .../Infrastructure/Framework/services.yaml | 1 + .../MetadataSynchronizerWorkflowTest.php | 202 ++++++++++++++++ .../Process/MetadataUpdaterWorkflowTest.php | 65 ------ .../ValueObject}/ComposerJsonFileTest.php | 23 +- psalm-baseline.xml | 12 +- .../allowed_in_metadata_adapters.php | 1 + .../allowed_in_metadata_core.php | 3 + 20 files changed, 565 insertions(+), 442 deletions(-) delete mode 100644 _metadata/src/AdapterForReadingExternalMetadataSource/StatelessWorkflowProcess.php rename _metadata/src/{AdapterForReadingExternalMetadataSource/UpdateModuleMetadata.php => Core/Process/Event/ModuleMetadataUpdateAbortedWithError.php} (83%) create mode 100644 _metadata/src/Core/Process/Event/ModuleMetadataUpdateCompletedWithInvalidMetadata.php rename _metadata/src/Core/Process/{ModuleMetadataDetected.php => Event/ModuleMetadataUpdateCompletedWithMetadata.php} (90%) create mode 100644 _metadata/src/Core/Process/Event/ModuleMetadataUpdateCompletedWithoutMetadata.php create mode 100644 _metadata/src/Core/Process/MetadataSynchronizerWorkflow.php delete mode 100644 _metadata/src/Core/Process/MetadataUpdaterWorkflow.php rename _metadata/src/Core/Process/{UpdateModuleMetadata.php => StartModuleMetadataUpdate.php} (95%) rename _metadata/src/{AdapterForReadingExternalMetadataSource => Core/ValueObject}/ComposerJsonFile.php (77%) create mode 100644 _metadata/tests/Unit/Core/Process/MetadataSynchronizerWorkflowTest.php delete mode 100644 _metadata/tests/Unit/Core/Process/MetadataUpdaterWorkflowTest.php rename _metadata/tests/Unit/{ComposerJsonFile => Core/ValueObject}/ComposerJsonFileTest.php (88%) diff --git a/_metadata/src/AdapterCli/ForSynchronizingMetadata/MetadataUpdateCommand.php b/_metadata/src/AdapterCli/ForSynchronizingMetadata/MetadataUpdateCommand.php index fa34b68..161e01c 100644 --- a/_metadata/src/AdapterCli/ForSynchronizingMetadata/MetadataUpdateCommand.php +++ b/_metadata/src/AdapterCli/ForSynchronizingMetadata/MetadataUpdateCommand.php @@ -17,7 +17,7 @@ use Ecotone\Messaging\Attribute\ConsoleCommand; use Ecotone\Modelling\CommandBus; -use Metadata\AdapterForReadingExternalMetadataSource\UpdateModuleMetadata; +use Metadata\Core\Process\StartModuleMetadataUpdate; class MetadataUpdateCommand { @@ -35,7 +35,7 @@ public function execute(CommandBus $commandBus): void // with metadata $repoUrl = 'https://github.com/MedicalMundi/oe-module-todo-list'; - $command = new UpdateModuleMetadata($moduleId, $repoUrl); + $command = new StartModuleMetadataUpdate($moduleId, $repoUrl); $commandBus->send($command); } diff --git a/_metadata/src/AdapterForReadingExternalMetadataSource/GithubAdapterForReadingExternalMetadataSource.php b/_metadata/src/AdapterForReadingExternalMetadataSource/GithubAdapterForReadingExternalMetadataSource.php index ad158b4..c2a04d1 100644 --- a/_metadata/src/AdapterForReadingExternalMetadataSource/GithubAdapterForReadingExternalMetadataSource.php +++ b/_metadata/src/AdapterForReadingExternalMetadataSource/GithubAdapterForReadingExternalMetadataSource.php @@ -15,27 +15,103 @@ namespace Metadata\AdapterForReadingExternalMetadataSource; +use Github\Api\Repo; use Github\Client as GithubClient; use Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\ExternalMetadataDto; use Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\ForReadingExternalMetadataSource; use Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\MetadataReaderException; +use Metadata\Core\ValueObject\ComposerJsonFile; +use Metadata\Core\ValueObject\Repository; +use Nyholm\Psr7\Request; +use Psr\Http\Client\ClientInterface; class GithubAdapterForReadingExternalMetadataSource implements ForReadingExternalMetadataSource { public function __construct( - private readonly GithubClient $githubClient + private readonly GithubClient $githubClient, + private readonly ClientInterface $httpclient, ) { } - public function readMetadataFromExternalSource(string $moduleUrl): ExternalMetadataDto + public function readMetadataFromExternalSource(string $moduleUrl): ?ExternalMetadataDto { + /** + * TODO: handle failure + */ + $repository = Repository::createFromRepositoryUrl($moduleUrl); + + + $defaultBranch = $this->getDefaultBranchName($repository); + + /** + * TODO: refactor + * $defaultBranch dovrebbe essere sempre un tipo string, + * la funzione sopra non dovrebbe tornare null + * ma eccezione di tipo http del client api + * o eccezione per dati non processabili + */ + if (null === $defaultBranch) { + return null; + } + + $composerJsonFileContent = $this->downloadComposerJsonFileContent($moduleUrl, $defaultBranch); + + $composerFile = ComposerJsonFile::createFromJson($composerJsonFileContent); + + if (! $composerFile->hasMetadata()) { + return null; + } + + $extractedMetadata = (array) $composerFile->getMetadata(); + $metadataDto = new ExternalMetadataDto( + enableSync: true, + category: (string) $extractedMetadata['category'], + tags: (array) $extractedMetadata['tags'], + ); + + return $metadataDto; + } + + private function doDownloadHttpRequest(string $url): string + { + $request = new Request('GET', $url); + + $response = $this->httpclient->sendRequest($request); + + return $response->getBody()->getContents(); + } + + private function downloadComposerJsonFileContent(string $url, string $reference): string + { + try { + $repository = Repository::createFromRepositoryUrl($url); + /** @var Repo $repoApi */ + $repoApi = $this->githubClient->api('repo'); + $fileInfo = (array) $repoApi + ->contents() + ->show($repository->getUsername(), $repository->getName(), 'composer.json', $reference); + + $composerJsonFileContent = $this->doDownloadHttpRequest((string) $fileInfo['download_url']); + + return $composerJsonFileContent; + } catch (\Exception $exception) { + throw new MetadataReaderException('Impossible to read metadata from: ' . $url . ' error: ' . $exception->getMessage()); + } + } + + private function getDefaultBranchName(Repository $repository): ? string + { + $data = []; try { - //get composer.json - // extract metadata section - // create metadataDto - return new ExternalMetadataDto(true, 'a category', ['tag-1', 'tag-2']); - } catch (\RuntimeException $exception) { - throw new MetadataReaderException($exception->getMessage()); + $data = $this->githubClient->repo()->show($repository->getUsername(), $repository->getName()); + } catch (\Exception $exception) { + throw $exception; } + + if (! \array_key_exists('default_branch', $data)) { + return null; + } + + return (string) $data['default_branch']; } } diff --git a/_metadata/src/AdapterForReadingExternalMetadataSource/StatelessWorkflowProcess.php b/_metadata/src/AdapterForReadingExternalMetadataSource/StatelessWorkflowProcess.php deleted file mode 100644 index f407108..0000000 --- a/_metadata/src/AdapterForReadingExternalMetadataSource/StatelessWorkflowProcess.php +++ /dev/null @@ -1,216 +0,0 @@ -logger->info('MetadataUpdater - init update for module id: ' . $command->moduleId); - - return $command; - } - - #[InternalHandler( - inputChannelName: 'module.enrich', - outputChannelName: 'module.getDefaultBranch', - changingHeaders: true - )] - public function enrichCommand(UpdateModuleMetadata $command, GithubClient $githubClient): array - { - /** - * TODO: handle failure - */ - $repository = Repository::createFromRepositoryUrl($command->repositoryUrl); - - - return [ - 'repository' => $repository, - ]; - } - - #[InternalHandler( - inputChannelName: 'module.getDefaultBranch', - outputChannelName: 'module.getComposerJson', - changingHeaders: true, - )] - public function getDefaultBranch( - UpdateModuleMetadata $command, - #[Header('repository')] - Repository $repository, - GithubClient $githubClient - ): ?array { - /** - * TODO: handle failure - */ - try { - $data = $githubClient->repo()->show($repository->getUsername(), $repository->getName()); - if (\array_key_exists('default_branch', $data)) { - return [ - 'default_branch' => (string) $data['default_branch'], - ]; - } - } catch (\Exception $exception) { - $this->logger->error('Metadata updater error on module id: ' . $command->moduleId . ' ' . $exception->getMessage()); - } - - return null; - } - - #[InternalHandler( - inputChannelName: 'module.getComposerJson', - outputChannelName: 'module.extractMetadata', - changingHeaders: true, - )] - public function getComposerJson( - UpdateModuleMetadata $command, - #[Header('repository')] - Repository $repository, - #[Header('default_branch')] - string $defaultBranchName, - ClientInterface $httpClient, - GithubClient $githubClient, - ): ?array { - $path = 'composer.json'; - - $reference = $defaultBranchName; - - /** - * TODO: handle failure - */ - try { - /** @var Repo $repoApi */ - $repoApi = $githubClient->api('repo'); - $fileInfo = (array) $repoApi - ->contents() - ->show($repository->getUsername(), $repository->getName(), $path, $reference); - $content = $this->getComposerJsonFileContent((string) $fileInfo['download_url'], $httpClient); - - return [ - 'composer_json_content' => $content, - ]; - } catch (\Exception $exception) { - $this->logger->error('Metadata updater error on module id: ' . $command->moduleId . ' ' . $exception->getMessage()); - } - - return null; - } - - #[InternalHandler( - inputChannelName: 'module.extractMetadata', - outputChannelName: 'module.validateMetadata', - changingHeaders: true, - )] - public function extractMetadata( - UpdateModuleMetadata $command, - #[Header('composer_json_content')] - string $composerJsonContent, - ): ?array { - $ar = (array) json_decode($composerJsonContent, true); - - if ($this->hasMetadata($ar)) { - return [ - 'metadata' => $ar['extra']['openemr-module']['metadata']['oe-modules.com'], - ]; - } - - return null; - } - - #[InternalHandler(inputChannelName: 'module.validateMetadata')] - public function validateMetadata(#[Header('metadata')] array $metadata, MetadataValidator $metadataValidator): ?UpdateModuleMetadata - { - if ($metadataValidator->validate($metadata)) { - echo 'metadata pass'; - } else { - echo 'metadata not pass'; - } - - return null; - } - - #[InternalHandler(inputChannelName: 'image.upload')] - public function uploadImage(UpdateModuleMetadata $command): void - { - //$imageUploader->uploadImage($command->path); - echo 'yyyy'; - } - - private function getComposerJsonFileContent(string $url, ClientInterface $httpclient): string - { - $request = new Request('GET', $url); - - $response = $httpclient->sendRequest($request); - - $x = $response->getBody()->getContents(); - - return $x; - } - - /** - * TODO: refactor - */ - private function hasMetadata(array $data): bool - { - $result = false; - - if (\array_key_exists('extra', $data)) { - $extraSection = (array) $data['extra']; - - - if (\array_key_exists('openemr-module', $extraSection)) { - $openEmrModuleSection = (array) $extraSection['openemr-module']; - - if (\array_key_exists('metadata', $openEmrModuleSection)) { - $metadataSection = (array) $openEmrModuleSection['metadata']; - - if (\array_key_exists('oe-modules.com', $metadataSection)) { - $result = true; - } - } - } - } - - return $result; - } -} diff --git a/_metadata/src/AdapterForReadingExternalMetadataSourceStub/StubAdapterForReadingExternalMetadataSource.php b/_metadata/src/AdapterForReadingExternalMetadataSourceStub/StubAdapterForReadingExternalMetadataSource.php index 3454b4c..1d128a4 100644 --- a/_metadata/src/AdapterForReadingExternalMetadataSourceStub/StubAdapterForReadingExternalMetadataSource.php +++ b/_metadata/src/AdapterForReadingExternalMetadataSourceStub/StubAdapterForReadingExternalMetadataSource.php @@ -22,16 +22,26 @@ class StubAdapterForReadingExternalMetadataSource implements ForReadingExternalM { public function __construct( private array $metadataDtosIndexedByUrl = [], + private array $exceptions = [], ) { } - public function readMetadataFromExternalSource(string $moduleUrl): ExternalMetadataDto + public function readMetadataFromExternalSource(string $moduleUrl): ?ExternalMetadataDto { + if (0 < \count($this->exceptions)) { + throw $this->exceptions[0]; + } + return $this->metadataDtosIndexedByUrl[$moduleUrl]; } - public function setExternalMetadataDto(string $url, ExternalMetadataDto $externalMetadataDto): void + public function setExternalMetadataDto(string $url, ?ExternalMetadataDto $externalMetadataDto): void { $this->metadataDtosIndexedByUrl[$url] = $externalMetadataDto; } + + public function setException(object $exception): void + { + $this->exceptions[] = $exception; + } } diff --git a/_metadata/src/Core/Port/Driven/ForReadingExternalMetadataSource/ForReadingExternalMetadataSource.php b/_metadata/src/Core/Port/Driven/ForReadingExternalMetadataSource/ForReadingExternalMetadataSource.php index 87bb21f..5232a55 100644 --- a/_metadata/src/Core/Port/Driven/ForReadingExternalMetadataSource/ForReadingExternalMetadataSource.php +++ b/_metadata/src/Core/Port/Driven/ForReadingExternalMetadataSource/ForReadingExternalMetadataSource.php @@ -20,5 +20,5 @@ interface ForReadingExternalMetadataSource /** * @throws MetadataReaderException */ - public function readMetadataFromExternalSource(string $moduleUrl): ExternalMetadataDto; + public function readMetadataFromExternalSource(string $moduleUrl): ?ExternalMetadataDto; } diff --git a/_metadata/src/AdapterForReadingExternalMetadataSource/UpdateModuleMetadata.php b/_metadata/src/Core/Process/Event/ModuleMetadataUpdateAbortedWithError.php similarity index 83% rename from _metadata/src/AdapterForReadingExternalMetadataSource/UpdateModuleMetadata.php rename to _metadata/src/Core/Process/Event/ModuleMetadataUpdateAbortedWithError.php index 4da3340..cf828da 100644 --- a/_metadata/src/AdapterForReadingExternalMetadataSource/UpdateModuleMetadata.php +++ b/_metadata/src/Core/Process/Event/ModuleMetadataUpdateAbortedWithError.php @@ -13,13 +13,13 @@ * @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT */ -namespace Metadata\AdapterForReadingExternalMetadataSource; +namespace Metadata\Core\Process\Event; -class UpdateModuleMetadata +class ModuleMetadataUpdateAbortedWithError { public function __construct( public readonly string $moduleId, - public readonly string $repositoryUrl + public readonly mixed $error, ) { } } diff --git a/_metadata/src/Core/Process/Event/ModuleMetadataUpdateCompletedWithInvalidMetadata.php b/_metadata/src/Core/Process/Event/ModuleMetadataUpdateCompletedWithInvalidMetadata.php new file mode 100644 index 0000000..eb842a0 --- /dev/null +++ b/_metadata/src/Core/Process/Event/ModuleMetadataUpdateCompletedWithInvalidMetadata.php @@ -0,0 +1,24 @@ +logger->info('Metadata synchronizer - initialize update process for module id: ' . $command->moduleId); + + try { + $repository = Repository::createFromRepositoryUrl($command->repositoryUrl); + + if ($repository->isSupported()) { + return $command; + } else { + $eventBus->publish( + new ModuleMetadataUpdateAbortedWithError($command->moduleId, 'Unsupported githost service provider: ' . $repository->getSource()) + ); + + $this->logger->info('Metadata synchronizer - unsupported repository detected url: ' . $command->repositoryUrl); + } + } catch (UnexpectedValueException $exception) { + $eventBus->publish( + new ModuleMetadataUpdateAbortedWithError($command->moduleId, $exception->getMessage()) + ); + $this->logger->info('Metadata synchronizer - error: ' . $exception->getMessage()); + } + + return null; + } + + #[InternalHandler( + inputChannelName: 'process.enrich', + outputChannelName: 'process.readMetadata', + changingHeaders: true, + )] + public function enrichCommand(StartModuleMetadataUpdate $command): array + { + $repository = Repository::createFromRepositoryUrl($command->repositoryUrl); + + return [ + 'repository' => $repository, + ]; + } + + #[InternalHandler( + inputChannelName: 'process.readMetadata', + outputChannelName: 'process.validateMetadata', + changingHeaders: true, + )] + public function readMetadata( + StartModuleMetadataUpdate $command, + EventBus $eventBus + ): ?array { + try { + $metadataDto = $this->metadataReader->readMetadataFromExternalSource($command->repositoryUrl); + + + if (null === $metadataDto) { + $eventBus->publish( + new ModuleMetadataUpdateCompletedWithoutMetadata($command->moduleId) + ); + } else { + return [ + 'metadata_dto' => $metadataDto, + ]; + } + } catch (MetadataReaderException $exception) { + $eventBus->publish( + new ModuleMetadataUpdateAbortedWithError($command->moduleId, $exception->getMessage()) + ); + $this->logger->info('Metadata synchronizer - error: ' . $exception->getMessage()); + } + + + return null; + } + + #[InternalHandler( + inputChannelName: 'process.validateMetadata', + outputChannelName: 'process.notifyProcessResult', + changingHeaders: true, + )] + public function validateMetadata( + StartModuleMetadataUpdate $command, + #[Header('metadata_dto')] + ExternalMetadataDto $metadataDto, + MetadataValidator $metadataValidator, + EventBus $eventBus, + ): ?array { + try { + $metadataValidationResult = $metadataValidator->validate($metadataDto->toArray()); + + return [ + 'metadata_validation_result' => $metadataValidationResult, + ]; + } catch (MetadataValidationException $exception) { + $eventBus->publish( + new ModuleMetadataUpdateCompletedWithInvalidMetadata($command->moduleId) + ); + $this->logger->info('Metadata synchronizer - error: ' . $exception->getMessage()); + } + + return null; + } + + #[InternalHandler( + inputChannelName: 'process.notifyProcessResult', + )] + public function triggerMetadataUpdate( + StartModuleMetadataUpdate $command, + #[Header('metadata_validation_result')] + bool $validationStatus, + #[Header('metadata_dto')] + ExternalMetadataDto $metadataDto, + EventBus $eventBus + ): void { + if ($validationStatus) { + $eventBus->publish(new ModuleMetadataUpdateCompletedWithMetadata($command->moduleId, $metadataDto)); + } + } +} diff --git a/_metadata/src/Core/Process/MetadataUpdaterWorkflow.php b/_metadata/src/Core/Process/MetadataUpdaterWorkflow.php deleted file mode 100644 index 10cbc6b..0000000 --- a/_metadata/src/Core/Process/MetadataUpdaterWorkflow.php +++ /dev/null @@ -1,127 +0,0 @@ -logger->info('MetadataUpdater - initialize update process for module id: ' . $command->moduleId); - - try { - $repository = Repository::createFromRepositoryUrl($command->repositoryUrl); - - if ($repository->isSupported()) { - return $command; - } else { - $this->logger->info('MetadataUpdater - unsupported repository detected url: ' . $command->repositoryUrl); - } - } catch (UnexpectedValueException $exception) { - $this->logger->info('MetadataUpdater - error: ' . $exception->getMessage()); - } - - return null; - } - - #[InternalHandler( - inputChannelName: 'process.enrich', - outputChannelName: 'process.readMetadata', - changingHeaders: true, - )] - public function enrichCommand(UpdateModuleMetadata $command): array - { - $repository = Repository::createFromRepositoryUrl($command->repositoryUrl); - - return [ - 'repository' => $repository, - ]; - } - - #[InternalHandler( - inputChannelName: 'process.readMetadata', - outputChannelName: 'process.validateMetadata', - changingHeaders: true, - )] - public function readMetadata( - UpdateModuleMetadata $command, - ): ?array { - /** - * if no error enrich command or fail - */ - $metadataDto = $this->metadataReader->readMetadataFromExternalSource($command->repositoryUrl); - - return [ - 'metadata_dto' => $metadataDto, - ]; - } - - #[InternalHandler( - inputChannelName: 'process.validateMetadata', - outputChannelName: 'process.notifyProcessResult', - changingHeaders: true, - )] - public function validateMetadata( - #[Header('metadata_dto')] - ExternalMetadataDto $metadataDto, - MetadataValidator $metadataValidator - ): array { - $metadataValidationResult = $metadataValidator->validate($metadataDto->toArray()); - - return [ - 'metadata_validation_result' => $metadataValidationResult, - ]; - } - - #[InternalHandler( - inputChannelName: 'process.notifyProcessResult', - )] - public function triggerMetadataUpdate( - UpdateModuleMetadata $command, - #[Header('metadata_validation_result')] - bool $validationStatus, - #[Header('metadata_dto')] - ExternalMetadataDto $metadataDto, - EventBus $eventBus - ): void { - if ($validationStatus) { - $eventBus->publish(new ModuleMetadataDetected($command->moduleId, $metadataDto)); - } else { - echo 'complete with no metadata'; - //$eventBus->publish(new ModuleMetadataDetected($command->moduleId, $metadataDto)); - } - } -} diff --git a/_metadata/src/Core/Process/UpdateModuleMetadata.php b/_metadata/src/Core/Process/StartModuleMetadataUpdate.php similarity index 95% rename from _metadata/src/Core/Process/UpdateModuleMetadata.php rename to _metadata/src/Core/Process/StartModuleMetadataUpdate.php index c2b9b60..32d8f3e 100644 --- a/_metadata/src/Core/Process/UpdateModuleMetadata.php +++ b/_metadata/src/Core/Process/StartModuleMetadataUpdate.php @@ -15,7 +15,7 @@ namespace Metadata\Core\Process; -class UpdateModuleMetadata +class StartModuleMetadataUpdate { public function __construct( public readonly string $moduleId, diff --git a/_metadata/src/AdapterForReadingExternalMetadataSource/ComposerJsonFile.php b/_metadata/src/Core/ValueObject/ComposerJsonFile.php similarity index 77% rename from _metadata/src/AdapterForReadingExternalMetadataSource/ComposerJsonFile.php rename to _metadata/src/Core/ValueObject/ComposerJsonFile.php index 71794ed..ab068ff 100644 --- a/_metadata/src/AdapterForReadingExternalMetadataSource/ComposerJsonFile.php +++ b/_metadata/src/Core/ValueObject/ComposerJsonFile.php @@ -13,20 +13,17 @@ * @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT */ -namespace Metadata\AdapterForReadingExternalMetadataSource; +namespace Metadata\Core\ValueObject; class ComposerJsonFile { private function __construct( - private string $source, + private string $value, ) { } public static function createFromJson(string $json): self { - /** - * TODO: check if valid json - */ return new self($json); } @@ -37,12 +34,11 @@ public function hasMetadata(): bool { $result = false; - $data = (array) json_decode($this->source, true); + $data = (array) json_decode($this->value, true); if (\array_key_exists('extra', $data)) { $extraSection = (array) $data['extra']; - if (\array_key_exists('openemr-module', $extraSection)) { $openEmrModuleSection = (array) $extraSection['openemr-module']; @@ -59,11 +55,15 @@ public function hasMetadata(): bool return $result; } - public function getMetadata(): array + public function getMetadata(): ?array { - $data = (array) json_decode($this->source, true); + $data = (array) json_decode($this->value, true); + + if ($this->hasMetadata()) { + /** @psalm-suppress MixedArrayAccess */ + return (array) $data['extra']['openemr-module']['metadata']['oe-modules.com']; + } - /** @psalm-suppress MixedArrayAccess */ - return (array) $data['extra']['openemr-module']['metadata']['oe-modules.com']; + return null; } } diff --git a/_metadata/src/Infrastructure/Framework/services.yaml b/_metadata/src/Infrastructure/Framework/services.yaml index f6c62f6..4c56db7 100644 --- a/_metadata/src/Infrastructure/Framework/services.yaml +++ b/_metadata/src/Infrastructure/Framework/services.yaml @@ -19,3 +19,4 @@ services: # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones + Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\ForReadingExternalMetadataSource: '@Metadata\AdapterForReadingExternalMetadataSource\GithubAdapterForReadingExternalMetadataSource' \ No newline at end of file diff --git a/_metadata/tests/Unit/Core/Process/MetadataSynchronizerWorkflowTest.php b/_metadata/tests/Unit/Core/Process/MetadataSynchronizerWorkflowTest.php new file mode 100644 index 0000000..9650e45 --- /dev/null +++ b/_metadata/tests/Unit/Core/Process/MetadataSynchronizerWorkflowTest.php @@ -0,0 +1,202 @@ +stubAdapterForReadingExternalMetadataSource = new StubAdapterForReadingExternalMetadataSource(); + $this->messagingSystem = EcotoneLite::bootstrapFlowTesting( + [MetadataSynchronizerWorkflow::class], + [ + MetadataSynchronizerWorkflow::class => new MetadataSynchronizerWorkflow(new NullLogger(), $this->stubAdapterForReadingExternalMetadataSource), + MetadataValidator::class => new MetadataValidator(), + ] + ); + } + + #[Test] + public function shouldNotifyProcessCompleteWithMetadata() + { + $moduleId = Uuid::uuid4()->toString(); + $aStubbedMetadataDto = new ExternalMetadataDto( + enableSync: true, + category: 'billing', + tags: ['sms', 'fax'] + ); + $this->stubAdapterForReadingExternalMetadataSource->setExternalMetadataDto(self::A_VALID_REPOSITORY_URL, $aStubbedMetadataDto); + $expectedEvent = new ModuleMetadataUpdateCompletedWithMetadata($moduleId, $aStubbedMetadataDto); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::A_VALID_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + #[Test] + public function shouldNotifyProcessCompletedWithoutMetadata() + { + $moduleId = Uuid::uuid4()->toString(); + $aStubbedMetadataDto = null; + $this->stubAdapterForReadingExternalMetadataSource->setExternalMetadataDto(self::A_VALID_REPOSITORY_URL, $aStubbedMetadataDto); + $expectedEvent = new ModuleMetadataUpdateCompletedWithoutMetadata($moduleId); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::A_VALID_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + #[Test] + public function shouldNotifyProcessCompletedWithInvalidMetadata() + { + $moduleId = Uuid::uuid4()->toString(); + $aStubbedMetadataDto = new ExternalMetadataDto( + enableSync: true, + category: 'invalid-category', + tags: ['invalid-tag-1', 'invalid-tag-2'] + ); + $this->stubAdapterForReadingExternalMetadataSource->setExternalMetadataDto(self::A_VALID_REPOSITORY_URL, $aStubbedMetadataDto); + $expectedEvent = new ModuleMetadataUpdateCompletedWithInvalidMetadata($moduleId); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::A_VALID_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + #[Test] + public function shouldNotifyProcessAbortedWithErrorWhenRepositoryUrlIsInvalid() + { + $moduleId = Uuid::uuid4()->toString(); + $expectedEvent = new ModuleMetadataUpdateAbortedWithError($moduleId, 'Impossible to fetch package by "' . self::AN_INVALID_REPOSITORY_URL . '" repository.'); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::AN_INVALID_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + #[Test] + public function shouldNotifyProcessAbortedWithErrorWhenGitHostingServiceProviderIsNotSupported() + { + $moduleId = Uuid::uuid4()->toString(); + $expectedEvent = new ModuleMetadataUpdateAbortedWithError($moduleId, 'Unsupported githost service provider: unsupportedgithostingservice.com'); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::AN_UNSUPPORTED_SERVICE_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + #[Test] + public function shouldNotifyProcessAbortedWithErrorWhenMetadataReaderHasIssue() + { + $moduleId = Uuid::uuid4()->toString(); + $this->stubAdapterForReadingExternalMetadataSource->setException(new MetadataReaderException('Network error')); + $expectedEvent = new ModuleMetadataUpdateAbortedWithError($moduleId, 'Network error'); + + + $result = $this->messagingSystem + ->sendCommand(new StartModuleMetadataUpdate($moduleId, self::A_VALID_REPOSITORY_URL)) + ->getRecordedEvents(); + + + $this->assertEquals( + [$expectedEvent], + $result + ); + } + + protected function tearDown(): void + { + $this->stubAdapterForReadingExternalMetadataSource = null; + $this->messagingSystem = null; + } +} diff --git a/_metadata/tests/Unit/Core/Process/MetadataUpdaterWorkflowTest.php b/_metadata/tests/Unit/Core/Process/MetadataUpdaterWorkflowTest.php deleted file mode 100644 index 4e1f37a..0000000 --- a/_metadata/tests/Unit/Core/Process/MetadataUpdaterWorkflowTest.php +++ /dev/null @@ -1,65 +0,0 @@ -toString(); - $repositoryUrl = 'https://github.com/MedicalMundi/oe-module-todo-list'; - $aMetadataDto = new ExternalMetadataDto( - enableSync: true, - category: 'billing', - tags: ['sms', 'fax'] - ); - $expectedMessage = new ModuleMetadataDetected($moduleId, $aMetadataDto); - $stubAdapterForReadingExternalMetadataSource->setExternalMetadataDto($repositoryUrl, $aMetadataDto); - $ecotoneLite = EcotoneLite::bootstrapFlowTesting( - [MetadataUpdaterWorkflow::class], - [ - MetadataUpdaterWorkflow::class => new MetadataUpdaterWorkflow(new NullLogger(), $stubAdapterForReadingExternalMetadataSource), - MetadataValidator::class => new MetadataValidator(), - ] - ); - - - $this->assertEquals( - [$expectedMessage], - $ecotoneLite - ->sendCommand(new UpdateModuleMetadata($moduleId, $repositoryUrl)) - ->getRecordedEvents() - ); - } -} diff --git a/_metadata/tests/Unit/ComposerJsonFile/ComposerJsonFileTest.php b/_metadata/tests/Unit/Core/ValueObject/ComposerJsonFileTest.php similarity index 88% rename from _metadata/tests/Unit/ComposerJsonFile/ComposerJsonFileTest.php rename to _metadata/tests/Unit/Core/ValueObject/ComposerJsonFileTest.php index 65f1186..e07e9ee 100644 --- a/_metadata/tests/Unit/ComposerJsonFile/ComposerJsonFileTest.php +++ b/_metadata/tests/Unit/Core/ValueObject/ComposerJsonFileTest.php @@ -13,9 +13,9 @@ * @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT */ -namespace MetadataTests\Unit\ComposerJsonFile; +namespace MetadataTests\Unit\Core\ValueObject; -use Metadata\AdapterForReadingExternalMetadataSource\ComposerJsonFile; +use Metadata\Core\ValueObject\ComposerJsonFile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; @@ -35,7 +35,6 @@ public function shouldDetectIfHasMetadata(bool $expectedResult, string $json): v } #[Test] - #[DataProvider(('jsonWithMetadataDataprovider'))] public function shouldReturnMetadata(): void { $json = '{ @@ -64,6 +63,24 @@ public function shouldReturnMetadata(): void self::assertSame($expectedResult, $sut->getMetadata()); } + #[Test] + public function shouldReturnNullWhenThereAreNotMetadata(): void + { + $json = '{ + "extra": { + "openemr-module": { + "metadata": { + } + } + } + }' + ; + + $sut = ComposerJsonFile::createFromJson($json); + + self::assertNull($sut->getMetadata()); + } + public static function jsonWithoutMetadataDataprovider(): iterable { return [ diff --git a/psalm-baseline.xml b/psalm-baseline.xml index a71130a..172a8d5 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -141,7 +141,7 @@ - ExternalMetadataDto + ?ExternalMetadataDto metadataDtosIndexedByUrl[$moduleUrl]]]> @@ -156,6 +156,16 @@ + + category]]> + enableSync]]> + tags]]> + + + category]]> + enableSync]]> + tags]]> + repositoryUrl validate diff --git a/tools/phparkitect/VendorDependencies/allowed_in_metadata_adapters.php b/tools/phparkitect/VendorDependencies/allowed_in_metadata_adapters.php index 9dd0136..bf8326c 100644 --- a/tools/phparkitect/VendorDependencies/allowed_in_metadata_adapters.php +++ b/tools/phparkitect/VendorDependencies/allowed_in_metadata_adapters.php @@ -7,6 +7,7 @@ 'Ecotone\Modelling\CommandBus', 'Github\Client', + 'Nyholm\Psr7\Request', 'RuntimeException', 'UnexpectedValueException' diff --git a/tools/phparkitect/VendorDependencies/allowed_in_metadata_core.php b/tools/phparkitect/VendorDependencies/allowed_in_metadata_core.php index 223076d..34da3b2 100644 --- a/tools/phparkitect/VendorDependencies/allowed_in_metadata_core.php +++ b/tools/phparkitect/VendorDependencies/allowed_in_metadata_core.php @@ -1,6 +1,8 @@