From bf8739b7cd257c89a5a53f13341076057378c192 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 00:06:45 +0100 Subject: [PATCH 1/9] Let create a bit more acces --- lib/Service/ObjectService.php | 117 ++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 27 deletions(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 5a3a5cd..ef052af 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -356,6 +356,11 @@ public function getObjects(?string $objectType = null, ?int $register = null, ?i */ public function saveObject(int $register, int $schema, array $object): ObjectEntity { + // Remove system properties (starting with _) + $object = array_filter($object, function($key) { + return !str_starts_with($key, '_'); + }, ARRAY_FILTER_USE_KEY); + // Convert register and schema to their respective objects if they are strings // @todo ??? if (is_string($register)) { $register = $this->registerMapper->find($register); @@ -374,8 +379,6 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt ); } -// $validationResult = $this->validateObject(object: $object, schemaId: $schema); - // Create new entity if none exists if (isset($object['id']) === false || $objectEntity === null) { $objectEntity = new ObjectEntity(); @@ -395,7 +398,7 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt $oldObject = clone $objectEntity; $objectEntity->setObject($object); - // Ensure UUID exists //@todo: this is not needed anymore? this kinde of uuid is set in the handleLinkRelations function + // Ensure UUID exists if (empty($objectEntity->getUuid())) { $objectEntity->setUuid(Uuid::v4()); } @@ -403,27 +406,23 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt // Let grap any links that we can $objectEntity = $this->handleLinkRelations($objectEntity, $object); - $schemaObject = $this->schemaMapper->find($schema); + $schemaObject = $this->schemaMapper->find($schema); // Handle object properties that are either nested objects or files - if ($schemaObject->getProperties() !== null && is_array($schemaObject->getProperties())) { - $objectEntity = $this->handleObjectRelations($objectEntity, $object, $schemaObject->getProperties(), $register, $schema); - $objectEntity->setObject($object); - } - - $objectEntity->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openregister.Objects.show', ['id' => $objectEntity->getUuid()]))); - - if ($objectEntity->getId()) {// && ($schemaObject->getHardValidation() === false || $validationResult->isValid() === true)){ - $objectEntity = $this->objectEntityMapper->update($objectEntity); - $this->auditTrailMapper->createAuditTrail(new: $objectEntity, old: $oldObject); - } else {//if ($schemaObject->getHardValidation() === false || $validationResult->isValid() === true) { - $objectEntity = $this->objectEntityMapper->insert($objectEntity); - $this->auditTrailMapper->createAuditTrail(new: $objectEntity); - } + if ($schemaObject->getProperties() !== null && is_array($schemaObject->getProperties())) { + $objectEntity = $this->handleObjectRelations($objectEntity, $object, $schemaObject->getProperties(), $register, $schema); + $objectEntity->setObject($object); + } -// if ($validationResult->isValid() === false) { -// throw new ValidationException(message: 'The object could not be validated', errors: $validationResult->error()); -// } + $objectEntity->setUri($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('openregister.Objects.show', ['id' => $objectEntity->getUuid()]))); + + if ($objectEntity->getId()) { + $objectEntity = $this->objectEntityMapper->update($objectEntity); + $this->auditTrailMapper->createAuditTrail(new: $objectEntity, old: $oldObject); + } else { + $objectEntity = $this->objectEntityMapper->insert($objectEntity); + $this->auditTrailMapper->createAuditTrail(new: $objectEntity); + } return $objectEntity; } @@ -1021,19 +1020,83 @@ public function setSchema(int $schema): void /** * Get the audit trail for a specific object * - * @todo: register and schema parameters are not needed anymore - * - * @param int $register The register ID - * @param int $schema The schema ID * @param string $id The object ID + * @param int|null $register Optional register ID to override current register + * @param int|null $schema Optional schema ID to override current schema * @return array The audit trail entries */ - public function getAuditTrail(int $register, int $schema, string $id): array + public function getAuditTrail(string $id, ?int $register = null, ?int $schema = null): array { + $register = $register ?? $this->getRegister(); + $schema = $schema ?? $this->getSchema(); + $filters = [ - 'object' => $id + 'object' => $id, + 'register' => $register, + 'schema' => $schema ]; return $this->auditTrailMapper->findAllUuid(idOrUuid: $id); } + + /** + * Get all relations for a specific object + * Returns objects that this object links to + * + * @param string $id The object ID + * @param int|null $register Optional register ID to override current register + * @param int|null $schema Optional schema ID to override current schema + * @return array The related objects + */ + public function getRelations(string $id, ?int $register = null, ?int $schema = null): array + { + $register = $register ?? $this->getRegister(); + $schema = $schema ?? $this->getSchema(); + + // First get the object to access its relations + $object = $this->find($id); + $relations = $object->getRelations() ?? []; + + // Get all referenced objects + $relatedObjects = []; + foreach ($relations as $path => $relationId) { + try { + $relatedObjects[$path] = $this->find($relationId); + } catch (Exception $e) { + // Skip relations that can't be found + continue; + } + } + + return $relatedObjects; + } + + /** + * Get all uses of a specific object + * Returns objects that link to this object + * + * @param string $id The object ID + * @param int|null $register Optional register ID to override current register + * @param int|null $schema Optional schema ID to override current schema + * @return array The objects using this object + */ + public function getUses(string $id, ?int $register = null, ?int $schema = null): array + { + $register = $register ?? $this->getRegister(); + $schema = $schema ?? $this->getSchema(); + + // Get the object to get its URI and UUID + $object = $this->find($id); + + // Find objects that reference this object's URI or UUID + $usingObjects = $this->objectEntityMapper->findByRelationUri( + search: $object->getUuid(), + partialMatch: true + ); + + // Filter out self-references if any + return array_filter($usingObjects, function($usingObject) use ($id) { + return $usingObject->getUuid() !== $id; + }); + } } From e7d5ffbbf68772352fb0c45f8ab6b9d387135b3d Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 00:14:24 +0100 Subject: [PATCH 2/9] Better extend functionality --- lib/Db/ObjectEntityMapper.php | 7 ++--- lib/Service/ObjectService.php | 50 +++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/lib/Db/ObjectEntityMapper.php b/lib/Db/ObjectEntityMapper.php index 84d7d8c..b4fc2ad 100644 --- a/lib/Db/ObjectEntityMapper.php +++ b/lib/Db/ObjectEntityMapper.php @@ -46,7 +46,7 @@ public function __construct(IDBConnection $db, MySQLJsonService $mySQLJsonServic * @param int|string $idOrUuid The ID or UUID of the object to find * @return ObjectEntity The ObjectEntity */ - public function find($idOrUuid): ObjectEntity + public function find($identifier): ObjectEntity { $qb = $this->db->getQueryBuilder(); @@ -54,8 +54,9 @@ public function find($idOrUuid): ObjectEntity ->from('openregister_objects') ->where( $qb->expr()->orX( - $qb->expr()->eq('id', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_INT)), - $qb->expr()->eq('uuid', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_STR)) + $qb->expr()->eq('id', $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_INT)), + $qb->expr()->eq('uuid', $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR)), + $qb->expr()->eq('uri', $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR)) ) ); diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index ef052af..a1a85aa 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -897,7 +897,7 @@ public function renderEntity(array $entity, ?array $extend = []): array * @param mixed $entity The entity to extend * @param array $extend Properties to extend with related data * @return array The extended entity as an array - * @throws Exception If property not found or no mapper available + * @throws Exception If property not found */ public function extendEntity(array $entity, array $extend): array { @@ -925,26 +925,48 @@ public function extendEntity(array $entity, array $extend): array } // Try to get mapper for property - $propertyObject = $property; try { $mapper = $this->getMapper(objectType: $property); $propertyObject = $singularProperty; + + // Extend with related objects using specific mapper + if (is_array($value) === true) { + $result[$property] = $this->getMultipleObjects(objectType: $propertyObject, ids: $value); + } else { + $objectId = is_object(value: $value) ? $value->getId() : $value; + $result[$property] = $mapper->find($objectId); + } } catch (Exception $e) { + // If no specific mapper found, try to look up values in default database try { - $mapper = $this->getMapper(objectType: $singularProperty); - $propertyObject = $singularProperty; - } catch (Exception $e) { - throw new Exception("No mapper available for property '$property'."); + if (is_array($value)) { + // Handle array of values + $extendedValues = []; + foreach ($value as $val) { + try { + $found = $this->objectEntityMapper->find($val); + if ($found) { + $extendedValues[] = $found; + } + } catch (Exception $e) { + continue; + } + } + if (!empty($extendedValues)) { + $result[$property] = $extendedValues; + } + } else { + // Handle single value + $found = $this->objectEntityMapper->find($value); + if ($found) { + $result[$property] = $found; + } + } + } catch (Exception $e2) { + // If lookup fails, keep original value + continue; } } - - // Extend with related objects - if (is_array($value) === true) { - $result[$property] = $this->getMultipleObjects(objectType: $propertyObject, ids: $value); - } else { - $objectId = is_object(value: $value) ? $value->getId() : $value; - $result[$property] = $mapper->find($objectId); - } } return $result; From 01eb5333130f94094736b959dfa70293f56eb7bd Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 00:51:18 +0100 Subject: [PATCH 3/9] Making the code a bit more readable --- lib/Service/ObjectService.php | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index a1a85aa..290ee56 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -223,7 +223,13 @@ public function delete(array|\JsonSerializable $object): bool * * @return array List of matching objects */ - public function findAll(?int $limit = null, ?int $offset = null, array $filters = [], array $sort = [], ?string $search = null, ?array $extend = []): array + public function findAll( + ?int $limit = null, + ?int $offset = null, + array $filters = [], + array $sort = [], ? + string $search = null, + ?array $extend = []): array { $objects = $this->getObjects( register: $this->getRegister(), @@ -232,7 +238,8 @@ public function findAll(?int $limit = null, ?int $offset = null, array $filters offset: $offset, filters: $filters, sort: $sort, - search: $search + search: $search, + extend: $extend ); return $objects; @@ -327,7 +334,16 @@ private function getDataFromObject(mixed $object, ?array $extend = []): mixed * * @return array The retrieved objects. */ - public function getObjects(?string $objectType = null, ?int $register = null, ?int $schema = null, ?int $limit = null, ?int $offset = null, array $filters = [], array $sort = [], ?string $search = null, ?array $extend = []): array + public function getObjects( + ?string $objectType = null, + ?int $register = null, + ?int $schema = null, + ?int $limit = null, + ?int $offset = null, + array $filters = [], + array $sort = [], + ?string $search = null, + ?array $extend = []): array { // Set object type and filters if register and schema are provided if ($objectType === null && $register !== null && $schema !== null) { @@ -340,7 +356,13 @@ public function getObjects(?string $objectType = null, ?int $register = null, ?i $mapper = $this->getMapper($objectType); // Use the mapper to find and return all objects of the specified type - return $mapper->findAll(limit: $limit, offset: $offset, filters: $filters, sort: $sort, search: $search); + return $mapper->findAll( + limit: $limit, + offset: $offset, + filters: $filters, + sort: $sort, + search: $search + ); } /** From f90659184d28766b043043d11d88a96122a85403 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 01:13:27 +0100 Subject: [PATCH 4/9] Fixing extend for service users --- lib/Service/ObjectService.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 4b31e4b..b553aa0 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -227,9 +227,10 @@ public function findAll( ?int $limit = null, ?int $offset = null, array $filters = [], - array $sort = [], ? - string $search = null, - ?array $extend = []): array + array $sort = [], + ?string $search = null, + ?array $extend = [] + ): array { $objects = $this->getObjects( register: $this->getRegister(), @@ -238,10 +239,18 @@ public function findAll( offset: $offset, filters: $filters, sort: $sort, - search: $search, - extend: $extend + search: $search ); + // If extend is provided, extend each object + if (!empty($extend)) { + $objects = array_map(function($object) use ($extend) { + // Convert object to array if needed + $objectArray = is_array($object) ? $object : $object->jsonSerialize(); + return $this->extendEntity(entity: $objectArray, extend: $extend); + }, $objects); + } + return $objects; } @@ -421,8 +430,8 @@ public function getObjects( ?int $offset = null, array $filters = [], array $sort = [], - ?string $search = null, - ?array $extend = []): array + ?string $search = null + ) { // Set object type and filters if register and schema are provided if ($objectType === null && $register !== null && $schema !== null) { From c20dc261e28446daa071625381bfb382213a86b1 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 01:25:18 +0100 Subject: [PATCH 5/9] Small fixes --- lib/Db/AuditTrailMapper.php | 2 +- lib/Service/ObjectService.php | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Db/AuditTrailMapper.php b/lib/Db/AuditTrailMapper.php index ce89d99..e9cd616 100644 --- a/lib/Db/AuditTrailMapper.php +++ b/lib/Db/AuditTrailMapper.php @@ -98,7 +98,7 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters public function findAllUuid(string $idOrUuid, ?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array { try { - $object = $this->objectEntityMapper->find(idOrUuid: $idOrUuid); + $object = $this->objectEntityMapper->find(identifier: $identifier); $objectId = $object->getId(); $filters['object'] = $objectId; return $this->findAll($limit, $offset, $filters, $searchConditions, $searchParams); diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index b553aa0..0506efb 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -130,10 +130,10 @@ public function validateObject(array $object, ?int $schemaId = null, object $sch * @param int|string $id The ID or UUID to search for * @param array|null $extend Properties to extend with related data * - * @return ObjectEntity The found object + * @return ObjectEntity|null The found object or null if not found * @throws Exception */ - public function find(int|string $id, ?array $extend = []): ObjectEntity + public function find(int|string $id, ?array $extend = []): ?ObjectEntity { return $this->getObject( register: $this->registerMapper->find($this->getRegister()), @@ -854,12 +854,11 @@ private function handleFileProperty(ObjectEntity $objectEntity, array $object, s * @param string $uuid The UUID of the object to get * @param array $extend Properties to extend with related data * - * @return ObjectEntity The resulting object + * @return ObjectEntity|null The resulting object or null if not found * @throws Exception If source type is unsupported */ - public function getObject(Register $register, Schema $schema, string $uuid, ?array $extend = []): ObjectEntity + public function getObject(Register $register, Schema $schema, string $uuid, ?array $extend = []): ?ObjectEntity { - // Handle internal source if ($register->getSource() === 'internal' || $register->getSource() === '') { return $this->objectEntityMapper->findByUuid($register, $schema, $uuid); From 49c59fb1994d6cb993d7ed813c3cd23ca28a901c Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 08:38:59 +0100 Subject: [PATCH 6/9] fixing the uses case --- lib/Service/ObjectService.php | 60 ++++++++++++++++------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 0506efb..1c67efb 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -1176,62 +1176,58 @@ public function getAuditTrail(string $id, ?int $register = null, ?int $schema = /** * Get all relations for a specific object - * Returns objects that this object links to + * Returns objects that link to this object (incoming references) * * @param string $id The object ID * @param int|null $register Optional register ID to override current register * @param int|null $schema Optional schema ID to override current schema - * @return array The related objects + * @return array The objects that reference this object */ public function getRelations(string $id, ?int $register = null, ?int $schema = null): array { $register = $register ?? $this->getRegister(); $schema = $schema ?? $this->getSchema(); - // First get the object to access its relations + // Get the object to get its URI and UUID $object = $this->find($id); - $relations = $object->getRelations() ?? []; - - // Get all referenced objects - $relatedObjects = []; - foreach ($relations as $path => $relationId) { - try { - $relatedObjects[$path] = $this->find($relationId); - } catch (Exception $e) { - // Skip relations that can't be found - continue; - } - } + + // Find objects that reference this object's URI or UUID + $referencingObjects = $this->objectEntityMapper->findByRelationUri( + search: $object->getUuid(), + partialMatch: true + ); - return $relatedObjects; + // Filter out self-references if any + return array_filter($referencingObjects, function($referencingObject) use ($id) { + return $referencingObject->getUuid() !== $id; + }); } /** * Get all uses of a specific object - * Returns objects that link to this object + * Returns objects that this object links to (outgoing references) * * @param string $id The object ID * @param int|null $register Optional register ID to override current register * @param int|null $schema Optional schema ID to override current schema - * @return array The objects using this object + * @return array The objects this object references */ public function getUses(string $id, ?int $register = null, ?int $schema = null): array { - $register = $register ?? $this->getRegister(); - $schema = $schema ?? $this->getSchema(); - - // Get the object to get its URI and UUID + // First get the object to access its relations $object = $this->find($id); - - // Find objects that reference this object's URI or UUID - $usingObjects = $this->objectEntityMapper->findByRelationUri( - search: $object->getUuid(), - partialMatch: true - ); + $relations = $object->getRelations() ?? []; - // Filter out self-references if any - return array_filter($usingObjects, function($usingObject) use ($id) { - return $usingObject->getUuid() !== $id; - }); + // Get all referenced objects + $referencedObjects = []; + foreach ($relations as $path => $relationId) { + $referencedObjects[$path] = $this->objectEntityMapper->find($relationId); + + if($referencedObjects[$path] === null){ + $referencedObjects[$path] = $relationId; + } + } + + return $referencedObjects; } } From eab74e1745794b51d01f3ac26735c5d8ae073560 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 08:59:17 +0100 Subject: [PATCH 7/9] Getting to work on the audit trails --- lib/Service/ObjectService.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 1c67efb..5604e3e 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -17,6 +17,7 @@ use OCA\OpenRegister\Db\ObjectEntityMapper; use OCA\OpenRegister\Db\AuditTrail; use OCA\OpenRegister\Db\AuditTrailMapper; +use OCA\OpenRegister\Db\ObjectAuditLogMapper; use OCA\OpenRegister\Exception\ValidationException; use OCA\OpenRegister\Formats\BsnFormat; use OCP\App\IAppManager; @@ -67,6 +68,7 @@ public function __construct( RegisterMapper $registerMapper, SchemaMapper $schemaMapper, AuditTrailMapper $auditTrailMapper, + ObjectAuditLogMapper $objectAuditLogMapper, private ContainerInterface $container, private readonly IURLGenerator $urlGenerator, private readonly FileService $fileService, @@ -77,6 +79,7 @@ public function __construct( $this->registerMapper = $registerMapper; $this->schemaMapper = $schemaMapper; $this->auditTrailMapper = $auditTrailMapper; + $this->objectAuditLogMapper = $objectAuditLogMapper; } /** @@ -1162,16 +1165,13 @@ public function setSchema(int $schema): void */ public function getAuditTrail(string $id, ?int $register = null, ?int $schema = null): array { - $register = $register ?? $this->getRegister(); - $schema = $schema ?? $this->getSchema(); - - $filters = [ - 'object' => $id, - 'register' => $register, - 'schema' => $schema - ]; + // Get the object to get its URI and UUID + $object = $this->find($id); - return $this->auditTrailMapper->findAllUuid(idOrUuid: $id); + // @todo this is not working, it fails to find the logs + $auditTrails = $this->objectAuditLogMapper->findAll(null, null, ['object_id' => (int) $object->getId()]); + + return $auditTrails; } /** From 2cb2d6826de86cebb08d61025e123428ca65d4c0 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Fri, 27 Dec 2024 14:03:14 +0100 Subject: [PATCH 8/9] Fix auditTrailMapper-functionCall --- lib/Db/AuditTrailMapper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Db/AuditTrailMapper.php b/lib/Db/AuditTrailMapper.php index e9cd616..974351e 100644 --- a/lib/Db/AuditTrailMapper.php +++ b/lib/Db/AuditTrailMapper.php @@ -92,10 +92,10 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters /** * Finds all audit trails for a given object * - * @param string $idOrUuid The id or uuid of the object + * @param string $identifier The id or uuid of the object * @return array The audit trails */ - public function findAllUuid(string $idOrUuid, ?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array + public function findAllUuid(string $identifier, ?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array { try { $object = $this->objectEntityMapper->find(identifier: $identifier); From 8186bcc0a40f3c02647fb934386942900e84970b Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 27 Dec 2024 14:14:53 +0100 Subject: [PATCH 9/9] Small audit trail fix --- lib/Service/ObjectService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 5604e3e..32a6190 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -1169,7 +1169,7 @@ public function getAuditTrail(string $id, ?int $register = null, ?int $schema = $object = $this->find($id); // @todo this is not working, it fails to find the logs - $auditTrails = $this->objectAuditLogMapper->findAll(null, null, ['object_id' => (int) $object->getId()]); + $auditTrails = $this->auditTrailMapper->findAll(filters: ['object' => $object->getId()]); return $auditTrails; }