From ed6a94ddcb7e32257e430d28978451eaae284ba9 Mon Sep 17 00:00:00 2001 From: Barry Brands Date: Tue, 10 Dec 2024 17:14:01 +0100 Subject: [PATCH 1/3] Added sourceHashMapping column --- lib/Db/Synchronization.php | 3 + lib/Migration/Version1Date20241210100000.php | 62 ++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 lib/Migration/Version1Date20241210100000.php diff --git a/lib/Db/Synchronization.php b/lib/Db/Synchronization.php index 473524b..3e4ee1f 100644 --- a/lib/Db/Synchronization.php +++ b/lib/Db/Synchronization.php @@ -16,6 +16,7 @@ class Synchronization extends Entity implements JsonSerializable protected ?string $sourceId = null; // The id of the source object protected ?string $sourceType = null; // The type of the source object (e.g. api, database, register/schema.) protected ?string $sourceHash = null; // The hash of the source object when it was last synced. + protected ?string $sourceHashMapping = null; // The mapping id of the mapping that we map the object to for hashing. protected ?string $sourceTargetMapping = null; // The mapping of the source object to the target object protected ?array $sourceConfig = null; // The configuration of the object in the source protected ?DateTime $sourceLastChanged = null; // The last changed date of the source object @@ -46,6 +47,7 @@ public function __construct() { $this->addType('sourceId', 'string'); $this->addType('sourceType', 'string'); $this->addType('sourceHash', 'string'); + $this->addType('sourceHashMapping', 'string'); $this->addType('sourceTargetMapping', 'string'); $this->addType('sourceConfig', 'json'); $this->addType('sourceLastChanged', 'datetime'); @@ -121,6 +123,7 @@ public function jsonSerialize(): array 'sourceId' => $this->sourceId, 'sourceType' => $this->sourceType, 'sourceHash' => $this->sourceHash, + 'sourceHashMapping' => $this->sourceHashMapping, 'sourceTargetMapping' => $this->sourceTargetMapping, 'sourceConfig' => $this->sourceConfig, 'sourceLastChanged' => isset($this->sourceLastChanged) === true ? $this->sourceLastChanged->format('c') : null, diff --git a/lib/Migration/Version1Date20241210100000.php b/lib/Migration/Version1Date20241210100000.php new file mode 100644 index 0000000..9d128f5 --- /dev/null +++ b/lib/Migration/Version1Date20241210100000.php @@ -0,0 +1,62 @@ +hasTable('openconnector_synchronizations') === true) { + $table = $schema->getTable('openconnector_synchronizations'); + + if ($table->hasColumn('sourceHashMapping') === false) { + $table->dropColumn('sourceHashMapping'); + $table->addColumn('source_hash_mapping', Types::STRING)->setNotnull(false); + } + } + + return $schema; + } + + /** + * @param IOutput $output + * @param Closure(): ISchemaWrapper $schemaClosure + * @param array $options + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { + } +} From cbef4f241f674d9a79d6f0d9c87eaa851bf43813 Mon Sep 17 00:00:00 2001 From: Barry Brands Date: Tue, 10 Dec 2024 17:15:52 +0100 Subject: [PATCH 2/3] Cleanup and added hash mapping functionality --- lib/Service/SynchronizationService.php | 69 +++++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/lib/Service/SynchronizationService.php b/lib/Service/SynchronizationService.php index 3f51acc..90771b9 100644 --- a/lib/Service/SynchronizationService.php +++ b/lib/Service/SynchronizationService.php @@ -294,6 +294,57 @@ private function fetchExtraDataForObject(Synchronization $synchronization, array return $extraData; } + /** + * Fetches multiple extra data entries for an object based on the source configuration. + * + * This method iterates through a list of extra data configurations, fetches the additional data for each configuration, + * and merges it with the original object. + * + * @param Synchronization $synchronization The synchronization instance containing configuration details. + * @param array $sourceConfig The source configuration containing extra data retrieval settings. + * @param array $object The original object for which extra data needs to be fetched. + * + * @return array The updated object with all fetched extra data merged into it. + */ + private function fetchMultipleExtraData(Synchronization $synchronization, array $sourceConfig, array $object): array + { + if (isset($sourceConfig[$this::EXTRA_DATA_CONFIGS_LOCATION]) === true) { + foreach ($sourceConfig[$this::EXTRA_DATA_CONFIGS_LOCATION] as $extraDataConfig) { + $object = array_merge($object, $this->fetchExtraDataForObject($synchronization, $extraDataConfig, $object)); + } + } + + return $object; + } + + /** + * Maps a given object using a source hash mapping configuration. + * + * This function retrieves a hash mapping configuration for a synchronization instance, if available, + * and applies it to the input object using the mapping service. + * + * @param Synchronization $synchronization The synchronization instance containing the hash mapping configuration. + * @param array $object The input object to be mapped. + * + * @return array The mapped object, or the original object if no mapping is found. + */ + private function mapHashObject(Synchronization $synchronization, array $object): array + { + if (empty($synchronization->getSourceHashMapping()) === false) { + try { + $sourceHashMapping = $this->mappingMapper->find(id: $synchronization->getSourceHashMapping()); + } catch (DoesNotExistException $exception) { + return new Exception($exception->getMessage()); + } + + // Execute mapping if found + if ($sourceHashMapping) { + return $this->mappingService->executeMapping(mapping: $sourceHashMapping, input: $object); + } + } + + return $object; + } /** * Synchronize a contract @@ -314,27 +365,19 @@ public function synchronizeContract(SynchronizationContract $synchronizationCont $sourceConfig = $this->callService->applyConfigDot($synchronization->getSourceConfig()); // Check if extra data needs to be fetched - if (isset($sourceConfig[$this::EXTRA_DATA_CONFIGS_LOCATION]) === true) { - foreach ($sourceConfig[$this::EXTRA_DATA_CONFIGS_LOCATION] as $extraDataConfig) { - $object = array_merge($object, $this->fetchExtraDataForObject($synchronization, $extraDataConfig, $object)); - } - } - - // @TODO: This should be unset through pre-mapping - if(isset($object['d']['vti_x005f_dirlateststamp']) === true) { - unset($object['d']['vti_x005f_dirlateststamp']); - } - + $object = $this->fetchMultipleExtraData(synchronization: $synchronization, sourceConfig: $sourceConfig, object: $object); + // Get mapped hash object (some fields can make it look the object has changed even if it hasn't) + $hashObject = $this->mapHashObject(synchronization: $synchronization, object: $object); // Let create a source hash for the object - $originHash = md5(serialize($object)); + $originHash = md5(serialize($hashObject)); // Let's prevent pointless updates @todo account for omnidirectional sync, unless the config has been updated since last check then we do want to rebuild and check if the tagert object has changed if ($originHash === $synchronizationContract->getOriginHash() && $synchronization->getUpdated() < $synchronizationContract->getSourceLastChecked()) { // The object has not changed and the config has not been updated since last check return $synchronizationContract; } - + // The object has changed, oke let do mappig and bla die bla $synchronizationContract->setOriginHash($originHash); $synchronizationContract->setSourceLastChanged(new DateTime()); From e6742bce5415a37927919c59e97bec2a10d32bfb Mon Sep 17 00:00:00 2001 From: Barry Brands Date: Tue, 10 Dec 2024 17:16:13 +0100 Subject: [PATCH 3/3] Added sourceHashMapping in frontend --- .../synchronization/synchronization.mock.ts | 2 ++ .../synchronization/synchronization.ts | 3 +++ .../synchronization/synchronization.types.ts | 1 + .../Synchronization/EditSynchronization.vue | 19 +++++++++++++++++-- .../SynchronizationDetails.vue | 4 ++++ 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/entities/synchronization/synchronization.mock.ts b/src/entities/synchronization/synchronization.mock.ts index 8a885fb..5e4b02b 100644 --- a/src/entities/synchronization/synchronization.mock.ts +++ b/src/entities/synchronization/synchronization.mock.ts @@ -9,6 +9,7 @@ export const mockSynchronizationData = (): TSynchronization[] => [ sourceId: 'source1', sourceType: 'api', sourceHash: 'source1', + sourceHashMapping: '1', sourceTargetMapping: 'source1', sourceConfig: {}, sourceLastChanged: '2023-05-01T12:00:00Z', @@ -32,6 +33,7 @@ export const mockSynchronizationData = (): TSynchronization[] => [ sourceId: 'source2', sourceType: 'api', sourceHash: 'source2', + sourceHashMapping: '1', sourceTargetMapping: 'source2', sourceConfig: {}, sourceLastChanged: '2023-05-02T12:00:00Z', diff --git a/src/entities/synchronization/synchronization.ts b/src/entities/synchronization/synchronization.ts index c9bac36..b055e22 100644 --- a/src/entities/synchronization/synchronization.ts +++ b/src/entities/synchronization/synchronization.ts @@ -11,6 +11,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati public sourceId: string public sourceType: string public sourceHash: string + public sourceHashMapping: string public sourceTargetMapping: string public sourceConfig: Record public sourceLastChanged: string @@ -35,6 +36,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati sourceId: synchronization.sourceId || '', sourceType: synchronization.sourceType || '', sourceHash: synchronization.sourceHash || '', + sourceHashMapping: synchronization.sourceHashMapping || '', sourceTargetMapping: synchronization.sourceTargetMapping || '', sourceConfig: synchronization.sourceConfig || {}, sourceLastChanged: synchronization.sourceLastChanged || '', @@ -63,6 +65,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati sourceId: z.string(), sourceType: z.string(), sourceHash: z.string(), + sourceHashMapping: z.string(), sourceTargetMapping: z.string(), sourceConfig: z.record(z.string(), z.string()), sourceLastChanged: z.string(), diff --git a/src/entities/synchronization/synchronization.types.ts b/src/entities/synchronization/synchronization.types.ts index 7dcf47a..8542024 100644 --- a/src/entities/synchronization/synchronization.types.ts +++ b/src/entities/synchronization/synchronization.types.ts @@ -5,6 +5,7 @@ export type TSynchronization = { sourceId: string sourceType: string sourceHash: string + sourceHashMapping: string sourceTargetMapping: string sourceConfig: Record sourceLastChanged: string diff --git a/src/modals/Synchronization/EditSynchronization.vue b/src/modals/Synchronization/EditSynchronization.vue index 1a76f1b..d616705 100644 --- a/src/modals/Synchronization/EditSynchronization.vue +++ b/src/modals/Synchronization/EditSynchronization.vue @@ -90,10 +90,15 @@ import { synchronizationStore, navigationStore, sourceStore, mappingStore } from :loading="sourcesLoading" input-label="Source ID" /> + + + input-label="Source target mapping" /> @@ -135,7 +140,7 @@ import { synchronizationStore, navigationStore, sourceStore, mappingStore } from + input-label="Target source mapping" /> { const activeSourceMapping = entities.find(mapping => mapping.id.toString() === this.synchronizationItem.sourceTargetMapping.toString()) const activeTargetMapping = entities.find(mapping => mapping.id.toString() === this.synchronizationItem.targetSourceMapping.toString()) + const sourceHashMapping = entities.find(mapping => mapping.id.toString() === this.synchronizationItem.sourceHashMapping.toString()) this.sourceTargetMappingOptions = { options: entities.map(mapping => ({ label: mapping.name, id: mapping.id, })), + hashValue: sourceHashMapping + ? { + label: sourceHashMapping.name, + id: sourceHashMapping.id, + } + : null, sourceValue: activeSourceMapping ? { label: activeSourceMapping.name, @@ -528,6 +542,7 @@ export default { ...this.synchronizationItem, sourceId: this.sourceOptions.sourceValue?.id || null, sourceType: this.typeOptions.value?.id || null, + sourceHashMapping: this.sourceTargetMappingOptions.hashValue?.id || null, sourceTargetMapping: this.sourceTargetMappingOptions.sourceValue?.id || null, targetType: this.targetTypeOptions.value?.id || null, targetId: targetId || null, diff --git a/src/views/Synchronization/SynchronizationDetails.vue b/src/views/Synchronization/SynchronizationDetails.vue index 34af78b..edb37a9 100644 --- a/src/views/Synchronization/SynchronizationDetails.vue +++ b/src/views/Synchronization/SynchronizationDetails.vue @@ -88,6 +88,10 @@ import { synchronizationStore, navigationStore, logStore } from '../../store/sto Source Hash:

{{ synchronizationStore.synchronizationItem.sourceHash || 'N/A' }}

+
+ Source Hash mapping id: +

{{ synchronizationStore.synchronizationItem.sourceHashMapping || 'N/A' }}

+
Source Last Changed:

{{ synchronizationStore.synchronizationItem.sourceLastChanged || 'N/A' }}