Objects
What is an Object?
In Open Register, an Object is an instance of data that conforms to a specific schema and is stored in a register. Objects are the actual content of your data management system - they represent real-world entities like people, products, locations, or events.
Each object:
- Has a unique identifier
- Conforms to a schema that defines its structure
- Belongs to a register that organizes related objects
- Can have relationships with other objects
- Can have attached files
- Maintains version history
Object Structure
All objects are stored as object entities, or json objects holding both the metadata and data of the actual object. These object entities are available through the objects api
. Object entities are a way of storing objects - they might be seen as an envelope for the actual object. When serialization happens the object is changed to the actual object (and the envelope moved to @self see metadata).
- Specifications
- Serialized Object
id | integer |
uuid | string |
uri | string |
version | string |
register | integer |
schema | integer |
object | object |
files | Array of strings |
relations | Array of strings |
Lock (object) or null Contains either a lock object or the value null | |
Deletion (object) or null Contains either a deletion object or the value null | |
owner | string |
{- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "uri": "/api/objects/123e4567-e89b-12d3-a456-426614174000",
- "version": "1.0",
- "register": 123,
- "schema": 123,
- "object": {
- "firstName": "John",
- "lastName": "Doe",
- "birthDate": "1980-01-15",
- "email": "john.doe@example.com",
- "address": {
- "street": "123 Main St",
- "city": "Anytown",
- "postalCode": "12345",
- "country": "USA"
}, - "phoneNumbers": [
- {
- "type": "mobile",
- "number": "555-123-4567"
}
], - "spouse": "123e4567-e89b-12d3-a456-426614174000"
}, - "files": [
- "123",
- "124"
], - "relations": {
- "spouse": "123e4567-e89b-12d3-a456-426614174000"
}, - "locked": {
- "user": "user_id",
- "process": "optional_process_name",
- "created": "timestamp",
- "duration": "seconds",
- "expiration": "timestamp"
}, - "deleted": {
- "deleted": "2023-01-01T00:00:00Z",
- "deletedBy": "user-12345",
- "deletedReason": "No longer needed",
- "retentionPeriod": 30,
- "purgeDate": "2023-01-31T00:00:00Z"
}, - "owner": "user-12345"
}
object | object |
object (@self) |
{- "object": { },
- "@self": {
- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "uri": "/api/objects/123e4567-e89b-12d3-a456-426614174000",
- "version": "1.0",
- "register": 123,
- "schema": 123,
- "textRepresentation": "John Doe, born 1980-01-15, email: john.doe@example.com",
- "locked": {
- "user": "user_id",
- "process": "optional_process_name",
- "created": "timestamp",
- "duration": "seconds",
- "expiration": "timestamp"
}, - "deleted": {
- "deleted": "2023-01-01T00:00:00Z",
- "deletedBy": "user-12345",
- "deletedReason": "No longer needed",
- "retentionPeriod": 30,
- "purgeDate": "2023-01-31T00:00:00Z"
}, - "owner": "user-12345",
- "authorization": {
- "read": true,
- "write": false
}, - "updated": "2023-05-20T10:15:00Z",
- "created": "2023-02-15T14:30:00Z",
- "folder": "/persons/john-doe",
- "files": [
- {
- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "filename": "profile.jpg",
- "extension": "jpg",
- "checksum": "abc123",
- "source": 1,
- "userId": "user-12345",
- "base64": "base64encodedstring",
- "filePath": "/files/profile.jpg",
- "created": "2023-02-15T14:30:00Z",
- "updated": "2023-05-20T10:15:00Z"
}, - {
- "id": 124,
- "uuid": "123e4567-e89b-12d3-a456-426614174001",
- "filename": "resume.pdf",
- "extension": "pdf",
- "checksum": "def456",
- "source": 1,
- "userId": "user-12345",
- "base64": "base64encodedstring",
- "filePath": "/files/resume.pdf",
- "created": "2023-02-16T14:30:00Z",
- "updated": "2023-05-21T10:15:00Z"
}
], - "relations": {
- "spouse": "123e4567-e89b-12d3-a456-426614174000"
}, - "errors": [
- "Property 'spouse' could not be extended because it does not exist."
]
}
}
Creating objects
Creating Objects
To create an object in Open Register, you need to ensure that both the register and schema are in place. An object always refers to a specific register and conforms to a defined schema.
Objects can be created using the 'POST /api/objects' endpoint. This endpoint allows you to submit a new object that adheres to the schema associated with the specified register.
Here is a brief overview of the steps to create an object:
-
Ensure Register and Schema are in Place:
- Before creating an object, make sure that the register and schema you want to use are already created and available in the system.
-
Prepare the Object Data:
- Structure your object data according to the schema defined for the register. This ensures that the object will be validated and stored correctly.
-
Make the API Request:
- Use the 'POST /api/objects' endpoint to submit your object data. The request should include the necessary information to identify the register and conform to the schema.
For detailed information on the API endpoint and how to use it, please refer to the API documentation for creating objects.
Right now objects can be added from the Objects menu item and then the "Add object option". Afhter selecting a Register and Schema an object can be added in pure json format
Object Features
Schema Validation
All objects are validated against their schema before being stored, ensuring data quality and consistency. You can read more about schema validation under schema.
Serialisation
In Open Register, objects or lists of objects can be serialized based on the header request. This means that all responses are available in both JSON and XML formats. The serialization format is determined by the Accept
header in the request.
For example:
- To receive the response in JSON format, set the
Accept
header toapplication/json
. - To receive the response in XML format, set the
Accept
header toapplication/xml
.
This flexibility allows clients to choose the most suitable format for their needs, ensuring compatibility with various systems and applications.
Metadata
Open Register keeps track of the metadata of objects, it always keeps track of the following fields whether or not they are part of the object. This data is stored in object entity but transferred to the @self property when the object is serialized.
id | integer Unique identifier for the object |
uuid | string Unique universal identifier for globally unique object identification |
uri | string Uniform Resource Identifier for unique addressable location |
version | string Semantic version number to track object versions |
register | integer Register identifier for object categorization/grouping |
schema | integer Schema identifier for data validation reference |
textRepresentation | string Text representation of object for search and display optimization |
Lock (object) or null Contains either a lock object or the value null | |
Deletion (object) or null Contains either a deletion object or the value null | |
owner | string Nextcloud user identifier for object ownership |
authorization | object Authorization rules for access control configuration |
updated | string <date-time> Last modification timestamp for change tracking |
created | string <date-time> Creation timestamp for lifecycle management |
folder | string Storage folder path for file organization |
Array of objects (File) Array of related files to track associated files | |
relations | Array of strings Array of related object IDs to track object relationships |
errors | Array of strings Array of error messages encounterd during the rendering process of this object |
{- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "uri": "/api/objects/123e4567-e89b-12d3-a456-426614174000",
- "version": "1.0",
- "register": 123,
- "schema": 123,
- "textRepresentation": "John Doe, born 1980-01-15, email: john.doe@example.com",
- "locked": {
- "user": "user_id",
- "process": "optional_process_name",
- "created": "timestamp",
- "duration": "seconds",
- "expiration": "timestamp"
}, - "deleted": {
- "deleted": "2023-01-01T00:00:00Z",
- "deletedBy": "user-12345",
- "deletedReason": "No longer needed",
- "retentionPeriod": 30,
- "purgeDate": "2023-01-31T00:00:00Z"
}, - "owner": "user-12345",
- "authorization": {
- "read": true,
- "write": false
}, - "updated": "2023-05-20T10:15:00Z",
- "created": "2023-02-15T14:30:00Z",
- "folder": "/persons/john-doe",
- "files": [
- {
- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "filename": "profile.jpg",
- "extension": "jpg",
- "checksum": "abc123",
- "source": 1,
- "userId": "user-12345",
- "base64": "base64encodedstring",
- "filePath": "/files/profile.jpg",
- "created": "2023-02-15T14:30:00Z",
- "updated": "2023-05-20T10:15:00Z"
}, - {
- "id": 124,
- "uuid": "123e4567-e89b-12d3-a456-426614174001",
- "filename": "resume.pdf",
- "extension": "pdf",
- "checksum": "def456",
- "source": 1,
- "userId": "user-12345",
- "base64": "base64encodedstring",
- "filePath": "/files/resume.pdf",
- "created": "2023-02-16T14:30:00Z",
- "updated": "2023-05-21T10:15:00Z"
}
], - "relations": {
- "spouse": "123e4567-e89b-12d3-a456-426614174000"
}, - "errors": [
- "Property 'spouse' could not be extended because it does not exist."
]
}
Relationships
Object Relations enable the creation and management of connections between objects, supporting complex data structures and relationships.
The relations system provides:
- Multiple relationship types
- Bi-directional relationships
- Relationship metadata
- Integrity management
** Key Benefits **
-
Data Organization
- Model complex relationships
- Maintain data connections
- Support hierarchical structures
-
Data Integration
- Link related information
- Create data networks
- Support cross-referencing
-
Process Management
- Track dependencies
- Manage workflows
- Support business processes
Objects can have relationships with other objects, creating a network of connected data. Relationships are stored in the relations
property as an array where the keys are dot notations referring to properties in the object, and the values are references to external objects. References can be either an id, uuid, or URL to an extended object. In this way, the relationship metadata property forms a quick index of all the objects that an object is related to. For more information on dot notation, please refer to this dot notation explanation.
{
"@self": {
"relations":{
"user":"1",
"user":"1",
}
}
.... The actual object
}
Extending
Data Extension allows you to automatically include related entities in API responses, reducing the need for multiple API calls and providing complete context in a single request. This is useful when you need to retrieve related data for a specific object or collection an lowers the number of API calls needed therby reducing the load on the server and improving performence client side.
The extend patern is based was orginally developed for the Open Catalogi project and is now available in the ObjectStore API. Its baed on the extend functionality of Zaak gericht werken but brought in line with pNLGov REST API Design Rules by adding a _ prefix to the parameter
Key Benefits of Extending
-
Efficiency
- Reduce the number of API calls
- Minimize server load
- Improve client-side performance
-
Contextual Data
- Provide complete context in a single request
- Include related entities automatically
- Simplify data retrieval
-
Flexibility
- Extend single or multiple properties
- Support nested property extensions
- Combine multiple extensions in one request
-
Consistency
- Ensure consistent data representation
- Maintain data integrity across related entities
- Simplify data management and integration
-
Scalability
- Handle complex data structures efficiently
- Support large-scale data operations
- Enhance overall system scalability
Extention patern is suported trough the objects api
Extend a single property:
?_extend=author
- Include full author object?_extend=category
- Include full category object?_extend=files
- Include file metadata
Extend nested properties:
?_extend=author.organization
- Include author with their organization?_extend=department.employees
- Include department with all employees?_extend=project.tasks.assignee
- Include project with tasks and their assignees
Combine multiple extensions:
?_extend=author,category,comments
- Include multiple related objects?_extend=files,metadata,relations
- Include all related data?_extend=all
- Include all possible relations on the root object
revertedBy
The revertedBy
property indicates which object this object was reverted from. This is part of Open Register's version control system, allowing you to track the history of object changes and reversions. For more details about how this works with schemas, see Schema Relationships.
inversedBy
The inversedBy
property represents inverse relationships between objects. It's used to maintain bidirectional relationships, where changes in one object automatically reflect in related objects. For more information about inverse relationships and how they work with schemas, see Schema Relationships.
Locking
Locking is a mechanism used to prevent concurrent editing of objects, ensuring data integrity in multi-user environments. A user (or a process on behalf of a user) might lock an object to prevent other users or processes from performing changes or deletions. This is particularly useful in scenarios such as:
- When a user is editing an object in a form, and you want to prevent use collisions.
- For BPMN processes that might take some time and cannot have their underlying data altered. However, keep in mind that for the latter example, a BPMN process could also use a specific version of an object and might run into trouble if it tries to update it later.
Locks are by default created for five minutes but can be created for any duration by supplying the duration period. Locks can be extended, but only by the user that created the lock. Locks can also be removed, but only by the user that created the lock. Locks are automatically removed if the user that created the lock performs an update or delete operation.
Key Benefits
-
Data Integrity
- Prevent concurrent modifications
- Avoid data conflicts
- Maintain consistency
-
Process Management
- Support long-running operations
- Coordinate multi-step updates
- Manage workflow dependencies
-
User Coordination
- Clear ownership indication
- Transparent lock status
- Managed access control
The Lock object is a crucial component in Open Register's locking mechanism. It is used to manage and enforce locks on objects, ensuring data integrity and preventing concurrent modifications. The Lock object contains information about the lock, such as the user who created it, the duration of the lock, and the timestamp when the lock was created.
user | string User ID that created the lock |
process | string Optional process name associated with the lock |
created | string <date-time> Timestamp when the lock was created |
duration | integer Duration of the lockin seconds |
expiration | string <date-time> Timestamp when the object expires (is autmaticly removed) |
{- "user": "user_id",
- "process": "optional_process_name",
- "created": "timestamp",
- "duration": "seconds",
- "expiration": "timestamp"
}
File Attachments
File Attachments enable objects to incorporate and manage associated files and documents seamlessly. Open Register utilizes Nextcloud's advanced file storage capabilities to offer a comprehensive and secure file management system. By integrating with Nextcloud's established infrastructure, Open Register benefits from various file handling features, including:
- Secure file storage and encryption
- File versioning and history
- Collaborative features such as sharing and commenting
- Preview generation for supported file types
- Automated virus scanning
- Support for flexible storage backends
Upon the creation of a register in Open Register, a corresponding share is automatically established in Nextcloud. Subsequently, when a schema is created, a folder is generated within that share. As objects are created, folders are established (using the UUID of the object) within the schema's folder. This structure ensures that each object has a dedicated folder, facilitating a straightforward and intuitive method for associating files with objects.
Alternatively, users can associate existing files with an object by utilizing the Nextcloud file system and tagging the file with 'object:[uuid]', where '[uuid]' represents the object's UUID. In both scenarios, there is no direct relationship between the file and a property within the object. However, the files are accessible through the object API, as file objects are included in the object metadata under the files array.
For more detailed information on file management and integration, please refer to the Files Documentation.
id | integer Unique identifier of the file in Nextcloud |
uuid | string Unique identifier for the file |
filename | string Name of the file |
downloadUrl | string <uri> Direct download URL for the file |
shareUrl | string <uri> URL to access the file via share link |
accessUrl | string <uri> URL to access the file |
extension | string File extension |
checksum | string ETag hash for file versioning |
source | integer Source identifier |
userId | string ID of the user who owns the file |
base64 | string Base64 encoded content of the file |
filePath | string Full path to the file in Nextcloud |
created | string <date-time> ISO 8601 timestamp when file was first shared |
updated | string <date-time> ISO 8601 timestamp of last modification |
{- "id": 123,
- "uuid": "123e4567-e89b-12d3-a456-426614174000",
- "filename": "profile.jpg",
- "extension": "jpg",
- "checksum": "abc123",
- "source": 1,
- "userId": "user-12345",
- "base64": "base64encodedstring",
- "filePath": "/files/profile.jpg",
- "created": "2023-02-15T14:30:00Z",
- "updated": "2023-05-20T10:15:00Z"
}
Soft Deleting
Open Register implements a soft deletion strategy for objects, ensuring data can be recovered and maintaining referential integrity.
Overview
The deletion system provides:
- Soft deletion of objects
- Retention of relationships
- Configurable retention periods
- Recovery capabilities
- Audit trail preservation
- Objects are never immediately deleted from the database
- Deletion sets the 'deleted' timestamp and related metadata
- Deleted objects are excluded from normal queries
- Relations to deleted objects are preserved
- Files linked to deleted objects are moved to a trash folder
- Deleted objects can be restored until purge date
- Objects are only permanently deleted after retention period
sequenceDiagram
participant User
participant OpenRegister
participant Nextcloud
User->>OpenRegister: Create Register
OpenRegister->>Nextcloud: Create Share
Nextcloud-->>OpenRegister: Share Created
User->>OpenRegister: Create Schema
OpenRegister->>Nextcloud: Create Folder in Share
Nextcloud-->>OpenRegister: Folder Created
User->>OpenRegister: Create Object
OpenRegister->>Nextcloud: Create Folder in Schema Folder
Nextcloud-->>OpenRegister: Folder Created
User->>Nextcloud: Upload File
Nextcloud-->>User: File Uploaded
User->>Nextcloud: Tag File with 'object:[uuid]'
Nextcloud-->>User: File Tagged
User->>OpenRegister: Retrieve Object
OpenRegister->>Nextcloud: Retrieve Files for Object
Nextcloud-->>OpenRegister: Files Retrieved
OpenRegister-->>User: Object with Files Metadata
Key Benefits
-
Data Safety
- Prevent accidental data loss
- Maintain data relationships
- Support data recovery
- Preserve audit history
-
Compliance
- Meet retention requirements
- Support legal holds
- Track deletion reasons
- Document deletion process
-
Management
- Flexible retention policies
- Controlled purge process
- Recovery options
- Clean data lifecycle
deleted | string <date-time> When the object was marked as deleted |
deletedBy | string User ID who performed the deletion |
deletedReason | string Optional reason for deletion |
retentionPeriod | integer Default: 30 How long to keep the deleted object (in days) |
purgeDate | string <date-time> When the object will be permanently deleted |
{- "deleted": "2023-01-01T00:00:00Z",
- "deletedBy": "user-12345",
- "deletedReason": "No longer needed",
- "retentionPeriod": 30,
- "purgeDate": "2023-01-31T00:00:00Z"
}
Version History
Open Register maintains a complete history of changes to objects, allowing you to track modifications over time and revert to previous versions if needed. This version history powers the Time Travel feature, which enables you to:
- View an object as it existed at any point in time
- Compare different versions to see what changed
- Restore objects to previous states
- Analyze the evolution of data over time
Time Travel
Time Travel in Open Register allows you to view and restore objects to any previous state in their history. This powerful feature enables data recovery, audit compliance, and historical analysis.
Retrieving a Specific Version
In Open Register, every change to an object results in a new version, which is recorded in the audit trail. This allows you to get an overview of all versions of an object by examining its audit trail. Each entry in the audit trail corresponds to a specific version of the object, providing a detailed history of changes over time.
To retrieve a specific version of an object directly from the API, you can use the API documentation for retrieving a specific object version.
Comparing Versions (Planned Feature)
This feature is currently a planned feature and is not yet available. We are working on implementing it in future releases.
You can compare two versions of an object to see what changed:
GET /api/objects/{id}/compare?version1=1.0&version2=1.2
Example response:
{
"id": "person-12345",
"changes": {
"lastName": {
"old": "Doe",
"new": "Smith-Johnson"
},
"email": {
"old": "john.doe@example.com",
"new": "jane.johnson@example.com"
},
"address": {
"old": {
"street": "123 Main St",
"city": "Anytown",
"postalCode": "12345",
"country": "USA"
},
"new": {
"street": "789 Pine St",
"city": "Newtown",
"postalCode": "54321",
"country": "USA"
}
}
}
}
Restoring Previous Versions
You can restore an object to a previous version:
POST /api/objects/{id}/restore
{
"version": "1.1"
}
Or restore an object as it existed at a specific point in time:
POST /api/objects/{id}/restore
{
"timestamp": "2023-03-10T09:45:00Z"
}
Audit Trails
Audit trails provide a complete history of all changes made to objects in Open Register. This feature ensures transparency and accountability by tracking who made what changes and when. The implementation follows the GEMMA Processing Logging standard. Open Register maintains immutable audit trails for objects, ensuring a permanent and unalterable record of changes. Audit trails track who made changes, when they were made, and what was changed. This feature is crucial for maintaining data integrity, security, and compliance. For detailed information on the API endpoint and how to use it, please refer to the API documentation for getting object audit trails.
The audit trail system automatically records:
- All modifications to objects
- Individual object reads (but not collection reads)
- Who made the changes
- When changes occurred
- What specific data was changed
- The reason for changes (when provided)
- Processing activities
Audit trails can be acces from the object view (itself part of the table view) select the register / schema / object that you want to audit and go to the audit trails tab in the view monolog
@todo needs immages
Read Action Logging
The system only logs read actions when accessing individual objects (e.g., GET /api/objects/123). Collection reads and search operations (e.g., GET /api/objects?name=test) are intentionally not logged for several important reasons:
-
Performance Impact
- Collection reads can return hundreds or thousands of objects
- Logging each object view would create massive amounts of audit data
- Database performance would degrade significantly
-
Storage Concerns
- The audit log would grow exponentially
- Storage costs would increase dramatically
- Valuable audit data would be diluted with less meaningful entries
-
Meaningful Tracking
- Individual object reads indicate specific interest in that object
- Collection reads are often exploratory or part of routine operations
- Focus on logging deliberate access to specific objects
uuid | string Unique identifier for the audit entry |
schema | integer Schema ID of the modified object |
register | integer Register ID of the modified object |
object | integer Object ID that was modified |
action | string Type of change that occurred |
changed | object Array of modified fields with old/new values |
user | string ID of the user who made the change |
userName | string Display name of the user |
session | string Session ID when change occurred |
request | string Request ID for tracing |
ipAddress | string IP address of the request |
version | string Object version after change |
created | string <date-time> Timestamp of the change |
processingActivity | string The processing activity from the registry |
processing | string The specific task being performed |
operation | string The step in the processing task |
legalBasis | string Legal basis for the processing |
retentionPeriod | string Retention period for the data |
executor | string The system or person executing the action |
system | string The system where the action occurred |
dataSource | string The source of the data |
{- "uuid": "550e8400-e29b-41d4-a716-446655440000",
- "schema": 42,
- "register": 123,
- "object": 456,
- "action": "create",
- "changed": {
- "name": {
- "old": "John",
- "new": "Jane"
}
}, - "user": "admin",
- "userName": "Administrator",
- "session": "sess_89d7h2",
- "request": "req_7d8h3j",
- "ipAddress": "192.168.1.1",
- "version": "1.0.0",
- "created": "2024-03-15T14:30:00Z",
- "processingActivity": "string",
- "processing": "string",
- "operation": "string",
- "legalBasis": "string",
- "retentionPeriod": "string",
- "executor": "string",
- "system": "string",
- "dataSource": "string"
}