Schemas

A schema defines both the data contained and the behavior (in the form of a state machine) of the documents it holds. A Schema object is identified within the Data Service by a unique identifier (id) and contains a name (3-50 characters) and description (max 100 characters). A schema is based on:

  • Permissions that determine who can access what documents,

  • Statuses which define the state of the document,

  • Properties which define the structure of the document,

  • Transitions which can trigger actions

Create a new schema

const mySchema = await exh.data.schemas.create({
    name: 'myNewSchema',
    description: 'This is my new schema',
});

Data Access Management

In this section we discuss how you can configure access to documents. A schema contains specific attributes that define the conditions which must be met to create, view, update or delete documents.

Access via permissions

Regardless of how the access modes (described below) are set, a user is always able to perform an operation on a document if they are assigned a specific permission. This permission can come from a global role of the user or a staff enlistment role the user has in the group of the document.

Action
Permission

Create a document

CREATE_DOCUMENTS

CREATE_DOCUMENTS:{schemaName}

View a document

VIEW_DOCUMENTS

VIEW_DOCUMENTS:{schemaName}

Update a document (Includes transitioning a document)

UPDATE_DOCUMENTS

UPDATE_DOCUMENTS:{schemaName}

Transition a document

TRANSITION_DOCUMENTS

TRANSITION_DOCUMENTS:{schemaName}

TRANSITION_DOCUMENTS:{schemaName}:{transitionName}

Delete a document

DELETE_DOCUMENTS

DELETE_DOCUMENTS:{schemaName}

Update linked users and groups (Only as a global permission)

UPDATE_ACCESS_TO_DOCUMENT

UPDATE_ACCESS_TO_DOCUMENT:{schemaName}

Access via schema access modes

Access Mode Properties

Schemas allow you to set any of the following properties to control who has access to perform certain actions on the documents for the schema.

Mode
Action

createMode

Defines who has access to create documents for the schema

readMode

Defines who has access to view documents for the schema

updateMode

Defines who has access to update documents for the schema (this includes transitioning)

deleteMode

Defines who has access to delete documents permanently from the schema

Access Mode Values

Access mode values can either be a general access mode or a combination of relational access mode values as shown in the code snipped below.

{
    "name": "myNewSchema",
    "description": "This is my new schema",
    "createMode": "allUsers",
    "readMode": ["linkedGroupPatients", "linkedGroupStaff"],
    "updateMode": ["creator", "linkedUsers"],
    "deleteMode": "permissionRequired"
}

General access mode values

The general access mode values determine if a user requires permission to perform the action for the Schema. A general access mode value is provided as one of the following strings.

General Access Mode Value
Description

"permissionRequired"

Only users with the correct permission have the ability to perform the action.

"allUsers"

All users will have the permission to perform the action on the documents in the schema collection. Regardless of their role or their permissions.

Relational access mode values

The relational access mode values determine if a user has the correct relation to the document to perform the action.

Relational access mode values are supplied as an array. When multiple relational access mode values are supplied, a user adhering to any relation in this array is allowed to perform the action on the document.

Relational Access Mode Value
Description

["creator"]

The user that created the document can perform the action.

["linkedUsers"]

All users where their user id is in the list of userIds of the document can perform the action.

["linkedGroupStaff"]

All users that have a staff enlistment in a group that is in the list of groupIds of the document can perform the action.

["linkedGroupPatients"]

All users that have a patient enlistment in a group that is in the list of groupIds of the document can perform the action.

Access Mode Compatibility Matrix

The following table shows the supported access mode values per each access mode.

createMode
readMode
updateMode
deleteMode

"permissionRequired"

"allUsers"

["creator"]

["linkedUsers"]

["linkedGroupPatients"]

["linkedGroupStaff"]

Legacy Access Modes

The following access modes are deprecated

Listed below are the deprecated values with their current equivalent

createMode

Legacy Access Mode
Access Mode

"default"

"allUsers"

readMode

Legacy Access Mode
Access Mode

"default"

["linkedUsers","linkedGroupStaff"]

"enlistedInLinkedGroups"

["linkedGroupPatients","linkedGroupStaff"]

updateMode

Legacy Access Mode
Access Mode

"default"

["linkedUsers","linkedGroupStaff"]

"creatorOnly"

["creator"]

"disabled"

"permissionRequired"

"linkedGroupsStaffOnly"

["linkedGroupStaff"]

deleteMode

Legacy Access Mode
Access Mode

"linkedUsersOnly"

["linkedUsers","linkedGroupStaff"]

Creating a schema

You can provide a name, description and the access modes upon creation of a schema:

