diff --git a/configurations/sharepoint-woo/mappings/xxllnc-suite-to-publications.json b/configurations/sharepoint-woo/mappings/xxllnc-suite-to-publications.json
index 744823b1..57ab360a 100644
--- a/configurations/sharepoint-woo/mappings/xxllnc-suite-to-publications.json
+++ b/configurations/sharepoint-woo/mappings/xxllnc-suite-to-publications.json
@@ -5,7 +5,7 @@
"title": "omschrijving",
"summary": "zaaktypeomschrijving",
"description": "zaaktypeomschrijving",
- "category": "{% if zaaktypecode|default %}{% set wooVerzoekenEnBesluiten = ['LP00000431', 'B1873', 'cherry'] %}{% set klachtoordelen = ['LP00000091', 'LP00001132', 'LP00000121', 'B0757', 'LP00000832', 'LP00001096'] %}{% if zaaktypecode in wooVerzoekenEnBesluiten %}{{ 'Woo-verzoeken en -besluiten' }}{% elseif zaaktypecode in klachtoordelen %}{{ 'Klachtoordelen' }}{% endif %}{% endif %}",
+ "category": "{% if zaaktypecode|default %}{% set wooVerzoekenEnBesluiten = ['LP00000431', 'B1873'] %}{% set klachtoordelen = ['LP00000091', 'LP00001132', 'LP00000121', 'B0757', 'LP00000832', 'LP00001096'] %}{% if zaaktypecode in wooVerzoekenEnBesluiten %}{{ 'Woo-verzoeken en -besluiten' }}{% elseif zaaktypecode in klachtoordelen %}{{ 'Klachtoordelen' }}{% endif %}{% endif %}",
"published": "startdatum",
"modified": "{{ 'now'|date('H:i:sTm-d-Y') }}",
"attachments": "[{% if files|default %}{% for file in files %} { {% if file['titel']|default %}\"title\": \"{{ file['titel'] }}\",{% endif %}\"labels\": [\"{{ 'Informatieverzoek' }}\"],{% if file['formaat']|default %}\"extension\": \"{{ file['formaat']|split('/')|last }}\",\"type\": \"{{ file['formaat'] }}\",{% endif %}{% if file['inhoud']|default and file['formaat']|default %}\"accessUrl\": \"data:{{ file['formaat'] }};base64,{{ file.inhoud }}\"{% endif %} }{{ loop.last ? '' : ',' }} {% endfor %}{% endif %}]",
diff --git a/configurations/sharepoint-woo/synchronizations/xxllnc-suite-to-publications.json b/configurations/sharepoint-woo/synchronizations/xxllnc-suite-to-publications.json
index 91922ef1..d5672fd6 100644
--- a/configurations/sharepoint-woo/synchronizations/xxllnc-suite-to-publications.json
+++ b/configurations/sharepoint-woo/synchronizations/xxllnc-suite-to-publications.json
@@ -5,6 +5,7 @@
"sourceId": "1",
"sourceType": "api",
"sourceHash": "",
+ "sourceHashMapping": "1",
"sourceTargetMapping": "1",
"sourceConfig": {
"idPosition": "identificatie",
@@ -13,6 +14,7 @@
"headers": [],
"query.startdatum__gte": "2024-08-01",
"query.einddatum__lt": "2025-01-01",
+ "query.maximaleVertrouwelijkheidaanduiding": "openbaar",
"usesPagination": "false",
"extraDataConfigs.0.staticEndpoint": "/tlb/zaaksysteem/api/v1/zaken/{{ originId }}/informatieobjecten",
"extraDataConfigs.0.mergeExtraData": "true",
@@ -21,5 +23,17 @@
"extraDataConfigs.0.extraDataConfigPerResult.staticEndpoint": "/tlb/zaaksysteem/api/v1/informatieobjecten/{{ originId }}"
},
"targetId": "1/1",
- "targetType": "register/schema"
+ "targetType": "register/schema",
+ "conditions": {
+ "if": [
+ {
+ "or": [
+ { "in": [{ "var": "zaaktypecode" }, ["LP00000431", "B1873"]] },
+ { "in": [{ "var": "zaaktypecode" }, ["LP00000091", "LP00001132", "LP00000121", "B0757", "LP00000832", "LP00001096"]] }
+ ]
+ },
+ true,
+ false
+ ]
+ }
}
\ No newline at end of file
diff --git a/lib/Db/SynchronizationContractMapper.php b/lib/Db/SynchronizationContractMapper.php
index 4549d7fc..5215f463 100644
--- a/lib/Db/SynchronizationContractMapper.php
+++ b/lib/Db/SynchronizationContractMapper.php
@@ -117,6 +117,32 @@ public function findOnTarget(string $synchronization, string $targetId): Synchro
}
}
+ /**
+ * Find all target IDs of synchronization contracts by synchronization ID
+ *
+ * @param string $synchronization The synchronization ID
+ *
+ * @return array An array of target IDs or an empty array if none found
+ */
+ public function findAllBySynchronization(string $synchronizationId): array
+ {
+ // Create query builder
+ $qb = $this->db->getQueryBuilder();
+
+ // Build select query with synchronization ID filter
+ $qb->select('*')
+ ->from('openconnector_synchronization_contracts')
+ ->where(
+ $qb->expr()->eq('synchronization_id', $qb->createNamedParameter($synchronizationId))
+ );
+
+ try {
+ return $this->findEntities($qb);
+ } catch (\Exception $e) {
+ return [];
+ }
+ }
+
/**
* Find all synchronization contracts with optional filtering and pagination
*
@@ -201,6 +227,34 @@ public function updateFromArray(int $id, array $object): SynchronizationContract
return $this->update($obj);
}
+ /**
+ * Find a synchronization contract by origin ID.
+ *
+ * @param string $originId The origin ID to search for.
+ *
+ * @return SynchronizationContract|null The matching contract or null if not found.
+ */
+ public function findByOriginId(string $originId): ?SynchronizationContract
+ {
+ // Create query builder
+ $qb = $this->db->getQueryBuilder();
+
+ // Build query to find contract matching origin_id
+ $qb->select('*')
+ ->from('openconnector_synchronization_contracts')
+ ->where(
+ $qb->expr()->eq('origin_id', $qb->createNamedParameter($originId))
+ )
+ ->setMaxResults(1); // Ensure only one result is returned
+
+ try {
+ return $this->findEntity($qb); // Use findEntity to return a single result
+ } catch (\OCP\AppFramework\Db\DoesNotExistException $e) {
+ return null; // Return null if no match is found
+ }
+ }
+
+
/**
* Find synchronization contracts by type and ID
*
diff --git a/lib/Service/SynchronizationService.php b/lib/Service/SynchronizationService.php
index 3f51accd..c5500511 100644
--- a/lib/Service/SynchronizationService.php
+++ b/lib/Service/SynchronizationService.php
@@ -97,7 +97,18 @@ public function synchronize(Synchronization $synchronization, ?bool $isTest = fa
$objectList = $this->getAllObjectsFromSource(synchronization: $synchronization, isTest: $isTest);
+ $synchronizedTargetIds = [];
foreach ($objectList as $key => $object) {
+
+ // Check if object adheres to conditions.
+ // Take note, JsonLogic::apply() returns a range of return types, so checking it with '=== false' or '!== true' does not work properly.
+ if ($synchronization->getConditions() !== [] && !JsonLogic::apply($synchronization->getConditions(), $object)) {
+
+ // @todo log that this object is not valid
+ unset($objectList[$key]);
+ continue;
+ }
+
// If the source configuration contains a dot notation for the id position, we need to extract the id from the source object
$originId = $this->getOriginId($synchronization, $object);
@@ -138,9 +149,15 @@ public function synchronize(Synchronization $synchronization, ?bool $isTest = fa
}
}
- $this->synchronizationContractMapper->update($synchronizationContract);
+ $synchronizationContract = $this->synchronizationContractMapper->update($synchronizationContract);
+
+ $synchronizedTargetIds[] = $synchronizationContract->getTargetId();
}
+ $this->deleteInvalidObjects(synchronization: $synchronization, synchronizedTargetIds: $synchronizedTargetIds);
+ // @todo log deleted objects count
+
+
foreach ($synchronization->getFollowUps() as $followUp) {
$followUpSynchronization = $this->synchronizationMapper->find($followUp);
$this->synchronize($followUpSynchronization, $isTest);
@@ -294,14 +311,64 @@ private function fetchExtraDataForObject(Synchronization $synchronization, array
return $extraData;
}
+ /**
+ * Deletes invalid objects associated with a synchronization.
+ *
+ * This function identifies and removes objects that are no longer valid or do not exist
+ * in the source data for a given synchronization. It compares the target IDs from the
+ * synchronization contract with the synchronized target IDs and deletes the unmatched ones.
+ *
+ * @param Synchronization $synchronization The synchronization entity to process.
+ * @param array $synchronizedTargetIds An array of target IDs that are still valid in the source.
+ *
+ * @return int The count of objects that were deleted.
+ *
+ * @throws Exception If any database or object deletion errors occur during execution.
+ */
+ public function deleteInvalidObjects(Synchronization $synchronization, array $synchronizedTargetIds): int
+ {
+ $deletedObjectsCount = 0;
+ $type = $synchronization->getTargetType();
+
+ switch ($type) {
+ case 'register/schema':
+
+ $targetIdsToDelete = [];
+ $allContracts = $this->synchronizationContractMapper->findAllBySynchronization($synchronization->getId());
+ $allContractTargetIds = [];
+ foreach ($allContracts as $contract) {
+ if ($contract->getTargetId() !== null) {
+ $allContractTargetIds[] = $contract->getTargetId();
+ }
+ }
+
+ // Check if we have contracts that became invalid or do not exist in the source anymore
+ $targetIdsToDelete = array_diff($allContractTargetIds, $synchronizedTargetIds);
+
+ foreach ($targetIdsToDelete as $targetIdToDelete) {
+ try {
+ $synchronizationContract = $this->synchronizationContractMapper->findOnTarget(synchronization: $synchronization->getId(), targetId: $targetIdToDelete);
+ $synchronizationContract = $this->updateTarget(synchronizationContract: $synchronizationContract, targetObject: [], action: 'delete');
+ $this->synchronizationContractMapper->update($synchronizationContract);
+ $deletedObjectsCount++;
+ } catch (DoesNotExistException $exception) {
+ // @todo log
+ }
+ }
+ break;
+ }
+
+ return $deletedObjectsCount;
+
+ }
/**
* Synchronize a contract
*
* @param SynchronizationContract $synchronizationContract
- * @param Synchronization|null $synchronization
- * @param array $object
- * @param bool|null $isTest False by default, currently added for synchronization-test endpoint
+ * @param Synchronization|null $synchronization
+ * @param array $object
+ * @param bool|null $isTest False by default, currently added for synchronization-test endpoint
*
* @return SynchronizationContract|Exception|array
* @throws ContainerExceptionInterface
@@ -334,18 +401,12 @@ public function synchronizeContract(SynchronizationContract $synchronizationCont
// 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());
$synchronizationContract->setSourceLastChecked(new DateTime());
- // Check if object adheres to conditions.
- // Take note, JsonLogic::apply() returns a range of return types, so checking it with '=== false' or '!== true' does not work properly.
- if ($synchronization->getConditions() !== [] && !JsonLogic::apply($synchronization->getConditions(), $object)) {
- return $synchronizationContract;
- }
-
// If no source target mapping is defined, use original object
if (empty($synchronization->getSourceTargetMapping()) === true) {
$targetObject = $object;
@@ -402,17 +463,65 @@ public function synchronizeContract(SynchronizationContract $synchronizationCont
}
+ /**
+ * Updates or deletes a target object in the Open Register system.
+ *
+ * This method updates a target object associated with a synchronization contract
+ * or deletes it based on the specified action. It extracts the register and schema
+ * from the target ID and performs the corresponding operation using the object service.
+ *
+ * @param SynchronizationContract $synchronizationContract The synchronization contract being updated.
+ * @param Synchronization $synchronization The synchronization entity containing the target ID.
+ * @param array|null $targetObject An optional array containing the data for the target object. Defaults to an empty array.
+ * @param string|null $action The action to perform: 'save' (default) to update or 'delete' to remove the target object.
+ *
+ * @return SynchronizationContract The updated synchronization contract with the modified target ID.
+ *
+ * @throws \Exception If an error occurs while interacting with the object service or processing the data.
+ */
+ private function updateTargetOpenRegister(SynchronizationContract $synchronizationContract, Synchronization $synchronization, ?array $targetObject = [], ?string $action = 'save'): SynchronizationContract
+ {
+ // Setup the object service
+ $objectService = $this->containerInterface->get('OCA\OpenRegister\Service\ObjectService');
+
+ // if we already have an id, we need to get the object and update it
+ if ($synchronizationContract->getTargetId() !== null) {
+ $targetObject['id'] = $synchronizationContract->getTargetId();
+ }
+
+ // Extract register and schema from the targetId
+ // The targetId needs to be filled in as: {registerId} + / + {schemaId} for example: 1/1
+ $targetId = $synchronization->getTargetId();
+ list($register, $schema) = explode('/', $targetId);
+
+ // Save the object to the target
+ switch ($action) {
+ case 'save':
+ $target = $objectService->saveObject($register, $schema, $targetObject);
+ // Get the id form the target object
+ $synchronizationContract->setTargetId($target->getUuid());
+ break;
+ case 'delete':
+ $objectService->deleteObject(register: $register, schema: $schema, uuid: $synchronizationContract->getTargetId());
+ $synchronizationContract->setTargetId(null);
+ break;
+ }
+
+ return $synchronizationContract;
+ }
+
/**
* Write the data to the target
*
* @param SynchronizationContract $synchronizationContract
- * @param array $targetObject
+ * @param array $targetObject
+ * @param string|null $action Determines what needs to be done with the target object, defaults to 'save'
*
* @return SynchronizationContract
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
- public function updateTarget(SynchronizationContract $synchronizationContract, array $targetObject): SynchronizationContract
+ public function updateTarget(SynchronizationContract $synchronizationContract, ?array $targetObject = [], ?string $action = 'save'): SynchronizationContract
{
// The function can be called solo set let's make sure we have the full synchronization object
if (isset($synchronization) === false) {
@@ -429,24 +538,7 @@ public function updateTarget(SynchronizationContract $synchronizationContract, a
switch ($type) {
case 'register/schema':
- // Setup the object service
- $objectService = $this->containerInterface->get('OCA\OpenRegister\Service\ObjectService');
-
- // if we already have an id, we need to get the object and update it
- if ($synchronizationContract->getTargetId() !== null) {
- $targetObject['id'] = $synchronizationContract->getTargetId();
- }
-
- // Extract register and schema from the targetId
- // The targetId needs to be filled in as: {registerId} + / + {schemaId} for example: 1/1
- $targetId = $synchronization->getTargetId();
- list($register, $schema) = explode('/', $targetId);
-
- // Save the object to the target
- $target = $objectService->saveObject($register, $schema, $targetObject);
-
- // Get the id form the target object
- $synchronizationContract->setTargetId($target->getUuid());
+ $synchronizationContract = $this->updateTargetOpenRegister(synchronizationContract: $synchronizationContract, synchronization: $synchronization, targetObject: $targetObject, action: $action);
break;
case 'api':
//@todo: implement
diff --git a/src/entities/synchronization/synchronization.mock.ts b/src/entities/synchronization/synchronization.mock.ts
index 8a885fbf..711def27 100644
--- a/src/entities/synchronization/synchronization.mock.ts
+++ b/src/entities/synchronization/synchronization.mock.ts
@@ -6,6 +6,7 @@ export const mockSynchronizationData = (): TSynchronization[] => [
id: 1,
name: 'Synchronization 1',
description: 'Synchronization 1',
+ conditions: '{"!!": { "var": "valid" }}',
sourceId: 'source1',
sourceType: 'api',
sourceHash: 'source1',
@@ -29,6 +30,7 @@ export const mockSynchronizationData = (): TSynchronization[] => [
id: 2,
name: 'Synchronization 2',
description: 'Synchronization 2',
+ conditions: '{"!!": { "var": "valid" }}',
sourceId: 'source2',
sourceType: 'api',
sourceHash: 'source2',
diff --git a/src/entities/synchronization/synchronization.ts b/src/entities/synchronization/synchronization.ts
index c9bac364..ac0fb2ff 100644
--- a/src/entities/synchronization/synchronization.ts
+++ b/src/entities/synchronization/synchronization.ts
@@ -8,6 +8,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati
public id: number
public name: string
public description: string
+ public conditions: string
public sourceId: string
public sourceType: string
public sourceHash: string
@@ -32,6 +33,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati
id: synchronization.id || null,
name: synchronization.name || '',
description: synchronization.description || '',
+ conditions: synchronization.conditions || '',
sourceId: synchronization.sourceId || '',
sourceType: synchronization.sourceType || '',
sourceHash: synchronization.sourceHash || '',
@@ -60,6 +62,7 @@ export class Synchronization extends ReadonlyBaseClass implements TSynchronizati
id: z.number().nullable(),
name: z.string(),
description: z.string(),
+ conditions: z.string(),
sourceId: z.string(),
sourceType: z.string(),
sourceHash: z.string(),
diff --git a/src/entities/synchronization/synchronization.types.ts b/src/entities/synchronization/synchronization.types.ts
index 7dcf47a1..97cb7453 100644
--- a/src/entities/synchronization/synchronization.types.ts
+++ b/src/entities/synchronization/synchronization.types.ts
@@ -2,6 +2,7 @@ export type TSynchronization = {
id: number
name: string
description: string
+ conditions: string
sourceId: string
sourceType: string
sourceHash: string
diff --git a/src/modals/Synchronization/EditSynchronization.vue b/src/modals/Synchronization/EditSynchronization.vue
index 1a76f1b2..ccef7ebe 100644
--- a/src/modals/Synchronization/EditSynchronization.vue
+++ b/src/modals/Synchronization/EditSynchronization.vue
@@ -81,6 +81,9 @@ import { synchronizationStore, navigationStore, sourceStore, mappingStore } from
+
+
@@ -199,6 +202,7 @@ export default {
synchronizationItem: { // Initialize with empty fields
name: '',
description: '',
+ conditions: '',
sourceId: '',
sourceType: '',
sourceConfig: {
@@ -274,7 +278,10 @@ export default {
mounted() {
if (this.IS_EDIT) {
// If there is a synchronization item in the store, use it
- this.synchronizationItem = { ...synchronizationStore.synchronizationItem }
+ this.synchronizationItem = {
+ ...synchronizationStore.synchronizationItem,
+ conditions: JSON.stringify(synchronizationStore.synchronizationItem.conditions),
+ }
// update targetTypeOptions with the synchronization item target type
this.targetTypeOptions.value = this.targetTypeOptions.options.find(option => option.id === this.synchronizationItem.targetType)
@@ -529,6 +536,7 @@ export default {
sourceId: this.sourceOptions.sourceValue?.id || null,
sourceType: this.typeOptions.value?.id || null,
sourceTargetMapping: this.sourceTargetMappingOptions.sourceValue?.id || null,
+ conditions: this.synchronizationItem.conditions ? JSON.parse(this.synchronizationItem.conditions) : [],
targetType: this.targetTypeOptions.value?.id || null,
targetId: targetId || null,
targetSourceMapping: this.sourceTargetMappingOptions.targetValue?.id || null,
diff --git a/src/views/Mapping/MappingDetails.vue b/src/views/Mapping/MappingDetails.vue
index 72257fb3..dc4c7717 100644
--- a/src/views/Mapping/MappingDetails.vue
+++ b/src/views/Mapping/MappingDetails.vue
@@ -160,9 +160,6 @@ import { mappingStore, navigationStore } from '../../store/store.js'
disable-menu
:size="44" />
-
- {{ value }}
-