Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metadata synch process [WIP] #190

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,9 @@ SENTRY_DSN=
# postgresql+advisory://db_user:db_password@localhost/db_name
LOCK_DSN=flock
###< symfony/lock ###

###> knplabs/github-api ###
GITHUB_AUTH_METHOD=http_password
GITHUB_USERNAME=username
GITHUB_SECRET=password_or_token
###< knplabs/github-api ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);

/**
* This file is part of the medicalmundi/marketplace-engine
*
* @copyright (c) 2024 MedicalMundi
*
* This software consists of voluntary contributions made by many individuals
* {@link https://github.com/medicalmundi/marketplace-engine/graphs/contributors developer} and is licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT
*/

namespace Metadata\AdapterCli\ForSynchronizingMetadata;

use Ecotone\Messaging\Attribute\ConsoleCommand;
use Ecotone\Modelling\CommandBus;
use Metadata\Core\Process\StartModuleMetadataUpdate;

class MetadataUpdateCommand
{
#[ConsoleCommand('metadata:module:update')]
public function execute(CommandBus $commandBus): void
{
$moduleId = 'foo';

// with error
//$repoUrl = 'https://github.com/zerai/foo';

// without metadata
//$repoUrl = 'https://github.com/zerai/oe-module-demo-farm-add-ons';

// with metadata
$repoUrl = 'https://github.com/MedicalMundi/oe-module-todo-list';

$command = new StartModuleMetadataUpdate($moduleId, $repoUrl);

$commandBus->send($command);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php declare(strict_types=1);

/**
* This file is part of the medicalmundi/marketplace-engine
*
* @copyright (c) 2024 MedicalMundi
*
* This software consists of voluntary contributions made by many individuals
* {@link https://github.com/medicalmundi/marketplace-engine/graphs/contributors developer} and is licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT
*/

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 ClientInterface $httpclient,
) {
}

public function readMetadataFromExternalSource(string $moduleUrl): ?ExternalMetadataDto
{
$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 {
$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'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
5 changes: 2 additions & 3 deletions _metadata/src/Core/MetadataUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

namespace Metadata\Core;

use Metadata\Core\MetadataValidationEngine\FixedTrueMetadataValidationEngineValidation;
use Metadata\Core\MetadataValidationEngine\ForMetadataSchemaValidation;
use Metadata\Core\MetadataValidationEngine\MetadataValidationException;
use Metadata\Core\MetadataValidationEngine\MetadataValidator;
use Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\ForReadingExternalMetadataSource;
use Metadata\Core\Port\Driven\ForReadingExternalMetadataSource\MetadataReaderException;
use Metadata\Core\Port\Driven\ForStoringMetadata;
Expand All @@ -29,10 +29,9 @@ class MetadataUpdater implements ForSynchronizingMetadata
public function __construct(
private readonly ForStoringMetadata $metadataStore,
private readonly ForReadingExternalMetadataSource $metadataReader,
/** TODO: Implement a real validator engine */
private ?ForMetadataSchemaValidation $validatorEngine = null,
) {
$this->validatorEngine = $validatorEngine ?? new FixedTrueMetadataValidationEngineValidation();
$this->validatorEngine = $validatorEngine ?? new MetadataValidator();
}

public function getMetadataForModule(string $moduleId): ?ModuleMetadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

namespace Metadata\Core\MetadataValidationEngine;

/**
* @throws MetadataValidationException
*/
interface ForMetadataSchemaValidation
{
public function validate(array $metadata): bool;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
class MetadataValidationException extends \Exception
{
public function __construct(
string $message = '',
int $code = 0,
?\Throwable $previous = null
) {
$message = 'Metadata validation error';
parent::__construct($message, $code, $previous);
$errorMessage = ($message === '') ? 'Metadata validation error' : $message;
parent::__construct($errorMessage, $code, $previous);
}
}
76 changes: 76 additions & 0 deletions _metadata/src/Core/MetadataValidationEngine/MetadataValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php declare(strict_types=1);

/**
* This file is part of the medicalmundi/marketplace-engine
*
* @copyright (c) 2024 MedicalMundi
*
* This software consists of voluntary contributions made by many individuals
* {@link https://github.com/medicalmundi/marketplace-engine/graphs/contributors developer} and is licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT
*/

namespace Metadata\Core\MetadataValidationEngine;

class MetadataValidator implements ForMetadataSchemaValidation
{
private const ALLOWED_CATEGORY = [
'administration',
'billing',
'ePrescribing',
'miscellaneous',
'telecom',
'telehealth',
'payment',
];

private const ALLOWED_TAG = [
'fax',
'organizer',
'reminder',
'scheduler',
'sms',
'todo',
];

/**
* @throws MetadataValidationException
*/
public function validate(array $metadata): bool
{
if (! \array_key_exists('category', $metadata)) {
throw new MetadataValidationException('Metadata key \'category\' not found');
} else {
$category = $metadata['category'];

if (! \is_string($category)) {
throw new MetadataValidationException('Metadata \'Category\' should be string type');
}

if (! \in_array($category, self::ALLOWED_CATEGORY)) {
throw new MetadataValidationException('Category not allowed: ' . $category);
}
}

if (! \array_key_exists('tags', $metadata)) {
throw new MetadataValidationException('Metadata key \'tags\' not found');
} else {
$tags = $metadata['tags'];

if (! \is_array($tags)) {
throw new MetadataValidationException('Metadata \'tags\' should be array type');
}
/** @var string $tag */
foreach ($tags as $tag) {
if (! \in_array($tag, self::ALLOWED_TAG)) {
throw new MetadataValidationException('Tag not allowed: ' . $tag);
}
}
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ public function __construct(
public readonly array $tags,
) {
}

public function toArray(): array
{
return [
'category' => $this->category,
'tags' => $this->tags,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ interface ForReadingExternalMetadataSource
/**
* @throws MetadataReaderException
*/
public function readMetadataFromExternalSource(string $moduleUrl): ExternalMetadataDto;
public function readMetadataFromExternalSource(string $moduleUrl): ?ExternalMetadataDto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
* @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT
*/

namespace Metadata\Core\MetadataValidationEngine;
namespace Metadata\Core\Process\Event;

class FixedTrueMetadataValidationEngineValidation implements ForMetadataSchemaValidation
class ModuleMetadataUpdateAbortedWithError
{
public function validate(array $metadata): bool
{
return true;
public function __construct(
public readonly string $moduleId,
public readonly mixed $error,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
* @license https://github.com/MedicalMundi/marketplace-engine/blob/main/LICENSE MIT
*/

namespace Metadata\Core\MetadataValidationEngine;
namespace Metadata\Core\Process\Event;

class FixedFalseMetadataValidationEngineValidation implements ForMetadataSchemaValidation
class ModuleMetadataUpdateCompletedWithInvalidMetadata
{
public function validate(array $metadata): bool
{
return false;
public function __construct(
public readonly string $moduleId
) {
}
}
Loading