await exh.data.schemas.create({
    name: 'Example Schema',
    description: 'The first schema I created',
    createMode: 'allUsers',
    readMode: ['linkedGroupPatients', 'linkedGroupStaff'],
    updateMode: ['creator', 'linkedUsers'],
    deleteMode: 'permissionRequired'
});

Properties

A Schema defines the structure of a document through properties. The Properties object contains type configurations, which represent the fields that should be accepted while creating or updating a document. The structure of the type configurations themselves is inspired by JSON Schema.

The Data Service supports five kinds of configurations (type attribute):

Type
Description

object

A container object to define multiple objects, in which each object must meet the schema defined in the properties attribute

array

An array object in which each value must meet the schema defined by the items attribute

string

A string object of which each value must meet the schema defined by the pattern, format, enum and/or const attributes,

number

A number object of which each value must meet the schema defined by the enum or const attribute,

boolean

A boolean object of which each value must meet the schema defined by the enum or const attribute.

{
  type: 'object',
  properties: {
    my_custom_field: { type: 'string' },
  },
  additionalProperties: { type: 'string' },
  required: ['my_custom_field'],
}

References:

Adding properties to your schema

await exh.data.properties.create(mySchema.id, {
  name: 'myFirstProperty',
  configuration: {
    type: 'string',
    enum: ['firstValue', 'secondValue'],
  }
});

Updating properties

You can update a specific property of a schema by providing the schema Id, the property path and the the json schema field configuration. The Property path makes use of the dot notation in case you are working with nested fields.

await exh.data.properties.update(
  mySchema.id,
  'myObjectField.myStringField',
  { type: 'string' }
);

Removing properties

You can remove a specific property of a schema by providing the schema Id, the property path and the the json schema field configuration. The Property path makes use of the dot notation in case you are working with nested fields.

await exh.data.properties.remove(
  mySchema.id,
  'myObjectField.myStringField',
  { type: 'string' }
);

Some examples

you can also make more complex objects and array's of objects.

await exh.data.properties.create(mySchema.id, {
  name: 'address',
  configuration: {
    type: 'object',
    properties: {
      street: {
        type: 'string',
        minLength: 1,
        pattern: '^[a-z]+$'
      },
      number: {
        type: 'number',
        minimum: 1,
        maximum: 300
      },
      inhabited: {
        type: 'boolean',
        enum: [true, false]
      },
      residents: {
        type: 'array',
        items: { type: 'string' },
        minItems: 1,
        maxItems: 10
      }
    }
  }
});

All attributes required to compose the type configurations, can be found in the API reference documentation and on the JSON Schema webpage.

Statuses

A document can be perceived as a finite-state machine, which remains in a state/status until a transition occurs. You can define a set of statuses for your document based on the expected workflow you want to build.

await exh.data.statuses.create(mySchema.id, {
    name: 'initialStatus',
});

CreationTransition

The creation transition is the transition that is executed when you create a document. It is the only type of transition that doesn't have a fromStatuses field as there is no status to start from.

When you create a new schema, by default the Data Service will include a status named "new" and a creation transition towards that status.

await exh.data.transitions.updateCreation(mySchema.id, {
  type: 'manual',
  toStatus: 'initialStatus',
  conditions: [...],
  actions: [...],
  afterActions: [...]
});

Currently the only supported transition type is manual. Other types may be added in the future.

Transitions

When you want to add more statuses to your document you will need to define transitions that allow you to move your document from one status to another. Normal transitions look the same as a creationTransition but these do include two additional parameters fromStatusesand name.

A Transition occurs from one Status to another. The Statuses a Transition is able to starts from are determined in the fromStatuses array, and the Status the Transition leads to is determined in the toStatus attribute.

await exh.data.transitions.updateCreation(mySchema.id, {
  name: 'firsTransition'
  type: 'manual',
  fromStatuses: ['initialStatus'],
  toStatus: 'secondStatus',
  conditions: [...],
  actions: [...],
  afterActions: [...]
});

A Transition object is identified by its name (name) and has a specific type assigned:

Types

Type
Description

Manual

A manual transition will be triggered when the transition execution endpoint is called on the document.

Automatic

An automatic transition will trigger when its conditions are met. E.g. when a document is transitioned to status A the data service will look for any automatic transitions that have status A mentioned in fromStatuses. If the conditions of that transition are met it will execute. If not, the Data Service will go to the next automatic transition in line.

The sequence of the transitions will depend on the sequence of configuration.

Transition conditions

Conditions need to be met before a transition can occur. There are three types of conditions which apply to the creationTransition and Transitions with the type manual:

Type
Description

input

The transition data must match a desired form, as specified by the type configurations in the configuration attribute.

initiatorHasRelationToUserInData

