From cf34a6652714eed3bda582ddcc45f0efa61028fc Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 26 Oct 2015 01:07:02 -0500 Subject: [PATCH] Mapping JSON API spec / schema to AMS [ci skip] --- docs/jsonapi/schema.md | 138 ++++++++++++ docs/jsonapi/schema/schema.json | 366 ++++++++++++++++++++++++++++++++ 2 files changed, 504 insertions(+) create mode 100644 docs/jsonapi/schema.md create mode 100644 docs/jsonapi/schema/schema.json diff --git a/docs/jsonapi/schema.md b/docs/jsonapi/schema.md new file mode 100644 index 00000000..ea7f134e --- /dev/null +++ b/docs/jsonapi/schema.md @@ -0,0 +1,138 @@ +[![JSON API 1.0](https://img.shields.io/badge/JSON%20API-1.0-lightgrey.svg)](http://jsonapi.org/) + +## JSON API Requests + +[Query Parameters Spec](http://jsonapi.org/format/#query-parameters) + +Headers: + +- Request: `Accept: application/vnd.api+json` +- Response: `Content-Type: application/vnd.api+json` + +### [Fetching Data](http://jsonapi.org/format/#fetching) + +A server MUST support fetching resource data for every URL provided as: + +- a `self` link as part of the top-level links object +- a `self` link as part of a resource-level links object +- a `related` link as part of a relationship-level links object + +Example supported requests + +- Individual resource or collection + - GET /articles + - GET /articles/1 + - GET /articles/1/author +- Relationships + - GET /articles/1/relationships/comments + - GET /articles/1/relationships/author +- Optional: [Inclusion of related resources](http://jsonapi.org/format/#fetching-includes) `ActiveModel::Serializer::Fieldset` + - GET /articles/1?`include`=comments + - GET /articles/1?`include`=comments.author + - GET /articles/1?`include`=author,comments.author + - GET /articles/1/relationships/comments?`include`=comments.author +- Optional: [Sparse Fieldsets](http://jsonapi.org/format/#fetching-sparse-fieldsets) `ActiveModel::Serializer::Fieldset` + - GET /articles?`include`=author&`fields`[articles]=title,body&`fields`[people]=name +- Optional: [Sorting](http://jsonapi.org/format/#fetching-sorting) + - GET /people?`sort`=age + - GET /people?`sort`=age,author.name + - GET /articles?`sort`=-created,title +- Optional: [Pagination](http://jsonapi.org/format/#fetching-pagination) + - GET /articles?`page`[number]=3&`page`[size]=1 +- Optional: [Filtering](http://jsonapi.org/format/#fetching-filtering) + - GET /comments?`filter`[post]=1 + - GET /comments?`filter`[post]=1,2 + - GET /comments?`filter`[post]=1,2 + +### [CRUD Actions](http://jsonapi.org/format/#crud) + +### [Asynchronous Processing](http://jsonapi.org/recommendations/#asynchronous-processing) + +### [Bulk Operations Extension](http://jsonapi.org/extensions/bulk/) + +## JSON API Document Schema + +| JSON API object | JSON API properties | Required | ActiveModelSerializers representation | +|-----------------------|----------------------------------------------------------------------------------------------------|----------|---------------------------------------| +| schema | oneOf (success, failure, info) | | +| success | data, included, meta, links, jsonapi | | ActiveModel::SerializableResource +| success.meta | meta | | ActiveModel::Serializer::Adapter::Base#meta +| success.included | UniqueArray(resource) | | ActiveModel::Serializer::Adapter::JsonApi#serializable_hash_for_collection +| success.data | data | | +| success.links | allOf (links, pagination) | | ActiveModel::Serializer::Adapter::JsonApi#links_for +| success.jsonapi | jsonapi | | +| failure | errors, meta, jsonapi | errors | +| failure.errors | UniqueArray(error) | | #1004 +| meta | Object | | +| data | oneOf (resource, UniqueArray(resource)) | | ActiveModel::Serializer::Adapter::JsonApi#serializable_hash_for_collection,#serializable_hash_for_single_resource +| resource | String(type), String(id), attributes, relationships, links, meta | type, id | ActiveModel::Serializer::Adapter::JsonApi#primary_data_for +| links | Uri(self), Link(related) | | #1028, #1246, #1282 +| link | oneOf (linkString, linkObject) | | +| link.linkString | Uri | | +| link.linkObject | Uri(href), meta | href | +| attributes | patternProperites(`"^(?!relationships$|links$)\\w[-\\w_]*$"`), any valid JSON | | ActiveModel::Serializer#attributes, ActiveModel::Serializer::Adapter::JsonApi#resource_object_for +| relationships | patternProperites(`"^\\w[-\\w_]*$"`); links, relationships.data, meta | | ActiveModel::Serializer::Adapter::JsonApi#relationships_for +| relationships.data | oneOf (relationshipToOne, relationshipToMany) | | ActiveModel::Serializer::Adapter::JsonApi#resource_identifier_for +| relationshipToOne | anyOf(empty, linkage) | | +| relationshipToMany | UniqueArray(linkage) | | +| empty | null | | +| linkage | String(type), String(id), meta | type, id | ActiveModel::Serializer::Adapter::JsonApi#primary_data_for +| pagination | pageObject(first), pageObject(last), pageObject(prev), pageObject(next) | | ActiveModel::Serializer::Adapter::JsonApi::PaginationLinks#serializable_hash +| pagination.pageObject | oneOf(Uri, null) | | +| jsonapi | String(version), meta | | ActiveModel::Serializer::Adapter::JsonApi::ApiObjects::JsonApi +| error | String(id), links, String(status), String(code), String(title), String(detail), error.source, meta | | +| error.source | String(pointer), String(parameter) | | +| pointer | [JSON Pointer RFC6901](https://tools.ietf.org/html/rfc6901) | | + + +The [http://jsonapi.org/schema](schema/schema.json) makes a nice roadmap. + +### Success Document +- [ ] success + - [ ] data: `"$ref": "#/definitions/data"` + - [ ] included: array of unique items of type `"$ref": "#/definitions/resource"` + - [ ] meta: `"$ref": "#/definitions/meta"` + - [ ] links: + - [ ] link: `"$ref": "#/definitions/links"` + - [ ] pagination: ` "$ref": "#/definitions/pagination"` + - [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"` + +### Failure Document + +- [ ] failure + - [ ] errors: array of unique items of type ` "$ref": "#/definitions/error"` + - [ ] meta: `"$ref": "#/definitions/meta"` + - [ ] jsonapi: `"$ref": "#/definitions/jsonapi"` + +### Info Document + +- [ ] info + - [ ] meta: `"$ref": "#/definitions/meta"` + - [ ] links: `"$ref": "#/definitions/links"` + - [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"` + +### Definitions + +- [ ] definitions: + - [ ] meta + - [ ] data: oneOf (resource, array of unique resources) + - [ ] resource + - [ ] attributes + - [ ] relationships + - [ ] relationshipToOne + - [ ] empty + - [ ] linkage + - [ ] meta + - [ ] relationshipToMany + - [ ] linkage + - [ ] meta + - [ ] links + - [ ] meta + - [ ] links + - [ ] link + - [ ] uri + - [ ] href, meta + - [ ] pagination + - [ ] jsonapi + - [ ] meta + - [ ] error: id, links, status, code, title: detail: source [{pointer, type}, {parameter: {description, type}], meta diff --git a/docs/jsonapi/schema/schema.json b/docs/jsonapi/schema/schema.json new file mode 100644 index 00000000..ef3ea351 --- /dev/null +++ b/docs/jsonapi/schema/schema.json @@ -0,0 +1,366 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "JSON API Schema", + "description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org", + "oneOf": [ + { + "$ref": "#/definitions/success" + }, + { + "$ref": "#/definitions/failure" + }, + { + "$ref": "#/definitions/info" + } + ], + + "definitions": { + "success": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/data" + }, + "included": { + "description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".", + "type": "array", + "items": { + "$ref": "#/definitions/resource" + }, + "uniqueItems": true + }, + "meta": { + "$ref": "#/definitions/meta" + }, + "links": { + "description": "Link members related to the primary data.", + "allOf": [ + { + "$ref": "#/definitions/links" + }, + { + "$ref": "#/definitions/pagination" + } + ] + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + } + }, + "additionalProperties": false + }, + "failure": { + "type": "object", + "required": [ + "errors" + ], + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/error" + }, + "uniqueItems": true + }, + "meta": { + "$ref": "#/definitions/meta" + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + } + }, + "additionalProperties": false + }, + "info": { + "type": "object", + "required": [ + "meta" + ], + "properties": { + "meta": { + "$ref": "#/definitions/meta" + }, + "links": { + "$ref": "#/definitions/links" + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + } + }, + "additionalProperties": false + }, + + "meta": { + "description": "Non-standard meta-information that can not be represented as an attribute or relationship.", + "type": "object", + "additionalProperties": true + }, + "data": { + "description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.", + "oneOf": [ + { + "$ref": "#/definitions/resource" + }, + { + "description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.", + "type": "array", + "items": { + "$ref": "#/definitions/resource" + }, + "uniqueItems": true + } + ] + }, + "resource": { + "description": "\"Resource objects\" appear in a JSON API document to represent resources.", + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/definitions/attributes" + }, + "relationships": { + "$ref": "#/definitions/relationships" + }, + "links": { + "$ref": "#/definitions/links" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + + "links": { + "description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.", + "type": "object", + "properties": { + "self": { + "description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.", + "type": "string", + "format": "uri" + }, + "related": { + "$ref": "#/definitions/link" + } + }, + "additionalProperties": true + }, + "link": { + "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.", + "oneOf": [ + { + "description": "A string containing the link's URL.", + "type": "string", + "format": "uri" + }, + { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "description": "A string containing the link's URL.", + "type": "string", + "format": "uri" + }, + "meta": { + "$ref": "#/definitions/meta" + } + } + } + ] + }, + + "attributes": { + "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.", + "type": "object", + "patternProperties": { + "^(?!relationships$|links$)\\w[-\\w_]*$": { + "description": "Attributes may contain any valid JSON value." + } + }, + "additionalProperties": false + }, + + "relationships": { + "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.", + "type": "object", + "patternProperties": { + "^\\w[-\\w_]*$": { + "properties": { + "links": { + "$ref": "#/definitions/links" + }, + "data": { + "description": "Member, whose value represents \"resource linkage\".", + "oneOf": [ + { + "$ref": "#/definitions/relationshipToOne" + }, + { + "$ref": "#/definitions/relationshipToMany" + } + ] + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "relationshipToOne": { + "description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.", + "anyOf": [ + { + "$ref": "#/definitions/empty" + }, + { + "$ref": "#/definitions/linkage" + } + ] + }, + "relationshipToMany": { + "description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.", + "type": "array", + "items": { + "$ref": "#/definitions/linkage" + }, + "uniqueItems": true + }, + "empty": { + "description": "Describes an empty to-one relationship.", + "type": "null" + }, + "linkage": { + "description": "The \"type\" and \"id\" to non-empty members.", + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + "pagination": { + "type": "object", + "properties": { + "first": { + "description": "The first page of data", + "oneOf": [ + { "type": "string", "format": "uri" }, + { "type": "null" } + ] + }, + "last": { + "description": "The last page of data", + "oneOf": [ + { "type": "string", "format": "uri" }, + { "type": "null" } + ] + }, + "prev": { + "description": "The previous page of data", + "oneOf": [ + { "type": "string", "format": "uri" }, + { "type": "null" } + ] + }, + "next": { + "description": "The next page of data", + "oneOf": [ + { "type": "string", "format": "uri" }, + { "type": "null" } + ] + } + } + }, + + "jsonapi": { + "description": "An object describing the server's implementation", + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + + "error": { + "type": "object", + "properties": { + "id": { + "description": "A unique identifier for this particular occurrence of the problem.", + "type": "string" + }, + "links": { + "$ref": "#/definitions/links" + }, + "status": { + "description": "The HTTP status code applicable to this problem, expressed as a string value.", + "type": "string" + }, + "code": { + "description": "An application-specific error code, expressed as a string value.", + "type": "string" + }, + "title": { + "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.", + "type": "string" + }, + "detail": { + "description": "A human-readable explanation specific to this occurrence of the problem.", + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "pointer": { + "description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].", + "type": "string" + }, + "parameter": { + "description": "A string indicating which query parameter caused the error.", + "type": "string" + } + } + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + } + } +}