diff --git a/Installation/Action/synczaken.action.json b/Installation/Action/synczaken.action.json new file mode 100644 index 0000000..8bb55a2 --- /dev/null +++ b/Installation/Action/synczaken.action.json @@ -0,0 +1,34 @@ +{ + "title": "ZgwToVrijbrpBirthAction", + "$id": "https://vrijbrp.nl/action/vrijbrp.synczaken.action.json", + "$schema": "https://json-schema.org/draft/2020-12/action", + "version": "0.0.2", + "listens": [ + "vrijbrp.zaken.sync" + ], + "throws": [], + "conditions": { + "==": [ + 1, + 1 + ] + }, + "class": "CommonGateway\\VrijBRPToZGWBundle\\ActionHandler\\SynchronizeCollectionHandler", + "configuration": { + "source": "https://vrijbrp.nl/source/vrijbrp.dossiers.source.json", + "schema": "https://vng.opencatalogi.nl/schemas/zrc.zaak.schema.json", + "mapping": "https://commongateway.nl/mapping/vrijbrp.dossierToZaak.mapping.json", + "endpoint": "/api/v1/dossiers/search", + "idField": "dossierId", + "resultsPath": "result.content", + "method": "POST", + "body": { + "types": [ + "intra_mun_relocation", + "inter_mun_relocation" + ] + } + }, + "isLockable": false, + "isEnabled": true +} \ No newline at end of file diff --git a/composer.json b/composer.json index 4c55e24..bcc6bda 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "license": "EUPL-1.2", "minimum-stability": "dev", "require": { - "php": ">=7.4", + "php": ">=8.2", "commongateway/corebundle": "^1.2.68 | <2.0" }, "require-dev": { diff --git a/src/ActionHandler/SynchronizeCollectionHandler.php b/src/ActionHandler/SynchronizeCollectionHandler.php new file mode 100644 index 0000000..39aea4d --- /dev/null +++ b/src/ActionHandler/SynchronizeCollectionHandler.php @@ -0,0 +1,60 @@ + 'https://commongateway.nl/ActionHandler/SynchronizationCollectionHandler.ActionHandler.json', + '$schema' => 'https://docs.commongateway.nl/schemas/ActionHandler.schema.json', + 'title' => 'SynchronizationCollectionHandler', + 'description' => '', + 'required' => [], + 'properties' => [], + ]; + + }//end getConfiguration() + + + /** + * Run the actual business logic in the appropriate server. + * + * @param array $data The data from the call + * @param array $configuration The configuration of the action + * + * @return array + */ + public function run(array $data, array $configuration): array + { + $configuration = $this->vrijBrpService->setVrijBRPDefaults($configuration); + + return $this->synchronizationService->synchronizeCollectionHandler($data, $configuration); + + }//end run() + + +}//end class diff --git a/src/Service/InstallationService.php b/src/Service/InstallationService.php deleted file mode 100644 index ff32741..0000000 --- a/src/Service/InstallationService.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * @category Service - */ -class InstallationService implements InstallerInterface -{ - - /** - * The entity manager - * - * @var EntityManagerInterface - */ - private EntityManagerInterface $entityManager; - - /** - * The installation logger. - * - * @var LoggerInterface - */ - private LoggerInterface $logger; - - - /** - * The constructor - * - * @param EntityManagerInterface $entityManager The entity manager. - * @param LoggerInterface $installationLogger The installation logger. - */ - public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $installationLogger - ) { - $this->entityManager = $entityManager; - $this->logger = $installationLogger; - - }//end __construct() - - - /** - * Every installation service should implement an installation function - * - * @return void - */ - public function install() - { - $this->logger->debug("VrijBRPToZGWBundle -> Install()", ['plugin' => 'common-gateway/vrijbrp-to-zgw-bundle']); - - $this->checkDataConsistency(); - - }//end install() - - - /** - * Every installation service should implement an update function - * - * @return void - */ - public function update() - { - $this->logger->debug("VrijBRPToZGWBundle -> Update()", ['plugin' => 'common-gateway/vrijbrp-to-zgw-bundle']); - - $this->checkDataConsistency(); - - }//end update() - - - /** - * Every installation service should implement an uninstallation function - * - * @return void - */ - public function uninstall() - { - $this->logger->debug("VrijBRPToZGWBundle -> Uninstall()", ['plugin' => 'common-gateway/vrijbrp-to-zgw-bundle']); - - // Do some cleanup to uninstall correctly... - - }//end uninstall() - - - /** - * The actual code run on update and installation of this bundle - * - * @return void - */ - public function checkDataConsistency() - { - //This is the place where you can add or change Installation data from/for this bundle or other required bundles. - //Note that in most cases it is recommended to use .json files in the Installation folder instead, if possible. - - $this->entityManager->flush(); - - }//end checkDataConsistency() - - -}//end class diff --git a/src/Service/NewSynchronizationService.php b/src/Service/NewSynchronizationService.php new file mode 100644 index 0000000..ae0f021 --- /dev/null +++ b/src/Service/NewSynchronizationService.php @@ -0,0 +1,218 @@ + + * + * @category Service + */ +class NewSynchronizationService +{ + + + public function __construct( + private readonly GatewayResourceService $resourceService, + private readonly CallService $callService, + private readonly SynchronizationService $synchronizationService, + private readonly LoggerInterface $synchronizationLogger, + private readonly EntityManagerInterface $entityManager, + private readonly MappingService $mappingService, + ) { + + }//end __construct() + + + /** + * Executes the synchronization from source to gateway. + * Slightly edited clone of the SynchronizationService in the gateway. + * + * @param Synchronization $synchronization The synchronization to update + * @param array $sourceObject The object in the source + * @param bool $unsafe Unset attributes that are not included in the hydrator array when calling the hydrate function + * + * @throws GuzzleException + * @throws LoaderError + * @throws SyntaxError + * + * @return Synchronization The updated synchronization + */ + public function synchronizeFromSource(Synchronization $synchronization, array $sourceObject=[], bool $unsafe=false): Synchronization + { + $this->synchronizationLogger->info("handleSync for Synchronization with id = {$synchronization->getId()->toString()}"); + + // create new object if no object exists + if (!$synchronization->getObject()) { + isset($this->io) && $this->io->text('creating new objectEntity'); + $this->synchronizationLogger->info('creating new objectEntity'); + $object = new ObjectEntity($synchronization->getEntity()); + $object->addSynchronization($synchronization); + $this->entityManager->persist($object); + $this->entityManager->persist($synchronization); + $oldDateModified = null; + } else { + $oldDateModified = $synchronization->getObject()->getDateModified()->getTimestamp(); + } + + $sourceObject = $sourceObject ?: $this->synchronizationService->getSingleFromSource($synchronization); + + if ($sourceObject === null) { + $this->synchronizationLogger->warning("Can not handle Synchronization with id = {$synchronization->getId()->toString()} if \$sourceObject === null"); + + return $synchronization; + } + + // Let check + $now = new DateTime(); + $synchronization->setLastChecked($now); + + $sha = hash('sha256', json_encode($sourceObject)); + + // Checking if data on source has changed. + if ($synchronization->getSha() === $sha) { + return $synchronization; + } + + // Counter + $counter = ($synchronization->getTryCounter() + 1); + if ($counter > 10000) { + $counter = 10000; + } + + $synchronization->setTryCounter($counter); + + // Set dont try before, expensional so in minutes 1,8,27,64,125,216,343,512,729,1000 + $addMinutes = pow($counter, 3); + if ($synchronization->getDontSyncBefore()) { + $dontTryBefore = $synchronization->getDontSyncBefore()->add(new DateInterval('PT'.$addMinutes.'M')); + } else { + $dontTryBefore = new DateTime(); + } + + $synchronization->setDontSyncBefore($dontTryBefore); + + if ($synchronization->getMapping()) { + $sourceObject = $this->mappingService->mapping($synchronization->getMapping(), $sourceObject); + } + + $synchronization->getObject()->hydrate($sourceObject, $unsafe); + + $synchronization->setSha($sha); + + $this->entityManager->persist($synchronization->getObject()); + $this->entityManager->persist($synchronization); + + if ($oldDateModified !== $synchronization->getObject()->getDateModified()->getTimestamp()) { + $date = new DateTime(); + (isset($this->io) ?? $this->io->text("set new dateLastChanged to {$date->format('d-m-YTH:i:s')}")); + $synchronization->setLastSynced(new DateTime()); + $synchronization->setTryCounter(0); + } else { + (isset($this->io) ?? $this->io->text("lastSynced is still {$synchronization->getObject()->getDateModified()->format('d-m-YTH:i:s')}")); + } + + return $synchronization; + + }//end synchronizeFromSource() + + + /** + * Fetch data from source in a way that is as abstract as possible at this time. + * + * @param array $configuration + * @param Source $source + * @return array + * @throws Exception + */ + public function getResults(array $configuration, Source $source): array + { + $response = $this->callService->call(source: $source, endpoint: $configuration['endpoint'], method: $configuration['method'], config: ['json' => $configuration['body']]); + + $result = $this->callService->decodeResponse(source: $source, response: $response, contentType: ($configuration['content-type'] ?? 'application/json')); + + $resultDot = new Dot($result); + + if ($resultDot->has(keys: $configuration['resultsPath']) === true) { + $return = $resultDot->get(key: $configuration['resultsPath']); + if ($return instanceof Dot) { + return $return->jsonSerialize(); + } else if (is_array($return)) { + return $return; + } + } + + throw new Exception('No cases found'); + + }//end getResults() + + + /** + * This function is designed to in time replace the existing syncCollectionHandler. + * At the moment it depends on the in-gateway SynchronizationService, and is one way with the source as the leading version. + * + * @param array $data + * @param array $configuration + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function synchronizeCollectionHandler(array $data, array $configuration): array + { + $source = $this->resourceService->getSource(reference: $configuration['source'], pluginName: "common-gateway/vrijbrp-to-zgw-bundle"); + $schema = $this->resourceService->getSchema(reference: $configuration['schema'], pluginName: "common-gateway/vrijbrp-to-zgw-bundle"); + + if (isset($configuration['mapping']) === true) { + $mapping = $this->resourceService->getMapping(reference: $configuration['mapping'], pluginName: "common-gateway/vrijbrp-to-zgw-bundle"); + } + + try { + $dossiers = $this->getResults(configuration: $configuration, source: $source); + } catch (Exception $exception) { + $this->synchronizationLogger->warning(message: $exception->getMessage(), context: ['plugin' => 'common-gateway/vrijbrp-to-zgw-bundle']); + return $data; + } + + foreach ($dossiers as $dossier) { + $dossierDot = new Dot($dossier); + + $synchronization = $this->synchronizationService->findSyncBySource(source: $source, entity: $schema, sourceId: $dossierDot[$configuration['idField']], endpoint: $configuration['endpoint']); + + if ($synchronization->getMapping() === null && isset($mapping) === true) { + $synchronization->setMapping($mapping); + } + + try { + $this->synchronizeFromSource(synchronization: $synchronization, sourceObject: $dossier); + } catch (Exception $exception) { + $this->synchronizationLogger->error(message: $exception->getMessage(), context: ['plugin' => 'common-gateway/vrijbrp-to-zgw-bundle']); + } + } + + return $data; + + }//end synchronizeCollectionHandler() + + +}//end class diff --git a/src/Service/VrijBrpService.php b/src/Service/VrijBrpService.php new file mode 100644 index 0000000..14e6d51 --- /dev/null +++ b/src/Service/VrijBrpService.php @@ -0,0 +1,33 @@ + [ + 'intra_mun_relocation', + 'inter_mun_relocation', + ], + ]; + } + + return $configuration; + + }//end setVrijBRPDefaults() + + +}//end class