The initiator of the Transition has a specified relation (as determined in relation) to a user (as determined in userIdField) mentioned in the transition data

initiatorHasRelationToGroupInData

The initiator of the Transition has a specified relation (as determined in relation) to a group (as determined in groupIdField) mentioned in the transition data

There is an additional condition which applies to all Transitions:

Type
Description

document

The content of a document must match a desired form, as specified by the type configurations in the configuration attribute

Input Condition

The transition data must match a desired form, as specified by the type configurations in the configuration attribute.

This Condition only applies to the creationTransition and Transitions with type manual.

Example

The following code creates a manual transition requiring:

  • The name in the input to be a string

  • The name in the input to exist

await exh.data.transitions.create(mySchema.id, {
  ...,
  conditions: [
    {
      type: 'input',
      configuration: {
        type: 'object',
        properties: {
          name: {
            type: 'string',
          },
        },
        required: ['name'],
      },
    },
  ],
  ...
});

initiatorHasRelationToUserInData Condition

The initiator of the Transition has a specified relation (as determined in relation) to a user (as determined in userIdField) mentioned in the transition data.

The only valid value of the relation field is isStaffOfTargetPatient. The initiator must have at least one staff enlistment for the same group the targeted user has a patient enlistment.

This Condition only applies to the creationTransition and Transitions with type manual.

Example

The following code creates a manual transition for which:

  • The targeted user is determined by the myPatientId data field.

  • The initiator of the transition must be a staff member from the same group to which the targeted user is a patient.

await exh.data.transitions.create(mySchema.id, {
  ...,
  conditions: [
    {
      type: 'initiatorHasRelationToUserInData',
      userIdField: 'myPatientId',
      relation: 'isStaffOfTargetPatient',
    },
  ],
  ...
});

initiatorHasRelationToGroupInData Condition

The initiator of the Transition has a specified relation (as determined in relation) to a group (as determined in groupIdField) mentioned in the transition data.

The value for the relation field can either be:

  • staff: The initiator must have a staff enlistment for the target group

  • patient: The initiator must have a patient enlistment for the target group

The optional field requiredPermission is only used when the relation is set to staff. When supplied the initiator must not only have a staff enlistment with the targeted group, but also a role assigned within that group with the specified permission.

This Condition only applies to the creationTransition and Transitions with type manual.

Example

The following code creates a manual transition for which:

  • The targeted group is determined by the myGroupId data field.

  • The initiator of the transition must be a staff member of the targeted group.

  • The initiator of the transition must have the MY_OWN_PERMISSION group permission.

await exh.data.transitions.create(mySchema.id, {
  ...,
  conditions: [
    {
      type: 'initiatorHasRelationToGroupInData',
      groupIdField: 'myGroupId',
      relation: 'staff',
      requiredPermission: 'MY_OWN_PERMISSION',
    },
  ],
  ...
});

Document Condition

The content of the existing document must match a desired form, as specified by the type configurations in the configuration attribute.

This Condition applies to all transitions.

Example

The following code creates a manual transition requiring:

  • The name in the document data to be a string

  • The name in the document data to exist

await exh.data.transitions.create(mySchema.id, {
    ...,
    conditions: [
      {
        type: 'document',
        configuration: {
          type: 'object',
          properties: {
            data: {
              type: 'object',
              properties: {
                name: {
                  type: 'string',
                },
              },
              required: ['name'],
            },
          },
          required: ['data'],
        },
      },
    ],
  });

Transition actions

You can attach actions to transitions. This way when a transition is executed and its conditions are met it will also trigger the action. In case of a Creation Transition, the action will be executed during the creation of the document.

Modifying the document

Action Type
Description

set

Change the value of a specific field.

unset

Remove one or multiple fields.

addItems

Add values to an array field.

removeItems

Removes values from an array field

To access an element in an array or embedded documents, use the dot notation.

code examples

await exh.data.transitions.create(mySchema.id, {
  ...,
  actions: [
    {
      type: 'set',
      field: 'myField',
      value: 'myValue'
    }
  ],
  ...
});

Modifying document access

Each document has a userIds and groupIds field. These field are part of determining the access policy towards that specific document depending on the general collection schema configuration.

Using actions you can modify these fields and therefore the access of the document.

Action Type
Description

linkCreator

Add the creatorId to the userIds of the document

linkUserFromData

Add a user id found in data of the document to the userIds of the document

linkEnlistedGroups

Add all groups where the creator of the document has a patient enlistment for to the groupIds of the document

linkGroupFromData

Add a group id found in data of the document to the groupIds field of the document

If you like to modify the access to documents from outside the data service you can perform access modification functions on the documents itself. Read the documentation here: Data Access Management

