|
|
|
[[Home]]
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
# What is the universe?
|
|
|
|
|
|
|
|
The universe holds detailed information about the database model and
|
|
|
|
the api, based on a user's permissions.
|
|
|
|
|
|
|
|
This means that it might look different for every user of a system.
|
|
|
|
|
|
|
|
The first call to retrieve the universe is relatively slow as it needs
|
|
|
|
to get generated taking all of the user's permissions into account, after
|
|
|
|
that it is cached and the call will be very fast and inexpensive.
|
|
|
|
|
|
|
|
The cache gets invalidated if the underlying model changes.
|
|
|
|
|
|
|
|
# Structure
|
|
|
|
|
|
|
|
The universe has this basic structure:
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"version" : "ca75c33bc98c97c9bc17334c6b79b0709bd1e753f3067338bc49d9eaa67f38e4",
|
|
|
|
"plugins" : { },
|
|
|
|
"apiSettings" : { },
|
|
|
|
"clientSettings" : { },
|
|
|
|
"model" : { },
|
|
|
|
"queryParameters": { },
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## version
|
|
|
|
|
|
|
|
Each version is unique for the database model.
|
|
|
|
|
|
|
|
## plugins
|
|
|
|
|
|
|
|
All plugins available to the user are listed and fully defined in this section.
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"plugins": {
|
|
|
|
"password_reset_token": {
|
|
|
|
"name": "password_reset_token",
|
|
|
|
"invocationUrl": "/plugins/password_reset_token",
|
|
|
|
"description": "password_reset_token",
|
|
|
|
"requestBodyExample": {
|
|
|
|
"email": "noreply@example.com"
|
|
|
|
},
|
|
|
|
"invocationVerb": "POST",
|
|
|
|
"requestBodyContentType": "application/json; charset=UTF-8",
|
|
|
|
"jjvInputValidator": {
|
|
|
|
"additionalProperties": false,
|
|
|
|
"type": "object",
|
|
|
|
"required": [
|
|
|
|
"email"
|
|
|
|
],
|
|
|
|
"properties": {
|
|
|
|
"email": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## clientSettings
|
|
|
|
|
|
|
|
These are settings for the current user, as set in the database.
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"clientSettings": {
|
|
|
|
"thousandSeparator": ",",
|
|
|
|
"loginName": "root",
|
|
|
|
"isSystemLanguage": true,
|
|
|
|
"lastName": "root",
|
|
|
|
"dateFormat": "MM/DD/YYYY",
|
|
|
|
"decimalSeparator": ".",
|
|
|
|
"firstName": "root",
|
|
|
|
"languageIsoCode": "en",
|
|
|
|
"languageName": "English",
|
|
|
|
"isRightToLeft": false,
|
|
|
|
"emailAddress": "noreply@example.com",
|
|
|
|
"timeFormat": "mm:ss"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## model
|
|
|
|
|
|
|
|
The main part of the universe. It lists all modules, their entities with
|
|
|
|
attributes and possible [[Transitions]]. It also provides the urls for
|
|
|
|
possible [[Queries]].
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"model": {
|
|
|
|
"system": {
|
|
|
|
"name": "system",
|
|
|
|
"label": "system"
|
|
|
|
"isUserDefinedModule": false,
|
|
|
|
"entities": {
|
|
|
|
"client": {
|
|
|
|
"id": 10,
|
|
|
|
"name": "client",
|
|
|
|
"label": "client",
|
|
|
|
|
|
|
|
"readOnly": false,
|
|
|
|
"isUserDefinedEntity": false,
|
|
|
|
"estimatedCount": 1,
|
|
|
|
"isHuge": false
|
|
|
|
"isTree": false,
|
|
|
|
"workflowVerb": null,
|
|
|
|
"workflowUrl": null,
|
|
|
|
|
|
|
|
"queryVerb": "POST",
|
|
|
|
"queryUrl": "/data/system/client/query",
|
|
|
|
"instanceRetrievalVerb": "GET",
|
|
|
|
"instanceRetrievalUrl": "/data/system/client/{{id}}",
|
|
|
|
"instanceQueryVerb": "POST",
|
|
|
|
"instanceQueryUrl": "/data/system/client/{{id}}/query",
|
|
|
|
"instanceCountVerb": "GET",
|
|
|
|
"instanceCountUrl": "/data/system/client/count",
|
|
|
|
"instanceLogVerb": "POST",
|
|
|
|
"instanceLogUrl": "/data/system/client/{{id}}/log",
|
|
|
|
|
|
|
|
"auto_ident": {
|
|
|
|
"columns": [
|
|
|
|
"me.login_name"
|
|
|
|
],
|
|
|
|
"concat": "concat_ws('/',me.login_name)",
|
|
|
|
"join": null
|
|
|
|
},
|
|
|
|
"attributes": {
|
|
|
|
"last_name": {
|
|
|
|
"referenceDetails": null,
|
|
|
|
"isReference": false,
|
|
|
|
"baseDataType": "text",
|
|
|
|
"dataType": "text",
|
|
|
|
"id": 58,
|
|
|
|
"isSystemAttribute": false,
|
|
|
|
"label": "last_name",
|
|
|
|
"description": "last name of client",
|
|
|
|
"displayOrder": 3,
|
|
|
|
"required": true,
|
|
|
|
"name": "last_name"
|
|
|
|
},
|
|
|
|
"modifying_action": {
|
|
|
|
"displayOrder": 253,
|
|
|
|
"required": true,
|
|
|
|
"name": "modifying_action",
|
|
|
|
"dataType": "bigint",
|
|
|
|
"id": 253,
|
|
|
|
"isSystemAttribute": true,
|
|
|
|
"label": "modifying_action",
|
|
|
|
"description": "reference to the last performed transition",
|
|
|
|
"referenceDetails": {
|
|
|
|
"resolveVerb": "GET",
|
|
|
|
"targetEntity": "action",
|
|
|
|
"resolveUrl": "/data/system/action/{{modifying_action}}",
|
|
|
|
"targetModule": "system",
|
|
|
|
"targetAttribute": "id"
|
|
|
|
},
|
|
|
|
"isReference": true,
|
|
|
|
"baseDataType": "bigint"
|
|
|
|
},
|
|
|
|
...
|
|
|
|
},
|
|
|
|
"transitions": {
|
|
|
|
"delete": {
|
|
|
|
"invocationUrl": "/data/system/client/{{id}}/transitions/delete",
|
|
|
|
"id": 32,
|
|
|
|
"requestBodyContentType": "application/json; charset=UTF-8",
|
|
|
|
"label": "delete",
|
|
|
|
"description": "A transition that deletes an instance",
|
|
|
|
"requestBody": {
|
|
|
|
"successor": "{{successor}}"
|
|
|
|
},
|
|
|
|
"preStateID": null,
|
|
|
|
"postState": null,
|
|
|
|
"name": "delete",
|
|
|
|
"invocationVerb": "DELETE",
|
|
|
|
"attributes": {
|
|
|
|
"successor": {
|
|
|
|
"name": "successor",
|
|
|
|
"required": false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"preState": null,
|
|
|
|
"postStateID": null,
|
|
|
|
"category": "delete"
|
|
|
|
},
|
|
|
|
...
|
|
|
|
},
|
|
|
|
...
|
|
|
|
},
|
|
|
|
...
|
|
|
|
},
|
|
|
|
...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## queryParameters
|
|
|
|
|
|
|
|
This section defines which parameters are available for the different
|
|
|
|
queries.
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"queryParameters": {
|
|
|
|
"instanceLogParameters": {
|
|
|
|
"page_size": {
|
|
|
|
"description": "size of page",
|
|
|
|
"dataType": "integer",
|
|
|
|
"name": "page_size"
|
|
|
|
},
|
|
|
|
"page": {
|
|
|
|
"name": "page",
|
|
|
|
"description": "which page",
|
|
|
|
"dataType": "integer"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"entityQuery": {
|
|
|
|
"id": {
|
|
|
|
"description": "id of saved queries in extensions.query entity.",
|
|
|
|
"dataType": "bigint",
|
|
|
|
"name": "id"
|
|
|
|
},
|
|
|
|
"context": {
|
|
|
|
"name": "context",
|
|
|
|
"dataType": "json",
|
|
|
|
"example": {
|
|
|
|
"attribute": "entity",
|
|
|
|
"module": "extensions",
|
|
|
|
"transition": "add",
|
|
|
|
"entity": "query"
|
|
|
|
},
|
|
|
|
"description": "Entity, transition and attribute defining the context the entity should be filtered with."
|
|
|
|
},
|
|
|
|
...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## apiSettings
|
|
|
|
|
|
|
|
Parameters set in the api which are relevant to using the api.
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"apiSettings": {
|
|
|
|
"version": "1.0",
|
|
|
|
"isHugeLimit": 1000000,
|
|
|
|
"maxPageSize": "1000"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
# Retrieve a users' universe
|
|
|
|
|
|
|
|
GET /universe
|
|
|
|
|
|
|
|
```
|
|
|
|
#!json
|
|
|
|
{
|
|
|
|
"version": "ca75c33bc98c97c9bc17334c6b79b0709bd1e753f3067338bc49d9eaa67f38e4",
|
|
|
|
"plugins": [
|
|
|
|
{
|
|
|
|
"jjvInputValidator": {
|
|
|
|
"additionalProperties": true,
|
|
|
|
"type": "object"
|
|
|
|
},
|
|
|
|
"invocationVerb": "POST",
|
|
|
|
"name": "generate_uuid",
|
|
|
|
"invocationUrl": "/plugins/generate_uuid",
|
|
|
|
"requestBodyExample": {},
|
|
|
|
"description": "This function returns a generated UUID"
|
|
|
|
},
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
Use the "from-db" option to discard the cache.
|
|
|
|
|
|
|
|
GET /universe?from-db=1
|
|
|
|
|
|
|
|
To only get the current version of the universe:
|
|
|
|
|
|
|
|
GET /universe/version
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"version": "b6c10140387bce47343af695803c0bb7b63304f040ec4a5c7acfc96ff97fe107"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that this reply, unlike the universe retrieval is not cached.
|
|
|
|
It can be used to check if the cached version is the most current version or if
|
|
|
|
the uncaching has failed for some reason. It is a fairly expensive query for the database though.
|
|
|
|
|
|
|
|
To get a specific version of a users' universe:
|
|
|
|
|
|
|
|
GET /universe/b6c10140387bce47343af695803c0bb7b63304f040ec4a5c7acfc96ff97fe107
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"clientSettings": {
|
|
|
|
"languageIsoCode": "en",
|
|
|
|
"timeFormat": "mm:ss",
|
|
|
|
"emailAddress": "noreply@example.com",
|
|
|
|
"languageName": "English",
|
|
|
|
"firstName": "root",
|
|
|
|
"lastName": "root",
|
|
|
|
"isRightToLeft": false,
|
|
|
|
"dateFormat": "MM/DD/YYYY",
|
|
|
|
"isSystemLanguage": true,
|
|
|
|
"thousandSeparator": ",",
|
|
|
|
"loginName": "root",
|
|
|
|
"decimalSeparator": "."
|
|
|
|
},
|
|
|
|
"plugins": [
|
|
|
|
{
|
|
|
|
"description":
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
Again, this is not cached and asks the database.
|
|
|
|
|
|
|
|
# estimated count and isHuge flag
|
|
|
|
|
|
|
|
Every entity entry in the universe has an "estimatedCount" flag, and derived from it (based on an API config variable),
|
|
|
|
"isHuge".
|
|
|
|
|
|
|
|
For any entity that "isHuge" the following restrictions apply:
|
|
|
|
|
|
|
|
* joining a 1:n relation is not allowed
|
|
|
|
* searching can only be done on indexed attributes
|
|
|
|
* the count for a filter result will be an estimate only
|
|
|
|
|
|
|
|
So, this means that for a not "isHuge" entity:
|
|
|
|
|
|
|
|
* joining a 1:n relation is allowed
|
|
|
|
* all attributes are searchable, independant of indexing
|
|
|
|
* the count for a filter result will be an accurate count
|
|
|
|
|
|
|
|
|
|
|
|
# Listing URL
|
|
|
|
|
|
|
|
To get instances in store.product, use the information to send a query:
|
|
|
|
|
|
|
|
```JSON
|
|
|
|
{
|
|
|
|
"queryUrl": "/data/store/product/query",
|
|
|
|
"queryVerb": "POST"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Look into the [[Queries]] documentation for more details.
|
|
|
|
|
|
|
|
# Transitions
|
|
|
|
|
|
|
|
The transitions for an entity in the universe lists all transitions that may be available for the user.
|
|
|
|
Depening
|
|
|
|
on the state and acls of an instance, only a subset may actually be available for an instance. This
|
|
|
|
is the endpoint to query which are actually available:
|
|
|
|
|
|
|
|
GET /data/{{module}}/{{entity}}/{{id}}/transitions
|
|
|
|
|
|
|
|
e.g. GET /data/system/entity/1/transitions
|
|
|
|
|
|
|
|
```JSON
|
|
|
|
{
|
|
|
|
"data": [
|
|
|
|
{
|
|
|
|
"id": 6,
|
|
|
|
"description": "action that modifies an instance",
|
|
|
|
"modifying_client": 1,
|
|
|
|
"successor": null,
|
|
|
|
"action_type": 2,
|
|
|
|
"modification_time": "2018-06-04 10:25:32.7335",
|
|
|
|
"label": "edit",
|
|
|
|
"instance_entity": 6,
|
|
|
|
"modifying_action": 13,
|
|
|
|
"notification": null,
|
|
|
|
"name": "edit",
|
|
|
|
"entity": 3,
|
|
|
|
"binding_entity": null
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"modifying_client": 1,
|
|
|
|
"successor": null,
|
|
|
|
"id": 7,
|
|
|
|
"description": "action that explicitly confirms the retrieval of an instance. It does not return any instance data rather than the success of the confimation.",
|
|
|
|
"action_type": 2,
|
|
|
|
"modification_time": "2018-06-04 10:25:32.733506",
|
|
|
|
"modifying_action": 13,
|
|
|
|
"notification": null,
|
|
|
|
"label": "read",
|
|
|
|
"instance_entity": 6,
|
|
|
|
"binding_entity": null,
|
|
|
|
"name": "read",
|
|
|
|
"entity": 3
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"entity": 3,
|
|
|
|
"name": "delete",
|
|
|
|
"binding_entity": null,
|
|
|
|
"label": "delete",
|
|
|
|
"instance_entity": 6,
|
|
|
|
"modifying_action": 13,
|
|
|
|
"notification": null,
|
|
|
|
"action_type": 3,
|
|
|
|
"modification_time": "2018-06-04 10:25:32.733512",
|
|
|
|
"id": 8,
|
|
|
|
"description": "action that deletes an instance",
|
|
|
|
"modifying_client": 1,
|
|
|
|
"successor": null
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that this is a very expensive query to run, so must not be used without thinking first.
|
|
|
|
|
|
|
|
Each transition has a "category". If the category is "create"
|
|
|
|
you can display a button [+] which leads to the form for creating an instance of the given entity.
|
|
|
|
|
|
|
|
# References
|
|
|
|
|
|
|
|
Looking at system.entity we see that modifying_client is a reference:
|
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"attributes": {
|
|
|
|
...
|
|
|
|
"modifying_client": {
|
|
|
|
"description": "reference to the client who performed the last change",
|
|
|
|
"id": 219,
|
|
|
|
"widgetName": "bigint",
|
|
|
|
"required": true,
|
|
|
|
"label": "modifying_client",
|
|
|
|
"dataType": "bigint",
|
|
|
|
"name": "modifying_client",
|
|
|
|
"isReference": true,
|
|
|
|
"referenceDetails": {
|
|
|
|
"targetModule": "system",
|
|
|
|
"resolveUrl": "/data/system/client/{{modifying_client}}",
|
|
|
|
"targetEntity": "client",
|
|
|
|
"targetAttribute": "id",
|
|
|
|
"resolveVerb": "GET"
|
|
|
|
},
|
|
|
|
"isSystemAttribute": true,
|
|
|
|
"displayOrder": 219
|
|
|
|
},
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Now there are various ways to get the data for this reference.
|
|
|
|
|
|
|
|
1. use default instance query
|
|
|
|
|
|
|
|
The default instance query will automatically join 1:1 relations:
|
|
|
|
|
|
|
|
POST /data/system/entity/1/query
|
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"modification_time": "2018-06-04 10:25:32.728015",
|
|
|
|
"modifying_client": {
|
|
|
|
"locked": null,
|
|
|
|
"email": "noreply@example.com",
|
|
|
|
"language": 1,
|
|
|
|
"modifying_action": 29,
|
|
|
|
"modification_time": "2018-06-04 10:25:32.748609",
|
|
|
|
"password_validation_period": null,
|
|
|
|
"first_name": "root",
|
|
|
|
"password": "15f387d6d56522516a14affa99be6e02",
|
|
|
|
"number_of_unsuccessfull_login_attempts": null,
|
|
|
|
"login_name": "root",
|
|
|
|
"id": 1,
|
|
|
|
"successor": null,
|
|
|
|
"modifying_client": 1,
|
|
|
|
"last_name": "root"
|
|
|
|
},
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
2. use custom instance query
|
|
|
|
|
|
|
|
Let's just get first name and last name of the referenced client.
|
|
|
|
|
|
|
|
POST /data/system/entity/1/query
|
|
|
|
```
|
|
|
|
{"attrs" : { "join" : "modifying_client", "+columns" : ["modifying_client.first_name","modifying_client.last_name"] } }
|
|
|
|
```
|
|
|
|
|
|
|
|
```JSON
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"modification_time": "2018-06-04 10:25:32.728015",
|
|
|
|
"modifying_client": {
|
|
|
|
"last_name": "root",
|
|
|
|
"first_name": "root"
|
|
|
|
},
|
|
|
|
"name": "data_type",
|
|
|
|
"module": 2,
|
|
|
|
"is_successor_not_required": 0,
|
|
|
|
"modifying_action": 5,
|
|
|
|
"label": "data_type",
|
|
|
|
"is_functional": 0,
|
|
|
|
"instance_entity": 3,
|
|
|
|
"is_translatable": 1,
|
|
|
|
"successor": null,
|
|
|
|
"id": 1,
|
|
|
|
"description": "type of data"
|
|
|
|
},
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
3. use resolveUrl to the referenced instance with a seperate request
|
|
|
|
|
|
|
|
GET /data/system/client/1
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"language": 1,
|
|
|
|
"instance_entity": 10,
|
|
|
|
"modifying_action": 29,
|
|
|
|
"locked": null,
|
|
|
|
"email": "noreply@example.com",
|
|
|
|
"number_of_unsuccessfull_login_attempts": null,
|
|
|
|
"login_name": "root",
|
|
|
|
"id": 1,
|
|
|
|
"successor": null,
|
|
|
|
"modifying_client": 1,
|
|
|
|
"last_name": "root",
|
|
|
|
"modification_time": "2018-06-04 10:25:32.748609",
|
|
|
|
"first_name": "root",
|
|
|
|
"password_validation_period": null,
|
|
|
|
"password": "15f387d6d56522516a14affa99be6e02"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
``` |
|
|
|
\ No newline at end of file |