JSON Hyper schema API¶
This page documents the JSON schema API provided by cubicweb-jsonschema
under the application/schema+json
content type.
Application schema¶
The application schema consists of hyper schema links with top-level entity types in the application.
>>> resp = client.get_schema('/schema')
>>> print(resp)
Response: 200 OK
Content-Type: application/json
Link: </>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "test app",
"type": "null",
"links" : [
{
"rel" : "collection",
"href" : "/author/",
"targetSchema" : {
"$ref" : "/author/schema"
},
"submissionSchema" : {
"$ref" : "/author/schema?role=creation"
},
"title" : "Author_plural"
},
{
"rel" : "collection",
"href" : "/book/",
"targetSchema" : {
"$ref" : "/book/schema"
},
"submissionSchema" : {
"$ref" : "/book/schema?role=creation"
},
"title" : "Book_plural"
}
]
}
Entity type schema¶
On a given entity type we get the following schema:
>>> resp = client.get_schema('/author/schema')
>>> print(resp)
Response: 200 OK
Content-Type: application/json
Link: </author/>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title" : "Author_plural",
"items" : {
"properties" : {
"type" : {
"type" : "string"
},
"id" : {
"type" : "string"
},
"title" : {
"type" : "string"
}
},
"type" : "object",
"links": [
{
"rel" : "item",
"href" : "/author/{id}",
"anchor": "#"
}
]
},
"type" : "array",
"links" : [
{
"rel" : "self",
"href" : "/author/",
"targetSchema" : {
"$ref" : "/author/schema"
},
"submissionSchema" : {
"$ref" : "/author/schema?role=creation"
},
"title" : "Author_plural"
}
]
}
>>> author_etype_hyperschema = resp.json
Hyperschema links¶
Let’s now follow one the links above, for instance to get the schema that needs to be respected in order to perform creation of an entity:
>>> self_link = author_etype_hyperschema['links'][0]
>>> resp = client.get_schema(self_link['submissionSchema']['$ref'])
>>> print(resp)
Response: 200 OK
Content-Type: application/json
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title" : "Author",
"type" : "object",
"properties" : {
"name" : {
"type" : "string",
"title" : "name"
}
},
"required" : [
"name"
],
"additionalProperties" : false
}
Entity schema¶
Before examining entity’s schema, we need an entity so let’s create one of
type Author
, by posting data at the /author/
route:
>>> r = client.post_json('/author/', {'name': 'bob'},
... headers={'Accept': 'application/json'})
>>> print(r)
Response: 201 Created
Content-Type: application/json
Location: https://localhost:80/author/.../
{
"name": "bob"
}
>>> entity_url = r.location
On our Author
entity we get the following schema:
>>> resp = client.get_schema(entity_url + '/schema')
>>> print(resp)
Response: 200 OK
Content-Type: application/json
Link: </author/.../>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title" : "Author",
"type" : "object",
"properties" : {
"name" : {
"title" : "name",
"type" : "string"
}
},
"additionalProperties" : false,
"links" : [
{
"rel" : "collection",
"href" : "/author/",
"targetSchema" : {
"$ref" : "/author/schema"
},
"title" : "Author_plural"
},
{
"rel" : "self",
"href" : "/author/.../",
"targetSchema" : {
"$ref" : "/author/.../schema?role=view"
},
"submissionSchema" : {
"$ref" : "/author/.../schema?role=edition"
},
"title" : "Author #..."
},
{
"href": "/author/.../publications/",
"rel": "related",
"title": "publications"
}
]
}
>>> author_hyperschema = resp.json
Hyperschema links¶
Let’s now follow the self
link above which indicates how to interact with
the current resource (entity). For instance, would we need to perform
edition of this resource we should send a request to self
link’s
href
URL with a payload matching the JSON Schema pointed at by schema
property of the link:
>>> self_link = author_hyperschema['links'][1]
>>> resp = client.get_schema(self_link['submissionSchema']['$ref'])
>>> print(resp)
Response: 200 OK
Content-Type: application/json
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title" : "Author",
"type" : "object",
"properties" : {
"name" : {
"type" : "string",
"title" : "name"
}
},
"required" : [
"name"
],
"additionalProperties" : false
}
On the other hand, self
link’s targetSchema
is:
>>> resp = client.get_schema(self_link['targetSchema']['$ref'])
>>> print(resp)
Response: 200 OK
Content-Type: application/json
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "Author",
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "name"
}
},
"additionalProperties": false
}
which is exactly the same schema as found in the Hyper Schema obtained above.
Entity workflow schema¶
Workflowable entity types expose the schema of their workflow transitions at
the /<etype>/<eid>/workflow-transitions/schema endpoint. We’ll work on a
UserAccount
entity type which has a simple created → activated →
deactivated workflow.
First, let’s create a UserAccount
entity by looking at what the creation
schema describes:
>>> r = client.get('/useraccount/schema?role=creation',
... headers={'Accept': 'application/schema+json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
Link: </useraccount/>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"additionalProperties": false,
"properties": {
"username": {
"title": "username",
"type": "string"
}
},
"required": [
"username"
],
"title": "UserAccount",
"type": "object"
}
>>> r = client.post_json('/useraccount/', {'username': 'tommy'},
... headers={'Accept': 'application/json'})
>>> print(r)
Response: 201 Created
Content-Type: application/json
Location: https://localhost:80/useraccount/.../
{
"username": "tommy",
"in_state": "created"
}
Notice the "in_state"
property that appears in the JSON instance along
with respective schema as shown below.
From our entity’s JSON Hyper Schema, we can find a link with a custom relation
type tag:cubicweb.org,2017:workflow-transitions
(following the Tag URI scheme) as advised by JSON Hyper
Schema specification:
>>> r = client.get('/useraccount/tommy/schema',
... headers={'Accept': 'application/schema+json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
{
"$schema": "http://json-schema.org/draft-06/schema#",
"additionalProperties": false,
"links": [
{
"href": "/useraccount/",
"rel": "collection",
"targetSchema": {
"$ref": "/useraccount/schema"
},
"title": "UserAccount_plural"
},
{
"href": "/useraccount/tommy/",
"rel": "self",
"submissionSchema": {
"$ref": "/useraccount/tommy/schema?role=edition"
},
"targetSchema": {
"$ref": "/useraccount/tommy/schema?role=view"
},
"title": "UserAccount #..."
},
{
"href": "/useraccount/tommy/workflow-transitions/",
"rel": "tag:cubicweb.org,2017:workflow-transitions",
"title": "workflow history"
}
],
"properties": {
"username": {
"title": "username",
"type": "string"
},
"in_state": {
"title": "state",
"type": "string",
"readOnly": true
}
},
"title": "UserAccount",
"type": "object"
}
This link endpoint is of course described by a JSON Schema to be found by
looking at rel="describedby"
Link header:
>>> r = client.head('/useraccount/tommy/workflow-transitions/',
... headers={'Accept': 'application/json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
Link: </useraccount/tommy/>; rel="up"; title="tommy", </useraccount/tommy/workflow-transitions/schema>; rel="describedby"; type="application/schema+json"
>>> r = client.get('/useraccount/tommy/workflow-transitions/schema',
... headers={'Accept': 'application/schema+json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
Link: </useraccount/.../workflow-transitions/>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"items": {
"links": [
{
"anchor": "#",
"href": "/useraccount/.../workflow-transitions/{id}",
"rel": "item"
}
],
"properties": {
"id": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string"
}
},
"type": "object"
},
"links": [
{
"href": "/useraccount/.../workflow-transitions/",
"rel": "self",
"submissionSchema": {
"$ref": "/useraccount/.../workflow-transitions/schema?role=creation"
},
"targetSchema": {
"$ref": "/useraccount/.../workflow-transitions/schema"
},
"title": "TrInfo_plural"
}
],
"title": "TrInfo_plural",
"type": "array"
}
This Hyper-Schema is an array of items (which will be TrInfo objects here).
The submissionSchema
documents how to submit a new workflow transition:
>>> r = client.get('/useraccount/tommy/workflow-transitions/schema?role=creation',
... headers={'Accept': 'application/schema+json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
Link: </useraccount/.../workflow-transitions/>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"properties": {
"comment": {
"type": "string"
},
"name": {
"oneOf": [
{
"enum": [
"activate"
],
"title": "activate"
}
],
"type": "string"
}
},
"required": [
"name"
],
"title": "TrInfo",
"type": "object"
}
We see that we should send a JSON document with a required name
property
and an optional comment
. Values for name
are restricted to possible
transitions on the entity as can be seen in the oneOf
array above.
Now if we change the state of the entity:
>>> r = client.post_json('/useraccount/tommy/workflow-transitions',
... {'name': 'activate'},
... headers={'Accept': 'application/json'})
and fetch back the previous JSON Schema:
>>> r = client.get('/useraccount/tommy/workflow-transitions/schema?role=creation',
... headers={'Accept': 'application/schema+json'})
>>> print(r)
Response: 200 OK
Content-Type: application/json
Link: </useraccount/.../workflow-transitions/>; rel="describes"; type="application/json"
{
"$schema": "http://json-schema.org/draft-06/schema#",
"properties": {
"comment": {
"type": "string"
},
"name": {
"oneOf": [
{
"enum": [
"deactivate"
],
"title": "deactivate"
}
],
"type": "string"
}
},
"required": [
"name"
],
"title": "TrInfo",
"type": "object"
}