code examples

await exh.data.transitions.create(mySchema.id, {
  ...,
  actions: [
    {
      type: 'linkCreator',
    }
  ],
  ...
});

Other actions

Action Type

Description

task

trigger the creation of a Task in the Task Service. Specify the functionName, the priority (optional: setting the priority of the Task in the Task Service) and optionally extra data as key-value pairs in the data variable.

code examples

await exh.data.transitions.create(mySchema.id, {
  ...,
  actions: [
    {
      type: 'task',
      functionName: 'myTaskServiceFunctionName',
      priority: 1,
      data: {
        ...inputFields,
      }
    }
  ],
  ...
});

After actions

Next to the list of actions you can also choose to define an action in the afterActions field.

Actions in the actions list are executed during the transition process. An after action however, only starts once a transition is fully completed (including potential automatic follow-up transitions). Since modifying a document during the transition process is not permitted, afterActions might be useful for triggering a task action that needs to perform additional modification to the document.

Currently only the task action is supported as an after action.

code examples

await exh.data.transitions.create(mySchema.id, {
  ...,
  afterActions: [
    {
      type: 'task',
      functionName: 'myTaskServiceFunctionName',
    }
  ],
  ...
});

Indexes

The Index object is identified by an id and a name. An index is set on a specific property in a Schema. This property is defined in the Fields object by the name and type attribute. The index is tailored with the following attributes:

Attribute
Description

background

A boolean value to determine whether the index must be built in the background; meaning it will take longer but active read and write operations are prioritized.

unique

A boolean value to determine whether the index must be unique

sparse

A boolean value to determine whether the index must be sparse

system

A boolean value to determine whether the index must be

Other settings

groupSyncMode

The groupIds field allows to define which groups should get access to the document. The users that have a staff enlistment in a group that is in the groupIds field will get access to the document. The level of access depends on the readMode, writeMode, updateMode and deleteMode values.

When a groupSyncMode is configured, the data service will automatically update the groupIds field of documents in the schema depending on changes in the user service. The table below lists the different options.

Mode
Description

disabled

The groupIds field is not automatically updated.

creatorPatientEnlistments

All the groups where the user that created the document (creatorId) is enlisted as a patient will be automatically added to the groupIds field.

linkedUsersPatientEnlistments

The groups that contain the users defined in the userIds as a patient are added to the groupIds field.

The synchronization is applied retroactively. It will be applied to all documents in a schema. That includes the documents that were created before configuring this setting and the documents that were created before the user was added to a group.

Example 1 • Allow doctors to access measurements created by patients

When you define a measurements schema, the patients will be the users creating the measurement documents. The creatorId of a measurement will be set to the patient userId.

To automatically give the appropriate doctors access to the measurements of their patients, you can set the groupSyncMode to creatorPatientEnlistments.

Example 2 • Allow doctors to read lab results of patients

Consider the use case where patients can have their saliva tested at kiosks that are deployed in pharmacies. When patients provide a specimen in the kiosk, the kiosk will process the specimen and upload the results. The corresponding document is created by the kiosk, thus creatorId of the document will not correspond to the userId of the patient.

To give doctors access to the results, set the groupSyncMode to linkedUsersPatientEnlistments and configure the kiosk to add the userId of the patient in the userIds field of the document.

Code Example

await exh.data.schemas.update(mySchema.id, {
    groupSyncMode: 'creatorPatientEnlistments',
});

defaultLimit & maximumLimit

The defaultLimit and maximumLimit refer to the number of documents that are returned to you when you execute a query. Pagination is crucial in keeping you API fast and responsive. By default the defaultLimit is set to 20 documents and the maximumLimit is set to 100 documents. We recommend to keep these settings. Only in very specific use cases would it be reasonable to change these settings.

setting

defaultLimit

Defaults to 20. Determines the number of documents returned when no RQL limit parameter is provided.

maximumLimit

Defaults to 100. Determines the number of documents returned when an RQL limit parameter is provided and the maximumLimit is crossed.

await exh.data.schemas.update(mySchema.id, {
  defaultLimit: 20,
  maximumLimit: 200,
});

Removing a schema

You can only remove disabled schema's. Removing a schema removes all documents in that schema collection. Removed documents are non retrievable.

await exh.data.schemas.remove(mySchema.id);

Enabling/disabling a schema

As the removal of a schema is something that you want to handle with great care the service first requires you to disable a schema. When a schema is disabled creation of new documents is disabled and no changes are allowed to existing documents.

await exh.data.schemas.disable(mySchema.id);

You can enable a disabled schema the same way:

await exh.data.schemas.enable(mySchema.id);

Last updated

Was this helpful?