From 17126146892885c4a6fa87da15967d8541318fa7 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 3 Nov 2026 18:04:47 +0100 Subject: [PATCH 1/2] refactor(listener): split the three object event handlers handleObjectCreated (92 lines), handleObjectUpdated (203) and handleObjectDeleted (137) took the event plus three services. They now take the event and one services array, read the object facts once (objectContext), look the catalog schemas up lazily in the same order (firstMatchingSchema), and hand each matched type to a small method. The contact person and contactgegevens update and deletion branches share runContactAction with their log lines in a table. Every call and log line keeps its order and text. Golden: 37 cases over handle() for created, updated and deleted events of every catalog type, with sync, refetch and contact failures, recorded on the old code. Assisted-by: ClaudeCode:claude-opus-5-5 --- lib/EventListener/StackiqEventListener.php | 846 ++--- .../StackiqEventListenerGoldenTest.php | 350 ++ .../golden/stackiqEventListener.json | 3106 +++++++++++++++++ 3 files changed, 3824 insertions(+), 478 deletions(-) create mode 100644 tests/Unit/EventListener/StackiqEventListenerGoldenTest.php create mode 100644 tests/Unit/EventListener/golden/stackiqEventListener.json diff --git a/lib/EventListener/StackiqEventListener.php b/lib/EventListener/StackiqEventListener.php index 9bdceaf3b..68c952662 100644 --- a/lib/EventListener/StackiqEventListener.php +++ b/lib/EventListener/StackiqEventListener.php @@ -54,6 +54,37 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StackiqEventListener implements IEventListener { + /** + * The log lines for a contact person (or contactgegevens) update or deletion: + * matched, done and failed. + */ + private const CONTACT_MESSAGES = [ + 'update' => [ + 'contactPerson' => [ + 'Stackiq: Matched contactpersoon schema - processing update', + 'Stackiq: Successfully processed contactpersoon update', + 'Stackiq: Failed to process contactpersoon update', + ], + 'contactgegevens' => [ + 'Stackiq: Matched contactgegevens schema - processing update (backward compatibility)', + 'Stackiq: Successfully processed contactgegevens update (as contactpersoon)', + 'Stackiq: Failed to process contactgegevens update', + ], + ], + 'deletion' => [ + 'contactPerson' => [ + 'Stackiq: Matched contactpersoon schema - processing deletion', + 'Stackiq: Successfully processed contactpersoon deletion', + 'Stackiq: Failed to process contactpersoon deletion', + ], + 'contactgegevens' => [ + 'Stackiq: Matched contactgegevens schema - processing deletion (backward compatibility)', + 'Stackiq: Successfully processed contactgegevens deletion', + 'Stackiq: Failed to process contactgegevens deletion', + ], + ], + ]; + /** * Constructor for StackiqEventListener * @@ -92,9 +123,7 @@ public function handle(Event $event): void { $this->dispatchEvent( event: $event, - contactSvc: $contactSvc, - settingsService: $settingsService, - logger: $logger + services: ['contactSvc' => $contactSvc, 'settings' => $settingsService, 'logger' => $logger] ); } catch (\Exception $e) { try { @@ -118,51 +147,26 @@ public function handle(Event $event): void { /** * Dispatches the supplied event to the matching per-lifecycle handler. * - * Extracted from {@see handle()} as part of the task 6.1 decomposition so the - * outer method retains only the try/catch envelope and the logging shell. - * * @param Event $event The event to dispatch. - * @param ContactpersoonService $contactSvc Contact-person service handle. - * @param SettingsService $settingsService Settings service handle. - * @param LoggerInterface $logger Logger handle. + * @param array $services The contactSvc, settings and logger services. * * @return void * * @spec openspec/changes/method-decomposition/tasks.md#task-6 */ - private function dispatchEvent( - Event $event, - ContactpersoonService $contactSvc, - SettingsService $settingsService, - LoggerInterface $logger, - ): void { + private function dispatchEvent(Event $event, array $services): void { if ($event instanceof ObjectCreatedEvent) { - $this->handleObjectCreated( - event: $event, - contactSvc: $contactSvc, - settingsService: $settingsService, - logger: $logger - ); + $this->handleObjectCreated(event: $event, services: $services); return; } if ($event instanceof ObjectUpdatedEvent) { - $this->handleObjectUpdated( - event: $event, - contactSvc: $contactSvc, - settingsService: $settingsService, - logger: $logger - ); + $this->handleObjectUpdated(event: $event, services: $services); return; } if ($event instanceof ObjectDeletedEvent) { - $this->handleObjectDeleted( - event: $event, - contactSvc: $contactSvc, - settingsService: $settingsService, - logger: $logger - ); + $this->handleObjectDeleted(event: $event, services: $services); return; } @@ -170,7 +174,7 @@ private function dispatchEvent( || $event instanceof ObjectUnlockedEvent || $event instanceof ObjectRevertedEvent ) { - $logger->debug( + $services['logger']->debug( 'Stackiq: Ignoring object lifecycle event', [ 'eventType' => get_class($event), @@ -390,190 +394,246 @@ private function refetchOrganizationWithContactpersonen( }//end try }//end refetchOrganizationWithContactpersonen() + /** + * The facts every handler reads off an object, with the services. + * + * @param \OCA\OpenRegister\Db\ObjectEntity $object The event's object. + * @param array $services The contactSvc, settings and logger services. + * + * @return array The object, its id, schema (raw and as int) and register, and the services. + */ + private function objectContext(\OCA\OpenRegister\Db\ObjectEntity $object, array $services): array { + $schemaId = $object->getSchema(); + return $services + [ + 'object' => $object, + 'objectId' => $object->getUuid(), + 'schemaId' => $schemaId, + 'schemaIdInt' => (int)$schemaId, + 'registerId' => $object->getRegister(), + ]; + }//end objectContext() + + /** + * Look the catalog schemas up one by one, until one matches the object's schema. + * + * @param array $ctx The object context. + * @param array $kinds The catalog object types, in lookup order. + * + * @return array{kind: string|null, ids: array} The matching type and the ids looked up. + */ + private function firstMatchingSchema(array $ctx, array $kinds): array { + $ids = []; + foreach ($kinds as $kind) { + $ids[$kind] = $ctx['settings']->getSchemaIdForObjectType(objectType: $kind); + if ($this->matchesSchema(objectSchemaIdInt: $ctx['schemaIdInt'], configured: $ids[$kind]) === true) { + return ['kind' => $kind, 'ids' => $ids]; + } + } + + return ['kind' => null, 'ids' => $ids]; + }//end firstMatchingSchema() + /** * Handles object creation events * * @param ObjectCreatedEvent $event The creation event - * @param ContactpersoonService $contactSvc The contact person service - * @param SettingsService $settingsService The settings service - * @param LoggerInterface $logger The logger instance + * @param array $services The contactSvc, settings and logger services. * * @return void * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * * @spec openspec/specs/method-decomposition/spec.md */ - private function handleObjectCreated( - ObjectCreatedEvent $event, - ContactpersoonService $contactSvc, - SettingsService $settingsService, - LoggerInterface $logger, - ): void { + private function handleObjectCreated(ObjectCreatedEvent $event, array $services): void { $object = $event->getObject(); if ($object === null) { - $logger->warning('Stackiq: ObjectCreatedEvent received with null object'); + $services['logger']->warning('Stackiq: ObjectCreatedEvent received with null object'); return; } - $objectSchemaId = $object->getSchema(); - $objectId = $object->getUuid(); - $objectRegisterId = $object->getRegister(); - - // Convert schema ID to integer for consistent comparison. - $objectSchemaIdInt = (int)$objectSchemaId; - + $ctx = $this->objectContext(object: $object, services: $services); + $logger = $ctx['logger']; $logger->info( 'Stackiq: Processing object creation', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'schemaIdInt' => $objectSchemaIdInt, - 'registerId' => $objectRegisterId, + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'schemaIdInt' => $ctx['schemaIdInt'], + 'registerId' => $ctx['registerId'], 'objectData' => json_encode($object->getObject()), ] ); - // Get configuration for different object types. - $catalogSchemaIds = $this->resolveCatalogSchemaIds(settingsService: $settingsService); - $organisationSchemaId = $catalogSchemaIds['organization']; - $contactSchemaId = $catalogSchemaIds['contactPerson']; - $contactInfoSchemaId = $catalogSchemaIds['contactgegevens']; - $gebruikSchemaId = $catalogSchemaIds['usage']; - + $ids = $this->resolveCatalogSchemaIds(settingsService: $ctx['settings']); $logger->debug( 'Stackiq: Configuration lookup results', [ - 'organisatieSchemaId' => $organisationSchemaId, - 'contactpersoonSchemaId' => $contactSchemaId, - 'contactgegevensSchemaId' => $contactInfoSchemaId, - 'gebruikSchemaId' => $gebruikSchemaId, - 'objectSchemaId' => $objectSchemaIdInt, + 'organisatieSchemaId' => $ids['organization'], + 'contactpersoonSchemaId' => $ids['contactPerson'], + 'contactgegevensSchemaId' => $ids['contactgegevens'], + 'gebruikSchemaId' => $ids['usage'], + 'objectSchemaId' => $ctx['schemaIdInt'], ] ); - // Check if this is an organization object. - if ($this->matchesSchema(objectSchemaIdInt: $objectSchemaIdInt, configured: $organisationSchemaId) === true) { - $objectData = $object->getObject(); - $status = strtolower($objectData['status'] ?? ''); - - // Only process active organizations. - if ($this->isActiveStatus(status: $status) === false) { - $logger->debug( - 'Stackiq: Skipping non-active organization creation', - [ - 'objectId' => $objectId, - 'status' => $status, - ] - ); - return; + $kind = null; + foreach ($ids as $type => $configured) { + if ($kind === null && $this->matchesSchema(objectSchemaIdInt: $ctx['schemaIdInt'], configured: $configured) === true) { + $kind = $type; } + } - $logger->info( - 'Stackiq: Processing active organization creation', + match ($kind) { + 'organization' => $this->onOrganisationCreated(ctx: $ctx), + 'contactPerson' => $this->onContactPersonCreated(ctx: $ctx), + 'contactgegevens' => $logger->info('Stackiq: Processing contactgegevens creation (deprecated)', ['objectId' => $ctx['objectId']]), + 'usage' => $this->onUsageCreated(ctx: $ctx), + default => $logger->debug( + 'Stackiq: Object creation not handled - not a supported object type', [ - 'objectId' => $objectId, + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaIdInt'], + 'registerId' => $ctx['registerId'], + 'supportedSchemas' => $ids, + ] + ), + }; + }//end handleObjectCreated() + + /** + * A created organisation is synced when it is active. + * + * @param array $ctx The object context. + * + * @return void + */ + private function onOrganisationCreated(array $ctx): void { + $status = strtolower($ctx['object']->getObject()['status'] ?? ''); + if ($this->isActiveStatus(status: $status) === false) { + $ctx['logger']->debug( + 'Stackiq: Skipping non-active organization creation', + [ + 'objectId' => $ctx['objectId'], 'status' => $status, ] ); - - $this->runOrganizationSync(object: $object, phase: 'creation', logger: $logger); - return; - }//end if - - // Check if this is a contactpersoon object. - if ($this->matchesSchema(objectSchemaIdInt: $objectSchemaIdInt, configured: $contactSchemaId) === true) { - $logger->info('Stackiq: Processing contactpersoon creation', ['objectId' => $objectId]); - $contactSvc->processContactpersoon($object); - return; - } - - // Check if this is a contactgegevens object (deprecated - use contactpersoon instead). - if ($this->matchesSchema(objectSchemaIdInt: $objectSchemaIdInt, configured: $contactInfoSchemaId) === true) { - $logger->info('Stackiq: Processing contactgegevens creation (deprecated)', ['objectId' => $objectId]); - // Contactgegevens is deprecated, use contactpersoon instead. return; } - // Check if this is a gebruik object. - if ($this->matchesSchema(objectSchemaIdInt: $objectSchemaIdInt, configured: $gebruikSchemaId) === true) { - $logger->info('Stackiq: Processing gebruik creation', ['objectId' => $objectId]); - $this->runGebruikSync(object: $object, phase: 'creation', logger: $logger); - return; - }//end if - - // Log unhandled object types. - $logger->debug( - 'Stackiq: Object creation not handled - not a supported object type', + $ctx['logger']->info( + 'Stackiq: Processing active organization creation', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaIdInt, - 'registerId' => $objectRegisterId, - 'supportedSchemas' => [ - 'organization' => $organisationSchemaId, - 'contactPerson' => $contactSchemaId, - 'contactgegevens' => $contactInfoSchemaId, - 'usage' => $gebruikSchemaId, - ], + 'objectId' => $ctx['objectId'], + 'status' => $status, ] ); - }//end handleObjectCreated() + + $this->runOrganizationSync(object: $ctx['object'], phase: 'creation', logger: $ctx['logger']); + }//end onOrganisationCreated() + + /** + * A created contact person gets processed. + * + * @param array $ctx The object context. + * + * @return void + */ + private function onContactPersonCreated(array $ctx): void { + $ctx['logger']->info('Stackiq: Processing contactpersoon creation', ['objectId' => $ctx['objectId']]); + $ctx['contactSvc']->processContactpersoon($ctx['object']); + }//end onContactPersonCreated() + + /** + * A created usage gets synced. + * + * @param array $ctx The object context. + * + * @return void + */ + private function onUsageCreated(array $ctx): void { + $ctx['logger']->info('Stackiq: Processing gebruik creation', ['objectId' => $ctx['objectId']]); + $this->runGebruikSync(object: $ctx['object'], phase: 'creation', logger: $ctx['logger']); + }//end onUsageCreated() /** * Handles object update events * * @param ObjectUpdatedEvent $event The update event - * @param ContactpersoonService $contactSvc The contact person service - * @param SettingsService $settingsService The settings service - * @param LoggerInterface $logger The logger instance + * @param array $services The contactSvc, settings and logger services. * * @return void * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * * @spec openspec/specs/method-decomposition/spec.md */ - private function handleObjectUpdated( - ObjectUpdatedEvent $event, - ContactpersoonService $contactSvc, - SettingsService $settingsService, - LoggerInterface $logger, - ): void { + private function handleObjectUpdated(ObjectUpdatedEvent $event, array $services): void { $object = $event->getNewObject(); - $oldObject = $event->getOldObject(); - if ($object === null) { - $logger->warning('Stackiq: ObjectUpdatedEvent received with null object'); + $services['logger']->warning('Stackiq: ObjectUpdatedEvent received with null object'); return; } - $objectSchemaId = $object->getSchema(); - $objectId = $object->getUuid(); - $objectRegisterId = $object->getRegister(); + $ctx = $this->objectContext(object: $object, services: $services) + ['oldObject' => $event->getOldObject()]; + $ctx['logger']->info( + 'Stackiq: Processing object update', + [ + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'schemaIdInt' => $ctx['schemaIdInt'], + 'registerId' => $ctx['registerId'], + 'hasOldObject' => $ctx['oldObject'] !== null, + ] + ); - // Convert schema ID to integer for consistent comparison. - $objectSchemaIdInt = (int)$objectSchemaId; + if ($this->isOrganisationUpdate(ctx: $ctx) === true) { + $this->onOrganisationUpdated(ctx: $ctx); + return; + } - $logger->info( - 'Stackiq: Processing object update', + $match = $this->firstMatchingSchema(ctx: $ctx, kinds: ['contactPerson', 'contactgegevens', 'usage']); + if ($match['kind'] === 'contactPerson' || $match['kind'] === 'contactgegevens') { + $this->runContactAction(ctx: $ctx, action: ['phase' => 'update', 'kind' => $match['kind'], 'configured' => $match['ids'][$match['kind']]]); + return; + } + + if ($match['kind'] === 'usage') { + $ctx['logger']->info( + 'Stackiq: Matched gebruik schema - processing update', + [ + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'configuredSchemaId' => $match['ids']['usage'], + ] + ); + + $this->runGebruikSync(object: $object, phase: 'update', logger: $ctx['logger']); + return; + } + + $ctx['logger']->debug( + 'Stackiq: Object update not handled - focusing only on organisatie, contactpersonen, and gebruik', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'schemaIdInt' => $objectSchemaIdInt, - 'registerId' => $objectRegisterId, - 'hasOldObject' => $oldObject !== null, + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'schemaIdInt' => $ctx['schemaIdInt'], + 'schemaIdType' => gettype($ctx['schemaId']), + 'registerId' => $ctx['registerId'], + 'handledSchemas' => ['organization' => $ctx['organisationSchemaId']] + $match['ids'], ] ); + }//end handleObjectUpdated() - // Check if this is an organization update. - $organisationSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'organization'); + /** + * Whether an updated object is an organisation; logs the schema check. + * + * @param array $ctx The object context; gets the organisation schema id. + * + * @return bool True when the object's schema is the organisation schema. + */ + private function isOrganisationUpdate(array &$ctx): bool { + $organisationSchemaId = $ctx['settings']->getSchemaIdForObjectType(objectType: 'organization'); + $ctx['organisationSchemaId'] = $organisationSchemaId; $orgSchemaIdInt = (int)$organisationSchemaId; - $logger->debug( + $ctx['logger']->debug( 'Got organisation schema ID', [ 'app' => 'stackiq', @@ -582,378 +642,208 @@ private function handleObjectUpdated( ] ); - $logger->debug( + $ctx['logger']->debug( 'Organization schema check', [ 'app' => 'stackiq', - 'objectSchemaId' => $objectSchemaId, - 'objectSchemaIdInt' => $objectSchemaIdInt, + 'objectSchemaId' => $ctx['schemaId'], + 'objectSchemaIdInt' => $ctx['schemaIdInt'], 'organisatieSchemaId' => $organisationSchemaId, 'organisatieSchemaIdInt' => $orgSchemaIdInt, - 'matches' => ($objectSchemaIdInt === $orgSchemaIdInt), + 'matches' => ($ctx['schemaIdInt'] === $orgSchemaIdInt), ] ); - if ($organisationSchemaId !== null && $objectSchemaIdInt === $orgSchemaIdInt) { - $objectData = $object->getObject(); - $status = strtolower($objectData['status'] ?? ''); - - $oldStatus = ''; - if ($oldObject !== null) { - $oldData = $oldObject->getObject(); - $oldStatus = strtolower($oldData['status'] ?? ''); - } - - $logger->debug( - 'Organization status check', - [ - 'app' => 'stackiq', - 'objectId' => $objectId, - 'status' => $status, - 'oldStatus' => $oldStatus, - 'statusChanged' => ($status !== $oldStatus), - 'isActief' => $this->isActiveStatus(status: $status), - 'willProcess' => ($this->isActiveStatus(status: $status) === true - && $status !== $oldStatus), - ] - ); + return $this->matchesSchema(objectSchemaIdInt: $ctx['schemaIdInt'], configured: $organisationSchemaId); + }//end isOrganisationUpdate() - // Only process active organizations. - if ($this->isActiveStatus(status: $status) === true && $status !== $oldStatus) { - $logger->info( - 'Stackiq: Processing active organization update', - [ - 'objectId' => $objectId, - 'status' => $status, - 'schemaId' => $objectSchemaId, - ] - ); + /** + * An updated organisation that just became active is refetched with its contact persons and synced. + * + * @param array $ctx The object context, with the old object. + * + * @return void + */ + private function onOrganisationUpdated(array $ctx): void { + $status = strtolower($ctx['object']->getObject()['status'] ?? ''); + $oldStatus = ''; + if ($ctx['oldObject'] !== null) { + $oldStatus = strtolower($ctx['oldObject']->getObject()['status'] ?? ''); + } - $orgWithContacts = $this->refetchOrganizationWithContactpersonen( - objectId: $objectId, - settingsService: $settingsService, - logger: $logger - ); - if ($orgWithContacts !== null) { - $this->runOrganizationSync(object: $orgWithContacts, phase: 'update', logger: $logger); - } - }//end if - - if ($this->isActiveStatus(status: $status) === false || $status === $oldStatus) { - $logger->debug( - 'Stackiq: Skipping non-active organization update', - [ - 'objectId' => $objectId, - 'status' => $status, - 'schemaId' => $objectSchemaId, - ] - ); - } + $becameActive = $this->isActiveStatus(status: $status) === true && $status !== $oldStatus; + $ctx['logger']->debug( + 'Organization status check', + [ + 'app' => 'stackiq', + 'objectId' => $ctx['objectId'], + 'status' => $status, + 'oldStatus' => $oldStatus, + 'statusChanged' => ($status !== $oldStatus), + 'isActief' => $this->isActiveStatus(status: $status), + 'willProcess' => $becameActive, + ] + ); + $facts = [ + 'objectId' => $ctx['objectId'], + 'status' => $status, + 'schemaId' => $ctx['schemaId'], + ]; + if ($becameActive === false) { + $ctx['logger']->debug('Stackiq: Skipping non-active organization update', $facts); return; - }//end if - - // Handle contactpersoon updates. - $contactSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'contactPerson'); - $cntSchemaIdInt = (int)$contactSchemaId; - - if ($contactSchemaId !== null && $objectSchemaIdInt === $cntSchemaIdInt) { - $logger->info( - 'Stackiq: Matched contactpersoon schema - processing update', - [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $contactSchemaId, - ] - ); + } - try { - $contactSvc->handleContactpersoonUpdate( - contactPersonObject: $object, - oldContactPersonObject: $oldObject - ); + $ctx['logger']->info('Stackiq: Processing active organization update', $facts); + $orgWithContacts = $this->refetchOrganizationWithContactpersonen( + objectId: $ctx['objectId'], + settingsService: $ctx['settings'], + logger: $ctx['logger'] + ); + if ($orgWithContacts !== null) { + $this->runOrganizationSync(object: $orgWithContacts, phase: 'update', logger: $ctx['logger']); + } + }//end onOrganisationUpdated() - $logger->info( - 'Stackiq: Successfully processed contactpersoon update', - [ - 'objectId' => $objectId, - 'timestamp' => date('Y-m-d H:i:s'), - ] - ); - } catch (\Exception $e) { - $logger->error( - 'Stackiq: Failed to process contactpersoon update', - [ - 'objectId' => $objectId, - 'exception' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTraceAsString(), - ] - ); - }//end try + /** + * Hand an updated or deleted contact person (or contactgegevens) to the contact person service. + * + * @param array $ctx The object context. + * @param array{phase: string, kind: string, configured: int|null} $action The phase, the matched type and its configured schema id. + * + * @return void + */ + private function runContactAction(array $ctx, array $action): void { + [$matched, $done, $failed] = self::CONTACT_MESSAGES[$action['phase']][$action['kind']]; + $ctx['logger']->info( + $matched, + [ + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'configuredSchemaId' => $action['configured'], + ] + ); - return; - }//end if + $call = fn () => $ctx['contactSvc']->handleContactDeletion($ctx['object']); + if ($action['phase'] === 'update') { + $call = fn () => $ctx['contactSvc']->handleContactpersoonUpdate( + contactPersonObject: $ctx['object'], + oldContactPersonObject: $ctx['oldObject'] + ); + } - // Handle contactgegevens updates (backward compatibility). - $contactInfoSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'contactgegevens'); - $infoSchemaIdInt = (int)$contactInfoSchemaId; + try { + $call(); - if ($contactInfoSchemaId !== null && $objectSchemaIdInt === $infoSchemaIdInt) { - $logger->info( - 'Stackiq: Matched contactgegevens schema - processing update (backward compatibility)', + $ctx['logger']->info( + $done, [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $contactInfoSchemaId, + 'objectId' => $ctx['objectId'], + 'timestamp' => date('Y-m-d H:i:s'), ] ); - - try { - // Handle contactgegevens as contactpersoon (backward compatibility). - $contactSvc->handleContactpersoonUpdate( - contactPersonObject: $object, - oldContactPersonObject: $oldObject - ); - - $logger->info( - 'Stackiq: Successfully processed contactgegevens update (as contactpersoon)', - [ - 'objectId' => $objectId, - 'timestamp' => date('Y-m-d H:i:s'), - ] - ); - } catch (\Exception $e) { - $logger->error( - 'Stackiq: Failed to process contactgegevens update', - [ - 'objectId' => $objectId, - 'exception' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTraceAsString(), - ] - ); - }//end try - - return; - }//end if - - // Handle gebruik updates. - $gebruikSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'usage'); - $gebruikSchemaIdInt = (int)$gebruikSchemaId; - - if ($gebruikSchemaId !== null && $objectSchemaIdInt === $gebruikSchemaIdInt) { - $logger->info( - 'Stackiq: Matched gebruik schema - processing update', + } catch (\Exception $e) { + $ctx['logger']->error( + $failed, [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $gebruikSchemaId, + 'objectId' => $ctx['objectId'], + 'exception' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString(), ] ); - - $this->runGebruikSync(object: $object, phase: 'update', logger: $logger); - return; - }//end if - - // Log if we don't handle this schema type. - $logger->debug( - 'Stackiq: Object update not handled - focusing only on organisatie, contactpersonen, and gebruik', - [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'schemaIdInt' => $objectSchemaIdInt, - 'schemaIdType' => gettype($objectSchemaId), - 'registerId' => $objectRegisterId, - 'handledSchemas' => [ - 'organization' => $organisationSchemaId, - 'contactPerson' => $contactSchemaId, - 'contactgegevens' => $contactInfoSchemaId, - 'usage' => $gebruikSchemaId, - ], - ] - ); - }//end handleObjectUpdated() + }//end try + }//end runContactAction() /** * Handles object deletion events * * @param ObjectDeletedEvent $event The deletion event - * @param ContactpersoonService $contactSvc The contact person service - * @param SettingsService $settingsService The settings service - * @param LoggerInterface $logger The logger instance + * @param array $services The contactSvc, settings and logger services. * * @return void * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * * @spec openspec/specs/method-decomposition/spec.md */ - private function handleObjectDeleted( - ObjectDeletedEvent $event, - ContactpersoonService $contactSvc, - SettingsService $settingsService, - LoggerInterface $logger, - ): void { + private function handleObjectDeleted(ObjectDeletedEvent $event, array $services): void { $object = $event->getObject(); if ($object === null) { - $logger->warning('Stackiq: ObjectDeletedEvent received with null object'); + $services['logger']->warning('Stackiq: ObjectDeletedEvent received with null object'); return; } - $objectSchemaId = $object->getSchema(); - $objectId = $object->getUuid(); - $objectRegisterId = $object->getRegister(); - - $logger->info( + $ctx = $this->objectContext(object: $object, services: $services); + $ctx['logger']->info( 'Stackiq: Processing object deletion', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'registerId' => $objectRegisterId, + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'registerId' => $ctx['registerId'], 'objectData' => $object->getObject(), ] ); - // Check if this is an organization deletion. - $organisationSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'organization'); - $orgSchemaIdInt = (int)$organisationSchemaId; - $objectSchemaIdInt = (int)$objectSchemaId; - - if ($organisationSchemaId !== null && $objectSchemaIdInt === $orgSchemaIdInt) { - $logger->info('Stackiq: Processing organization deletion', ['objectId' => $objectId]); - $this->runOrganizationSync(object: $object, phase: 'deletion', logger: $logger); - return; - }//end if - - // Handle contactpersoon deletion. - $contactSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'contactPerson'); - $cntSchemaIdInt = (int)$contactSchemaId; - - if ($contactSchemaId !== null && $objectSchemaIdInt === $cntSchemaIdInt) { - $logger->info( - 'Stackiq: Matched contactpersoon schema - processing deletion', - [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $contactSchemaId, - ] - ); - - try { - $contactSvc->handleContactDeletion($object); - - $logger->info( - 'Stackiq: Successfully processed contactpersoon deletion', - [ - 'objectId' => $objectId, - 'timestamp' => date('Y-m-d H:i:s'), - ] - ); - } catch (\Exception $e) { - $logger->error( - 'Stackiq: Failed to process contactpersoon deletion', - [ - 'objectId' => $objectId, - 'exception' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTraceAsString(), - ] - ); - }//end try - - return; - }//end if - - // Handle contactgegevens deletion (backward compatibility). - $contactInfoSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'contactgegevens'); - $infoSchemaIdInt = (int)$contactInfoSchemaId; - - if ($contactInfoSchemaId !== null && $objectSchemaIdInt === $infoSchemaIdInt) { - $logger->info( - 'Stackiq: Matched contactgegevens schema - processing deletion (backward compatibility)', + $match = $this->firstMatchingSchema(ctx: $ctx, kinds: ['organization', 'contactPerson', 'contactgegevens', 'usage']); + match ($match['kind']) { + 'organization' => $this->onOrganisationDeleted(ctx: $ctx), + 'contactPerson', 'contactgegevens' => $this->runContactAction( + ctx: $ctx, + action: ['phase' => 'deletion', 'kind' => $match['kind'], 'configured' => $match['ids'][$match['kind']]] + ), + 'usage' => $this->onUsageDeleted(ctx: $ctx, configured: $match['ids']['usage']), + default => $ctx['logger']->debug( + 'Stackiq: Object deletion not handled - focusing only on organisatie, contactpersonen, and gebruik', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $contactInfoSchemaId, + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'registerId' => $ctx['registerId'], + 'handledSchemas' => $match['ids'], ] - ); - - try { - $contactSvc->handleContactDeletion($object); - - $logger->info( - 'Stackiq: Successfully processed contactgegevens deletion', - [ - 'objectId' => $objectId, - 'timestamp' => date('Y-m-d H:i:s'), - ] - ); - } catch (\Exception $e) { - $logger->error( - 'Stackiq: Failed to process contactgegevens deletion', - [ - 'objectId' => $objectId, - 'exception' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTraceAsString(), - ] - ); - }//end try - - return; - }//end if - - // Handle gebruik deletion. - $gebruikSchemaId = $settingsService->getSchemaIdForObjectType(objectType: 'usage'); - $gebruikSchemaIdInt = (int)$gebruikSchemaId; - - if ($gebruikSchemaId !== null && $objectSchemaIdInt === $gebruikSchemaIdInt) { - $objectData = $object->getObject(); + ), + }; + }//end handleObjectDeleted() - $logger->info( - 'Stackiq: Matched gebruik schema - processing deletion', - [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'configuredSchemaId' => $gebruikSchemaId, - 'consumer' => $objectData['consumer']['name'] ?? 'Unknown', - 'product' => $objectData['product']['name'] ?? 'Unknown', - ] - ); + /** + * A deleted organisation gets synced. + * + * @param array $ctx The object context. + * + * @return void + */ + private function onOrganisationDeleted(array $ctx): void { + $ctx['logger']->info('Stackiq: Processing organization deletion', ['objectId' => $ctx['objectId']]); + $this->runOrganizationSync(object: $ctx['object'], phase: 'deletion', logger: $ctx['logger']); + }//end onOrganisationDeleted() - // For deletions, we mainly log the event since the object is being removed. - // No specific cleanup needed for gebruik objects currently. - $logger->info( - 'Stackiq: Gebruik object deleted - no specific cleanup required', - [ - 'objectId' => $objectId, - 'timestamp' => date('Y-m-d H:i:s'), - ] - ); - return; - }//end if + /** + * A deleted usage is only logged; it needs no clean-up. + * + * @param array $ctx The object context. + * @param int|null $configured The configured usage schema id. + * + * @return void + */ + private function onUsageDeleted(array $ctx, ?int $configured): void { + $objectData = $ctx['object']->getObject(); + $ctx['logger']->info( + 'Stackiq: Matched gebruik schema - processing deletion', + [ + 'objectId' => $ctx['objectId'], + 'schemaId' => $ctx['schemaId'], + 'configuredSchemaId' => $configured, + 'consumer' => $objectData['consumer']['name'] ?? 'Unknown', + 'product' => $objectData['product']['name'] ?? 'Unknown', + ] + ); - // Log if we don't handle this schema type. - $logger->debug( - 'Stackiq: Object deletion not handled - focusing only on organisatie, contactpersonen, and gebruik', + $ctx['logger']->info( + 'Stackiq: Gebruik object deleted - no specific cleanup required', [ - 'objectId' => $objectId, - 'schemaId' => $objectSchemaId, - 'registerId' => $objectRegisterId, - 'handledSchemas' => [ - 'organization' => $organisationSchemaId, - 'contactPerson' => $contactSchemaId, - 'contactgegevens' => $contactInfoSchemaId, - 'usage' => $gebruikSchemaId, - ], + 'objectId' => $ctx['objectId'], + 'timestamp' => date('Y-m-d H:i:s'), ] ); - }//end handleObjectDeleted() + }//end onUsageDeleted() }//end class diff --git a/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php b/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php new file mode 100644 index 000000000..0d457189f --- /dev/null +++ b/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php @@ -0,0 +1,350 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * @link https://github.com/ConductionNL/stackiq + * + * @spec openspec/specs/method-decomposition/spec.md + */ + +declare(strict_types=1); + +namespace OCA\Stackiq\Tests\Unit\EventListener; + +use OCA\OpenRegister\Db\ObjectEntity; +use OCA\OpenRegister\Event\ObjectCreatedEvent; +use OCA\OpenRegister\Event\ObjectDeletedEvent; +use OCA\OpenRegister\Event\ObjectUpdatedEvent; +use OCA\Stackiq\EventListener\StackiqEventListener; +use OCA\Stackiq\Service\ContactpersoonService; +use OCA\Stackiq\Service\GebruikSyncService; +use OCA\Stackiq\Service\OrganizationSyncService; +use OCA\Stackiq\Service\SettingsService; +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerInterface; + +/** + * Golden cases for StackiqEventListener::handle(). + * + * @covers \OCA\Stackiq\EventListener\StackiqEventListener + * + * @spec openspec/specs/method-decomposition/spec.md + */ +class StackiqEventListenerGoldenTest extends TestCase { + + /** + * The configured catalog schema ids. + */ + private const SCHEMAS = ['organization' => 1, 'contactPerson' => 2, 'contactgegevens' => 3, 'usage' => 4]; + + /** + * Context keys whose values depend on the clock or the run. + */ + private const VOLATILE = ['timestamp', 'trace', 'file', 'line']; + + /** + * Everything the doubles saw, in order. + * + * @var array + */ + private array $trace = []; + + /** + * Per-case behaviour of the doubles. + * + * @var array + */ + private array $plan = []; + + /** + * The cases: event kind, schema, data, old data and the doubles' behaviour. + * + * @return array}> + */ + public static function cases(): array { + $active = ['name' => 'Gemeente A', 'status' => 'Actief']; + $inactive = ['name' => 'Gemeente A', 'status' => 'inactief']; + $usage = ['consumer' => ['name' => 'Gemeente A'], 'product' => ['name' => 'Zaaksysteem']]; + return [ + 'created organisation active' => ['created', '1', $active, null, []], + 'created organisation active english' => ['created', '1', ['status' => 'ACTIVE'], null, []], + 'created organisation inactive' => ['created', '1', $inactive, null, []], + 'created organisation no status' => ['created', '1', [], null, []], + 'created organisation sync fails' => ['created', '1', $active, null, ['orgSyncThrows' => true]], + 'created contact person' => ['created', '2', ['email' => 'a@a.nl'], null, []], + 'created contact person fails' => ['created', '2', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], + 'created contactgegevens' => ['created', '3', [], null, []], + 'created usage' => ['created', '4', $usage, null, []], + 'created usage sync fails' => ['created', '4', $usage, null, ['usageSyncThrows' => true]], + 'created other schema' => ['created', '9', ['x' => 1], null, []], + 'created, no schemas configured' => ['created', '1', $active, null, ['noSchemas' => true]], + 'created, schema id as string' => ['created', '01', $active, null, []], + 'created, two types share a schema' => ['created', '4', ['email' => 'a@a.nl'], null, ['schemas' => ['organization' => 1, 'contactPerson' => 4, 'contactgegevens' => 3, 'usage' => 4]]], + 'updated organisation activated' => ['updated', '1', $active, $inactive, []], + 'updated organisation, no old object' => ['updated', '1', $active, null, []], + 'updated organisation still active' => ['updated', '1', $active, $active, []], + 'updated organisation deactivated' => ['updated', '1', $inactive, $active, []], + 'updated organisation, refetch fails' => ['updated', '1', $active, $inactive, ['findThrows' => true]], + 'updated organisation, sync fails' => ['updated', '1', $active, $inactive, ['orgSyncThrows' => true]], + 'updated contact person' => ['updated', '2', ['email' => 'a@a.nl'], ['email' => 'b@a.nl'], []], + 'updated contact person fails' => ['updated', '2', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], + 'updated contactgegevens' => ['updated', '3', ['email' => 'a@a.nl'], null, []], + 'updated contactgegevens fails' => ['updated', '3', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], + 'updated usage' => ['updated', '4', $usage, null, []], + 'updated other schema' => ['updated', '9', ['x' => 1], null, []], + 'updated, no schemas configured' => ['updated', '2', [], null, ['noSchemas' => true]], + 'deleted organisation' => ['deleted', '1', $active, null, []], + 'deleted organisation, sync fails' => ['deleted', '1', $active, null, ['orgSyncThrows' => true]], + 'deleted contact person' => ['deleted', '2', ['email' => 'a@a.nl'], null, []], + 'deleted contact person fails' => ['deleted', '2', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], + 'deleted contactgegevens' => ['deleted', '3', [], null, []], + 'deleted contactgegevens fails' => ['deleted', '3', [], null, ['contactThrows' => true]], + 'deleted usage' => ['deleted', '4', $usage, null, []], + 'deleted usage without names' => ['deleted', '4', [], null, []], + 'deleted other schema' => ['deleted', '9', ['x' => 1], null, []], + 'deleted, no schemas configured' => ['deleted', '4', [], null, ['noSchemas' => true]], + ]; + }//end cases() + + /** + * Each case matches its recording. + * + * @param string $kind created, updated or deleted. + * @param string $schema The object's schema id. + * @param array $data The object data. + * @param array|null $oldData The previous object data, for an update. + * @param array $plan The behaviour of the doubles. + * + * @return void + * + * @dataProvider cases + */ + public function testHandleMatchesTheRecording(string $kind, string $schema, array $data, ?array $oldData, array $plan): void { + $this->plan = $plan; + $this->trace = []; + $object = $this->entity(uuid: 'obj-1', schema: $schema, data: $data); + $events = [ + 'created' => fn () => new ObjectCreatedEvent($object), + 'updated' => fn () => new ObjectUpdatedEvent($object, $oldData === null ? null : $this->entity(uuid: 'obj-1', schema: $schema, data: $oldData)), + 'deleted' => fn () => new ObjectDeletedEvent($object), + ]; + + (new StackiqEventListener($this->container()))->handle($events[$kind]()); + + $this->check(key: $this->dataName(), value: $this->trace); + }//end testHandleMatchesTheRecording() + + /** + * An object entity double. + * + * @param string $uuid The uuid. + * @param string $schema The schema id. + * @param array $data The data. + * + * @return ObjectEntity + */ + private function entity(string $uuid, string $schema, array $data): ObjectEntity { + $entity = $this->createMock(ObjectEntity::class); + $entity->method('getUuid')->willReturn($uuid); + $entity->method('getSchema')->willReturn($schema); + $entity->method('getRegister')->willReturn('5'); + $entity->method('getObject')->willReturn($data); + return $entity; + }//end entity() + + /** + * A container with recording doubles for every service the listener resolves. + * + * @return ContainerInterface + */ + private function container(): ContainerInterface { + $settings = $this->createMock(SettingsService::class); + $settings->method('getSchemaIdForObjectType')->willReturnCallback( + function (string $objectType): ?int { + $this->trace[] = ['settings.getSchemaIdForObjectType', $objectType]; + if (isset($this->plan['noSchemas']) === true) { + return null; + } + + return ($this->plan['schemas'] ?? self::SCHEMAS)[$objectType] ?? null; + } + ); + $settings->method('getVoorzieningenConfig')->willReturnCallback( + function (): array { + $this->trace[] = ['settings.getVoorzieningenConfig']; + return ['register' => '5', 'organisatie_schema' => '1']; + } + ); + + $contacts = $this->createMock(ContactpersoonService::class); + foreach (['processContactpersoon', 'handleContactpersoonUpdate', 'handleContactDeletion'] as $method) { + $contacts->method($method)->willReturnCallback( + function (...$args) use ($method) { + $this->trace[] = ['contacts.' . $method, array_map(fn ($arg) => is_object($arg) === true ? $arg->getUuid() : $arg, $args)]; + if (isset($this->plan['contactThrows']) === true) { + throw new \RuntimeException('contact failed'); + } + + return true; + } + ); + } + + $orgSync = $this->createMock(OrganizationSyncService::class); + $orgSync->method('processSpecificOrganization')->willReturnCallback( + function ($object): array { + $this->trace[] = ['orgSync.processSpecificOrganization', $object->getUuid(), $object->getObject()]; + if (isset($this->plan['orgSyncThrows']) === true) { + throw new \RuntimeException('org sync failed'); + } + + return ['organizationsProcessed' => 1]; + } + ); + + $usageSync = $this->createMock(GebruikSyncService::class); + $usageSync->method('processSpecificGebruik')->willReturnCallback( + function ($object): array { + $this->trace[] = ['usageSync.processSpecificGebruik', $object->getUuid()]; + if (isset($this->plan['usageSyncThrows']) === true) { + throw new \RuntimeException('usage sync failed'); + } + + return ['processed' => 1]; + } + ); + + $objects = new class($this) { + /** + * @param StackiqEventListenerGoldenTest $test The test, for the trace. + */ + public function __construct(private StackiqEventListenerGoldenTest $test) { + } + + /** + * @param mixed ...$args The lookup, by name. + * + * @return ObjectEntity + */ + public function find(...$args): ObjectEntity { + return $this->test->refetch(args: $args); + } + }; + + $services = [ + LoggerInterface::class => $this->logger(), + ContactpersoonService::class => $contacts, + SettingsService::class => $settings, + 'OCA\Stackiq\Service\OrganizationSyncService' => $orgSync, + GebruikSyncService::class => $usageSync, + 'OCA\OpenRegister\Service\ObjectService' => $objects, + ]; + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->willReturnCallback( + function (string $id) use ($services) { + $this->trace[] = ['container.get', $id]; + return $services[$id]; + } + ); + + return $container; + }//end container() + + /** + * Record a refetch of the organisation and answer it. + * + * @param array $args The find() arguments, by name. + * + * @return ObjectEntity The refetched organisation. + */ + public function refetch(array $args): ObjectEntity { + $this->trace[] = ['objects.find', $args]; + if (isset($this->plan['findThrows']) === true) { + throw new \RuntimeException('find failed'); + } + + return $this->entity(uuid: 'obj-1', schema: '1', data: ['status' => 'actief', 'contactpersonen' => [['email' => 'a@a.nl'], ['email' => 'b@a.nl']]]); + }//end refetch() + + /** + * A logger that records every line, with volatile values masked. + * + * @return LoggerInterface + */ + private function logger(): LoggerInterface { + $trace = &$this->trace; + return new class($trace, self::VOLATILE) extends AbstractLogger { + /** + * @param array $trace The shared trace. + * @param array $volatile The keys to mask. + */ + public function __construct(private array &$trace, private array $volatile) { + } + + /** + * @param mixed $level The level. + * @param string|\Stringable $message The message. + * @param array $context The context. + * + * @return void + */ + public function log($level, string|\Stringable $message, array $context = []): void { + foreach ($this->volatile as $key) { + if (array_key_exists($key, $context) === true) { + $context[$key] = '(' . $key . ')'; + } + } + + if (isset($context['eventType']) === true) { + $context['eventType'] = preg_replace('/Mock\w+/', 'Mock', (string) $context['eventType']); + } + + $this->trace[] = ['log.' . $level, (string) $message, $context]; + } + }; + }//end logger() + + /** + * Compare a case with the golden file, or record it. + * + * @param string $key The case. + * @param array $value What the case observed. + * + * @return void + */ + private function check(string $key, array $value): void { + $json = json_decode(json_encode($value, JSON_PARTIAL_OUTPUT_ON_ERROR), true); + $file = __DIR__ . '/golden/stackiqEventListener.json'; + $saved = []; + if (file_exists($file) === true) { + $saved = json_decode((string) file_get_contents($file), true); + } + + if (getenv('RECORD_GOLDEN') !== false) { + $saved[$key] = $json; + if (is_dir(dirname($file)) === false) { + mkdir(dirname($file)); + } + + file_put_contents($file, json_encode($saved, (JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)) . "\n"); + $this->addToAssertionCount(count: 1); + return; + } + + $this->assertArrayHasKey(key: $key, array: $saved, message: 'No golden for ' . $key); + $this->assertSame(expected: $saved[$key], actual: $json); + }//end check() +}//end class diff --git a/tests/Unit/EventListener/golden/stackiqEventListener.json b/tests/Unit/EventListener/golden/stackiqEventListener.json new file mode 100644 index 000000000..ff16bbd0b --- /dev/null +++ b/tests/Unit/EventListener/golden/stackiqEventListener.json @@ -0,0 +1,3106 @@ +{ + "created organisation active": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"name\":\"Gemeente A\",\"status\":\"Actief\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.info", + "Stackiq: Processing active organization creation", + { + "objectId": "obj-1", + "status": "actief" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "name": "Gemeente A", + "status": "Actief" + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization creation", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "created organisation active english": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"status\":\"ACTIVE\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.info", + "Stackiq: Processing active organization creation", + { + "objectId": "obj-1", + "status": "active" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "status": "ACTIVE" + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization creation", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "created organisation inactive": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"name\":\"Gemeente A\",\"status\":\"inactief\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.debug", + "Stackiq: Skipping non-active organization creation", + { + "objectId": "obj-1", + "status": "inactief" + } + ] + ], + "created organisation no status": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "[]" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.debug", + "Stackiq: Skipping non-active organization creation", + { + "objectId": "obj-1", + "status": "" + } + ] + ], + "created organisation sync fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"name\":\"Gemeente A\",\"status\":\"Actief\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.info", + "Stackiq: Processing active organization creation", + { + "objectId": "obj-1", + "status": "actief" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "name": "Gemeente A", + "status": "Actief" + } + ], + [ + "log.error", + "Stackiq: Failed to process organization creation", + { + "objectId": "obj-1", + "exception": "org sync failed", + "file": "(file)", + "line": "(line)" + } + ] + ], + "created contact person": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "objectData": "{\"email\":\"a@a.nl\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 2 + } + ], + [ + "log.info", + "Stackiq: Processing contactpersoon creation", + { + "objectId": "obj-1" + } + ], + [ + "contacts.processContactpersoon", + [ + "obj-1", + false + ] + ] + ], + "created contact person fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "objectData": "{\"email\":\"a@a.nl\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 2 + } + ], + [ + "log.info", + "Stackiq: Processing contactpersoon creation", + { + "objectId": "obj-1" + } + ], + [ + "contacts.processContactpersoon", + [ + "obj-1", + false + ] + ], + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "log.error", + "Stackiq: Error in event handler", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "exception": "contact failed", + "file": "(file)", + "line": "(line)", + "trace": "(trace)" + } + ] + ], + "created contactgegevens": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "3", + "schemaIdInt": 3, + "registerId": "5", + "objectData": "[]" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 3 + } + ], + [ + "log.info", + "Stackiq: Processing contactgegevens creation (deprecated)", + { + "objectId": "obj-1" + } + ] + ], + "created usage": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "4", + "schemaIdInt": 4, + "registerId": "5", + "objectData": "{\"consumer\":{\"name\":\"Gemeente A\"},\"product\":{\"name\":\"Zaaksysteem\"}}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 4 + } + ], + [ + "log.info", + "Stackiq: Processing gebruik creation", + { + "objectId": "obj-1" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\GebruikSyncService" + ], + [ + "usageSync.processSpecificGebruik", + "obj-1" + ], + [ + "log.info", + "Stackiq: Successfully processed gebruik creation", + { + "objectId": "obj-1", + "processResult": { + "processed": 1 + } + } + ] + ], + "created usage sync fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "4", + "schemaIdInt": 4, + "registerId": "5", + "objectData": "{\"consumer\":{\"name\":\"Gemeente A\"},\"product\":{\"name\":\"Zaaksysteem\"}}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 4 + } + ], + [ + "log.info", + "Stackiq: Processing gebruik creation", + { + "objectId": "obj-1" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\GebruikSyncService" + ], + [ + "usageSync.processSpecificGebruik", + "obj-1" + ], + [ + "log.error", + "Stackiq: Failed to process gebruik creation", + { + "objectId": "obj-1", + "exception": "usage sync failed", + "file": "(file)", + "line": "(line)" + } + ] + ], + "created other schema": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "9", + "schemaIdInt": 9, + "registerId": "5", + "objectData": "{\"x\":1}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 9 + } + ], + [ + "log.debug", + "Stackiq: Object creation not handled - not a supported object type", + { + "objectId": "obj-1", + "schemaId": 9, + "registerId": "5", + "supportedSchemas": { + "organization": 1, + "contactPerson": 2, + "contactgegevens": 3, + "usage": 4 + } + } + ] + ], + "created, no schemas configured": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"name\":\"Gemeente A\",\"status\":\"Actief\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": null, + "contactpersoonSchemaId": null, + "contactgegevensSchemaId": null, + "gebruikSchemaId": null, + "objectSchemaId": 1 + } + ], + [ + "log.debug", + "Stackiq: Object creation not handled - not a supported object type", + { + "objectId": "obj-1", + "schemaId": 1, + "registerId": "5", + "supportedSchemas": { + "organization": null, + "contactPerson": null, + "contactgegevens": null, + "usage": null + } + } + ] + ], + "created, schema id as string": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "01", + "schemaIdInt": 1, + "registerId": "5", + "objectData": "{\"name\":\"Gemeente A\",\"status\":\"Actief\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 2, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 1 + } + ], + [ + "log.info", + "Stackiq: Processing active organization creation", + { + "objectId": "obj-1", + "status": "actief" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "name": "Gemeente A", + "status": "Actief" + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization creation", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "updated organisation activated": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "inactief", + "statusChanged": true, + "isActief": true, + "willProcess": true + } + ], + [ + "log.info", + "Stackiq: Processing active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "1" + } + ], + [ + "settings.getVoorzieningenConfig" + ], + [ + "container.get", + "OCA\\OpenRegister\\Service\\ObjectService" + ], + [ + "objects.find", + { + "id": "obj-1", + "register": "5", + "schema": "1", + "_extend": [ + "contactpersonen" + ], + "_rbac": false, + "_multitenancy": false + } + ], + [ + "log.info", + "Stackiq: Refetched organization with contactpersonen", + { + "objectId": "obj-1", + "contactpersonenCount": 2 + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "status": "actief", + "contactpersonen": [ + { + "email": "a@a.nl" + }, + { + "email": "b@a.nl" + } + ] + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization update", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "updated organisation, no old object": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "", + "statusChanged": true, + "isActief": true, + "willProcess": true + } + ], + [ + "log.info", + "Stackiq: Processing active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "1" + } + ], + [ + "settings.getVoorzieningenConfig" + ], + [ + "container.get", + "OCA\\OpenRegister\\Service\\ObjectService" + ], + [ + "objects.find", + { + "id": "obj-1", + "register": "5", + "schema": "1", + "_extend": [ + "contactpersonen" + ], + "_rbac": false, + "_multitenancy": false + } + ], + [ + "log.info", + "Stackiq: Refetched organization with contactpersonen", + { + "objectId": "obj-1", + "contactpersonenCount": 2 + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "status": "actief", + "contactpersonen": [ + { + "email": "a@a.nl" + }, + { + "email": "b@a.nl" + } + ] + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization update", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "updated organisation still active": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "actief", + "statusChanged": false, + "isActief": true, + "willProcess": false + } + ], + [ + "log.debug", + "Stackiq: Skipping non-active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "1" + } + ] + ], + "updated organisation deactivated": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "inactief", + "oldStatus": "actief", + "statusChanged": true, + "isActief": false, + "willProcess": false + } + ], + [ + "log.debug", + "Stackiq: Skipping non-active organization update", + { + "objectId": "obj-1", + "status": "inactief", + "schemaId": "1" + } + ] + ], + "updated organisation, refetch fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "inactief", + "statusChanged": true, + "isActief": true, + "willProcess": true + } + ], + [ + "log.info", + "Stackiq: Processing active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "1" + } + ], + [ + "settings.getVoorzieningenConfig" + ], + [ + "container.get", + "OCA\\OpenRegister\\Service\\ObjectService" + ], + [ + "objects.find", + { + "id": "obj-1", + "register": "5", + "schema": "1", + "_extend": [ + "contactpersonen" + ], + "_rbac": false, + "_multitenancy": false + } + ], + [ + "log.error", + "Stackiq: Failed to refetch organization with contactpersonen", + { + "objectId": "obj-1", + "exception": "find failed", + "file": "(file)", + "line": "(line)" + } + ] + ], + "updated organisation, sync fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "1", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "1", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "inactief", + "statusChanged": true, + "isActief": true, + "willProcess": true + } + ], + [ + "log.info", + "Stackiq: Processing active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "1" + } + ], + [ + "settings.getVoorzieningenConfig" + ], + [ + "container.get", + "OCA\\OpenRegister\\Service\\ObjectService" + ], + [ + "objects.find", + { + "id": "obj-1", + "register": "5", + "schema": "1", + "_extend": [ + "contactpersonen" + ], + "_rbac": false, + "_multitenancy": false + } + ], + [ + "log.info", + "Stackiq: Refetched organization with contactpersonen", + { + "objectId": "obj-1", + "contactpersonenCount": 2 + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "status": "actief", + "contactpersonen": [ + { + "email": "a@a.nl" + }, + { + "email": "b@a.nl" + } + ] + } + ], + [ + "log.error", + "Stackiq: Failed to process organization update", + { + "objectId": "obj-1", + "exception": "org sync failed", + "file": "(file)", + "line": "(line)" + } + ] + ], + "updated contact person": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "2", + "objectSchemaIdInt": 2, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "log.info", + "Stackiq: Matched contactpersoon schema - processing update", + { + "objectId": "obj-1", + "schemaId": "2", + "configuredSchemaId": 2 + } + ], + [ + "contacts.handleContactpersoonUpdate", + [ + "obj-1", + "obj-1" + ] + ], + [ + "log.info", + "Stackiq: Successfully processed contactpersoon update", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "updated contact person fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "2", + "objectSchemaIdInt": 2, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "log.info", + "Stackiq: Matched contactpersoon schema - processing update", + { + "objectId": "obj-1", + "schemaId": "2", + "configuredSchemaId": 2 + } + ], + [ + "contacts.handleContactpersoonUpdate", + [ + "obj-1", + null + ] + ], + [ + "log.error", + "Stackiq: Failed to process contactpersoon update", + { + "objectId": "obj-1", + "exception": "contact failed", + "file": "(file)", + "line": "(line)", + "trace": "(trace)" + } + ] + ], + "updated contactgegevens": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "3", + "schemaIdInt": 3, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "3", + "objectSchemaIdInt": 3, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "log.info", + "Stackiq: Matched contactgegevens schema - processing update (backward compatibility)", + { + "objectId": "obj-1", + "schemaId": "3", + "configuredSchemaId": 3 + } + ], + [ + "contacts.handleContactpersoonUpdate", + [ + "obj-1", + null + ] + ], + [ + "log.info", + "Stackiq: Successfully processed contactgegevens update (as contactpersoon)", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "updated contactgegevens fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "3", + "schemaIdInt": 3, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "3", + "objectSchemaIdInt": 3, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "log.info", + "Stackiq: Matched contactgegevens schema - processing update (backward compatibility)", + { + "objectId": "obj-1", + "schemaId": "3", + "configuredSchemaId": 3 + } + ], + [ + "contacts.handleContactpersoonUpdate", + [ + "obj-1", + null + ] + ], + [ + "log.error", + "Stackiq: Failed to process contactgegevens update", + { + "objectId": "obj-1", + "exception": "contact failed", + "file": "(file)", + "line": "(line)", + "trace": "(trace)" + } + ] + ], + "updated usage": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "4", + "schemaIdInt": 4, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "4", + "objectSchemaIdInt": 4, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.info", + "Stackiq: Matched gebruik schema - processing update", + { + "objectId": "obj-1", + "schemaId": "4", + "configuredSchemaId": 4 + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\GebruikSyncService" + ], + [ + "usageSync.processSpecificGebruik", + "obj-1" + ], + [ + "log.info", + "Stackiq: Successfully processed gebruik update", + { + "objectId": "obj-1", + "processResult": { + "processed": 1 + } + } + ] + ], + "updated other schema": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "9", + "schemaIdInt": 9, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "9", + "objectSchemaIdInt": 9, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Object update not handled - focusing only on organisatie, contactpersonen, and gebruik", + { + "objectId": "obj-1", + "schemaId": "9", + "schemaIdInt": 9, + "schemaIdType": "string", + "registerId": "5", + "handledSchemas": { + "organization": 1, + "contactPerson": 2, + "contactgegevens": 3, + "usage": 4 + } + } + ] + ], + "updated, no schemas configured": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": null, + "organisatieSchemaIdInt": 0 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "2", + "objectSchemaIdInt": 2, + "organisatieSchemaId": null, + "organisatieSchemaIdInt": 0, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Object update not handled - focusing only on organisatie, contactpersonen, and gebruik", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "schemaIdType": "string", + "registerId": "5", + "handledSchemas": { + "organization": null, + "contactPerson": null, + "contactgegevens": null, + "usage": null + } + } + ] + ], + "deleted organisation": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "1", + "registerId": "5", + "objectData": { + "name": "Gemeente A", + "status": "Actief" + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.info", + "Stackiq: Processing organization deletion", + { + "objectId": "obj-1" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "name": "Gemeente A", + "status": "Actief" + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization deletion", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "deleted organisation, sync fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "1", + "registerId": "5", + "objectData": { + "name": "Gemeente A", + "status": "Actief" + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.info", + "Stackiq: Processing organization deletion", + { + "objectId": "obj-1" + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "name": "Gemeente A", + "status": "Actief" + } + ], + [ + "log.error", + "Stackiq: Failed to process organization deletion", + { + "objectId": "obj-1", + "exception": "org sync failed", + "file": "(file)", + "line": "(line)" + } + ] + ], + "deleted contact person": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "2", + "registerId": "5", + "objectData": { + "email": "a@a.nl" + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "log.info", + "Stackiq: Matched contactpersoon schema - processing deletion", + { + "objectId": "obj-1", + "schemaId": "2", + "configuredSchemaId": 2 + } + ], + [ + "contacts.handleContactDeletion", + [ + "obj-1" + ] + ], + [ + "log.info", + "Stackiq: Successfully processed contactpersoon deletion", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "deleted contact person fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "2", + "registerId": "5", + "objectData": { + "email": "a@a.nl" + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "log.info", + "Stackiq: Matched contactpersoon schema - processing deletion", + { + "objectId": "obj-1", + "schemaId": "2", + "configuredSchemaId": 2 + } + ], + [ + "contacts.handleContactDeletion", + [ + "obj-1" + ] + ], + [ + "log.error", + "Stackiq: Failed to process contactpersoon deletion", + { + "objectId": "obj-1", + "exception": "contact failed", + "file": "(file)", + "line": "(line)", + "trace": "(trace)" + } + ] + ], + "deleted contactgegevens": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "3", + "registerId": "5", + "objectData": [] + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "log.info", + "Stackiq: Matched contactgegevens schema - processing deletion (backward compatibility)", + { + "objectId": "obj-1", + "schemaId": "3", + "configuredSchemaId": 3 + } + ], + [ + "contacts.handleContactDeletion", + [ + "obj-1" + ] + ], + [ + "log.info", + "Stackiq: Successfully processed contactgegevens deletion", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "deleted contactgegevens fails": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "3", + "registerId": "5", + "objectData": [] + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "log.info", + "Stackiq: Matched contactgegevens schema - processing deletion (backward compatibility)", + { + "objectId": "obj-1", + "schemaId": "3", + "configuredSchemaId": 3 + } + ], + [ + "contacts.handleContactDeletion", + [ + "obj-1" + ] + ], + [ + "log.error", + "Stackiq: Failed to process contactgegevens deletion", + { + "objectId": "obj-1", + "exception": "contact failed", + "file": "(file)", + "line": "(line)", + "trace": "(trace)" + } + ] + ], + "deleted usage": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "4", + "registerId": "5", + "objectData": { + "consumer": { + "name": "Gemeente A" + }, + "product": { + "name": "Zaaksysteem" + } + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.info", + "Stackiq: Matched gebruik schema - processing deletion", + { + "objectId": "obj-1", + "schemaId": "4", + "configuredSchemaId": 4, + "consumer": "Gemeente A", + "product": "Zaaksysteem" + } + ], + [ + "log.info", + "Stackiq: Gebruik object deleted - no specific cleanup required", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "deleted usage without names": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "4", + "registerId": "5", + "objectData": [] + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.info", + "Stackiq: Matched gebruik schema - processing deletion", + { + "objectId": "obj-1", + "schemaId": "4", + "configuredSchemaId": 4, + "consumer": "Unknown", + "product": "Unknown" + } + ], + [ + "log.info", + "Stackiq: Gebruik object deleted - no specific cleanup required", + { + "objectId": "obj-1", + "timestamp": "(timestamp)" + } + ] + ], + "deleted other schema": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "9", + "registerId": "5", + "objectData": { + "x": 1 + } + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Object deletion not handled - focusing only on organisatie, contactpersonen, and gebruik", + { + "objectId": "obj-1", + "schemaId": "9", + "registerId": "5", + "handledSchemas": { + "organization": 1, + "contactPerson": 2, + "contactgegevens": 3, + "usage": 4 + } + } + ] + ], + "deleted, no schemas configured": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectDeletedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object deletion", + { + "objectId": "obj-1", + "schemaId": "4", + "registerId": "5", + "objectData": [] + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Object deletion not handled - focusing only on organisatie, contactpersonen, and gebruik", + { + "objectId": "obj-1", + "schemaId": "4", + "registerId": "5", + "handledSchemas": { + "organization": null, + "contactPerson": null, + "contactgegevens": null, + "usage": null + } + } + ] + ], + "created, two types share a schema": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectCreatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object creation", + { + "objectId": "obj-1", + "schemaId": "4", + "schemaIdInt": 4, + "registerId": "5", + "objectData": "{\"email\":\"a@a.nl\"}" + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "settings.getSchemaIdForObjectType", + "contactgegevens" + ], + [ + "settings.getSchemaIdForObjectType", + "usage" + ], + [ + "log.debug", + "Stackiq: Configuration lookup results", + { + "organisatieSchemaId": 1, + "contactpersoonSchemaId": 4, + "contactgegevensSchemaId": 3, + "gebruikSchemaId": 4, + "objectSchemaId": 4 + } + ], + [ + "log.info", + "Stackiq: Processing contactpersoon creation", + { + "objectId": "obj-1" + } + ], + [ + "contacts.processContactpersoon", + [ + "obj-1", + false + ] + ] + ] +} From 73fa68bde7d0ad9976bc775313485f99e73831d2 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 3 Nov 2026 19:21:20 +0100 Subject: [PATCH 2/2] test(listener): pin an escaping Error and a zero-padded schema id; drop the stale skipped test Two golden cases from review, recorded on the old code: an Error from the contact person service escapes handle() (catch (\Exception) does not catch it), and an organisation update with schema id "01" matches schema 1. StackiqEventListenerTest skipped all 12 of its cases in setUp(): they were written against a constructor and service calls the listener no longer has. The golden test covers the current dispatch, so the stale file goes. Assisted-by: ClaudeCode:claude-opus-5-5 --- .../StackiqEventListenerGoldenTest.php | 12 +- .../StackiqEventListenerTest.php | 387 ------------------ .../golden/stackiqEventListener.json | 220 ++++++++++ 3 files changed, 231 insertions(+), 388 deletions(-) delete mode 100644 tests/Unit/EventListener/StackiqEventListenerTest.php diff --git a/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php b/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php index 0d457189f..f32a8f15d 100644 --- a/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php +++ b/tests/Unit/EventListener/StackiqEventListenerGoldenTest.php @@ -99,8 +99,10 @@ public static function cases(): array { 'updated organisation deactivated' => ['updated', '1', $inactive, $active, []], 'updated organisation, refetch fails' => ['updated', '1', $active, $inactive, ['findThrows' => true]], 'updated organisation, sync fails' => ['updated', '1', $active, $inactive, ['orgSyncThrows' => true]], + 'updated organisation, schema \'01\'' => ['updated', '01', $active, $inactive, []], 'updated contact person' => ['updated', '2', ['email' => 'a@a.nl'], ['email' => 'b@a.nl'], []], 'updated contact person fails' => ['updated', '2', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], + 'updated contact person, Error escapes' => ['updated', '2', ['email' => 'a@a.nl'], null, ['contactThrows' => 'error']], 'updated contactgegevens' => ['updated', '3', ['email' => 'a@a.nl'], null, []], 'updated contactgegevens fails' => ['updated', '3', ['email' => 'a@a.nl'], null, ['contactThrows' => true]], 'updated usage' => ['updated', '4', $usage, null, []], @@ -142,7 +144,11 @@ public function testHandleMatchesTheRecording(string $kind, string $schema, arra 'deleted' => fn () => new ObjectDeletedEvent($object), ]; - (new StackiqEventListener($this->container()))->handle($events[$kind]()); + try { + (new StackiqEventListener($this->container()))->handle($events[$kind]()); + } catch (\Throwable $e) { + $this->trace[] = ['escaped', get_class($e), $e->getMessage()]; + } $this->check(key: $this->dataName(), value: $this->trace); }//end testHandleMatchesTheRecording() @@ -194,6 +200,10 @@ function (): array { $contacts->method($method)->willReturnCallback( function (...$args) use ($method) { $this->trace[] = ['contacts.' . $method, array_map(fn ($arg) => is_object($arg) === true ? $arg->getUuid() : $arg, $args)]; + if (($this->plan['contactThrows'] ?? null) === 'error') { + throw new \Error('contact broke'); + } + if (isset($this->plan['contactThrows']) === true) { throw new \RuntimeException('contact failed'); } diff --git a/tests/Unit/EventListener/StackiqEventListenerTest.php b/tests/Unit/EventListener/StackiqEventListenerTest.php deleted file mode 100644 index 42215db86..000000000 --- a/tests/Unit/EventListener/StackiqEventListenerTest.php +++ /dev/null @@ -1,387 +0,0 @@ - - * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * @link https://github.com/ConductionNL/stackiq - * @version 1.0.0 - */ -class StackiqEventListenerTest extends TestCase { - /** - * Mock of the StackiqService - * - * @var StackiqService|MockObject - */ - private StackiqService|MockObject $softwareCatalogueService; - - /** - * Mock of the LoggerInterface - * - * @var LoggerInterface|MockObject - */ - private LoggerInterface|MockObject $logger; - - /** - * The event listener instance under test - * - * @var StackiqEventListener - */ - private StackiqEventListener $eventListener; - - /** - * Set up the test environment before each test - * - * @return void - */ - protected function setUp(): void { - parent::setUp(); - - // The StackiqEventListener was refactored after these tests - // were written: handle() now dispatches via SettingsService schema-id - // lookups (handleObjectCreated/Updated/Deleted private dispatchers) - // rather than the direct handleNewContact/handleNewGebruiker/etc. - // service methods these tests assert. The tests need to be rewritten - // against the new dispatch flow and additional collaborators - // (SettingsService, AppManager, IUserManager, etc.) — tracked as a - // follow-up. See https://github.com/ConductionNL/stackiq - $this->markTestSkipped( - 'Stale against current StackiqEventListener — needs ' - . 'rewrite against new SettingsService-driven dispatch. ' - . 'Tracked as follow-up issue.' - ); - - $this->softwareCatalogueService = $this->createMock(StackiqService::class); - $this->logger = $this->createMock(LoggerInterface::class); - - $this->eventListener = new StackiqEventListener( - $this->softwareCatalogueService, - $this->logger - ); - } - - /** - * Test handling organization creation event - * - * @return void - */ - public function testHandleOrganizationCreatedEvent(): void { - // Create mock ObjectEntity for organization (schema ID 1) - $organization = $this->createMock(ObjectEntity::class); - $organization->method('getSchema')->willReturn(1); - - // Create ObjectCreatedEvent - $event = new ObjectCreatedEvent($organization); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleNewOrganization') - ->with($organization); - - $this->softwareCatalogueService - ->expects($this->once()) - ->method('sendOrganizationWelcomeEmail') - ->with($organization); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling contact creation event - * - * @return void - */ - public function testHandleContactCreatedEvent(): void { - // Create mock ObjectEntity for contact (schema ID 2) - $contact = $this->createMock(ObjectEntity::class); - $contact->method('getSchema')->willReturn(2); - - // Create ObjectCreatedEvent - $event = new ObjectCreatedEvent($contact); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleNewContact') - ->with($contact); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling gebruiker (user) creation event - * - * @return void - */ - public function testHandleGebruikerCreatedEvent(): void { - // Create mock ObjectEntity for gebruiker (schema ID 3) - $user = $this->createMock(ObjectEntity::class); - $user->method('getSchema')->willReturn(3); - - // Create ObjectCreatedEvent - $event = new ObjectCreatedEvent($user); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleNewGebruiker') - ->with($user); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling contact update event - * - * @return void - */ - public function testHandleContactUpdatedEvent(): void { - // Create mock ObjectEntity for contact (schema ID 2) - $newContact = $this->createMock(ObjectEntity::class); - $newContact->method('getSchema')->willReturn(2); - - $oldContact = $this->createMock(ObjectEntity::class); - - // Create ObjectUpdatedEvent - $event = new ObjectUpdatedEvent($newContact, $oldContact); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleContactUpdate') - ->with($newContact); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling gebruiker (user) update event - * - * @return void - */ - public function testHandleGebruikerUpdatedEvent(): void { - // Create mock ObjectEntity for gebruiker (schema ID 3) - $newUser = $this->createMock(ObjectEntity::class); - $newUser->method('getSchema')->willReturn(3); - - $oldUser = $this->createMock(ObjectEntity::class); - - // Create ObjectUpdatedEvent - $event = new ObjectUpdatedEvent($newUser, $oldUser); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleGebruikerUpdate') - ->with($newUser, $oldUser); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling contact deletion event - * - * @return void - */ - public function testHandleContactDeletedEvent(): void { - // Create mock ObjectEntity for contact (schema ID 2) - $contact = $this->createMock(ObjectEntity::class); - $contact->method('getSchema')->willReturn(2); - - // Create ObjectDeletedEvent - $event = new ObjectDeletedEvent($contact); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('handleContactDeletion') - ->with($contact); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling gebruiker (user) deletion event - should block user - * - * @return void - */ - public function testHandleGebruikerDeletedEvent(): void { - // Create mock ObjectEntity for gebruiker (schema ID 3) - $user = $this->createMock(ObjectEntity::class); - $user->method('getSchema')->willReturn(3); - - // Create ObjectDeletedEvent - $event = new ObjectDeletedEvent($user); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('blockUserForGebruiker') - ->with($user); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling gebruiker (user) locking event - * - * @return void - */ - public function testHandleGebruikerLockedEvent(): void { - // Create mock ObjectEntity for gebruiker (schema ID 3) - $user = $this->createMock(ObjectEntity::class); - $user->method('getSchema')->willReturn(3); - - // Create ObjectLockedEvent - $event = new ObjectLockedEvent($user); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('temporarilyBlockUserForGebruiker') - ->with($user); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * Test handling gebruiker (user) unlocking event - * - * @return void - */ - public function testHandleGebruikerUnlockedEvent(): void { - // Create mock ObjectEntity for gebruiker (schema ID 3) - $user = $this->createMock(ObjectEntity::class); - $user->method('getSchema')->willReturn(3); - - // Create ObjectUnlockedEvent - $event = new ObjectUnlockedEvent($user); - - // Set expectations for service calls - $this->softwareCatalogueService - ->expects($this->once()) - ->method('restoreUserAccessForGebruiker') - ->with($user); - - // Handle the event - $this->eventListener->handle($event); - } - - /** - * An object-reverted event reaches the listener without driving - * StackiqService. - * - * The two cases that stood here asserted - * `syncUserWithRevertedContact()` / `updateUserFromRevertedGebruiker()` - * were called once each. `StackiqEventListener` does not call - * them — it never did — and both service methods were log-only stubs with - * no caller, removed with this change. The assertion that survives is the - * true one: the revert path must not blow up, and must not reach a - * capability the app does not implement. - * - * @return void - */ - public function testHandleRevertedEventDoesNotDriveTheCatalogueService(): void { - $contact = $this->createMock(ObjectEntity::class); - $contact->method('getSchema')->willReturn(2); - - $this->softwareCatalogueService - ->expects($this->never()) - ->method('handleContactUpdate'); - - $this->eventListener->handle( - new ObjectRevertedEvent($contact, new \DateTime('2024-01-01 12:00:00')) - ); - } - - /** - * Test handling events with null objects - * - * @return void - */ - public function testHandleEventWithUnmatchedSchema(): void { - // Create a mock ObjectEntity with a schema that doesn't match any configured schema - $object = $this->createMock(ObjectEntity::class); - $object->method('getSchema')->willReturn(999999); - $object->method('getUuid')->willReturn('test-uuid'); - $object->method('getRegister')->willReturn(1); - - // Create ObjectCreatedEvent with a valid object but unmatched schema - $event = new ObjectCreatedEvent($object); - - // No service methods should be called since schema doesn't match - $this->softwareCatalogueService - ->expects($this->never()) - ->method($this->anything()); - - // Handle the event - should return early since schema doesn't match - $this->eventListener->handle($event); - } - - /** - * Test exception handling during event processing - * - * @return void - */ - public function testExceptionHandlingDuringEventProcessing(): void { - // Create mock ObjectEntity for organization (schema ID 1) - $organization = $this->createMock(ObjectEntity::class); - $organization->method('getSchema')->willReturn(1); - - // Create ObjectCreatedEvent - $event = new ObjectCreatedEvent($organization); - - // Mock service to throw exception - $exception = new \Exception('Service error'); - $this->softwareCatalogueService - ->method('handleNewOrganization') - ->willThrowException($exception); - - // Expect logger to be called with error - $this->logger - ->expects($this->once()) - ->method('error') - ->with( - 'Failed to handle new organization: Service error', - [ - 'exception' => $exception, - 'object' => $organization - ] - ); - - // Handle the event - $this->eventListener->handle($event); - } -} diff --git a/tests/Unit/EventListener/golden/stackiqEventListener.json b/tests/Unit/EventListener/golden/stackiqEventListener.json index ff16bbd0b..2b50aea6f 100644 --- a/tests/Unit/EventListener/golden/stackiqEventListener.json +++ b/tests/Unit/EventListener/golden/stackiqEventListener.json @@ -3102,5 +3102,225 @@ false ] ] + ], + "updated organisation, schema '01'": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "01", + "schemaIdInt": 1, + "registerId": "5", + "hasOldObject": true + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "01", + "objectSchemaIdInt": 1, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": true + } + ], + [ + "log.debug", + "Organization status check", + { + "app": "stackiq", + "objectId": "obj-1", + "status": "actief", + "oldStatus": "inactief", + "statusChanged": true, + "isActief": true, + "willProcess": true + } + ], + [ + "log.info", + "Stackiq: Processing active organization update", + { + "objectId": "obj-1", + "status": "actief", + "schemaId": "01" + } + ], + [ + "settings.getVoorzieningenConfig" + ], + [ + "container.get", + "OCA\\OpenRegister\\Service\\ObjectService" + ], + [ + "objects.find", + { + "id": "obj-1", + "register": "5", + "schema": "1", + "_extend": [ + "contactpersonen" + ], + "_rbac": false, + "_multitenancy": false + } + ], + [ + "log.info", + "Stackiq: Refetched organization with contactpersonen", + { + "objectId": "obj-1", + "contactpersonenCount": 2 + } + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\OrganizationSyncService" + ], + [ + "orgSync.processSpecificOrganization", + "obj-1", + { + "status": "actief", + "contactpersonen": [ + { + "email": "a@a.nl" + }, + { + "email": "b@a.nl" + } + ] + } + ], + [ + "log.info", + "Stackiq: Successfully processed organization update", + { + "objectId": "obj-1", + "processResult": { + "organizationsProcessed": 1 + } + } + ] + ], + "updated contact person, Error escapes": [ + [ + "container.get", + "Psr\\Log\\LoggerInterface" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\ContactpersoonService" + ], + [ + "container.get", + "OCA\\Stackiq\\Service\\SettingsService" + ], + [ + "log.info", + "Stackiq: Processing event", + { + "eventType": "OCA\\OpenRegister\\Event\\ObjectUpdatedEvent", + "timestamp": "(timestamp)" + } + ], + [ + "log.info", + "Stackiq: Processing object update", + { + "objectId": "obj-1", + "schemaId": "2", + "schemaIdInt": 2, + "registerId": "5", + "hasOldObject": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "organization" + ], + [ + "log.debug", + "Got organisation schema ID", + { + "app": "stackiq", + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1 + } + ], + [ + "log.debug", + "Organization schema check", + { + "app": "stackiq", + "objectSchemaId": "2", + "objectSchemaIdInt": 2, + "organisatieSchemaId": 1, + "organisatieSchemaIdInt": 1, + "matches": false + } + ], + [ + "settings.getSchemaIdForObjectType", + "contactPerson" + ], + [ + "log.info", + "Stackiq: Matched contactpersoon schema - processing update", + { + "objectId": "obj-1", + "schemaId": "2", + "configuredSchemaId": 2 + } + ], + [ + "contacts.handleContactpersoonUpdate", + [ + "obj-1", + null + ] + ], + [ + "escaped", + "Error", + "contact broke" + ] ] }