Some useful RAML 1.0 snippets using JSON API 1.0 and OAuth 2.0
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
OAS
examples
libraries
.gitignore
LICENSE
README.md
api.raml
json2yaml.py

README.md

RAML snippets

Some useful RAML 1.0 snippets.

Introduction

Find here some useful RAML 1.0 snippets (mostly as RAML 1.0 Libraries) that can be included in your root RAML API document.

JSON API 1.0 Libraries

jsonApiLibrary.raml: request/reply type definitions library

This library defines RAML types for {json:api} 1.0 RESTful API responses.

This library of RAML types is derived directly from the json-api 1.0 specification which is coded using the json-schema.org specification.

The following CHANGES have been made from the schema's YAML:

  • resource type is redefined a subclass of resource_post because id is not required for POST request body whereas it is mandatory for response body.
  • I couldn't represent allOf for links so made a pagelinks type which extends link.
  • Fix attributes and relationships types (patternProperties not directly mappable to RAML)
  • Move type: up for readability and changed type: array/items: into just type: itemtype[]

See the related jsonApiCollections.raml Library for useful resourceTypes and traits that uses these types.

Jsonapi uses mediatype application/vnd.api+json in requests and responses.

Example usage:

uses:
  api: jsonApiLibrary.raml
...
/widgets:
  get:
    responses: 
      200:
        body: 
          application/vnd.api+json:
            type: api.success
            properties: 
              data: widgets[]

Make your API-specific data types subclasses of api.resource and your methods should use type api.success for successful responses and api.failure for failure responses.

For reasons I don't quite understand, you must use this with the same uses key name (api) in your main api.raml and any other libraries that reference types defined here.

This file is mostly machine-generated by reading the jsonapi.json 1.0 schema and converting to YAML and then RAML.

The json-api specification used to create this derivative work is in the public domain under a Creative Commons Zero v1.0 Universal license.

jsonApiCollections.raml: collection-item resourceTypes and traits library

This is a companion to the jsonApiLibrary.raml Library.

resourceTypes The collection and item resourceTypes follow the {json:api} 1.0 recommendations regarding URL naming: a resourcePathName should be the same as it's type (both plural nouns). However, because we may be using RAML Libraries for those type definitions, one can't use resourcePathName but must still provide a dataType.

The relationshipCollection is a special resourceType for to-many relationships that allows GET, POST, PATCH & DELETE. (Normal collections don't allow PATCH or DELETE of the entire collection and the meaning of DELETE for relationships is quite different.) The relationshipItem is a special resourceType for to-one relationships that allows GET & PATCH.

collection required parameters:

  • dataType: the response RAML type (e.g. mythings). You must also define types named e.g. mythings_post and mythings_patch. These are all subclasses of on another with various properties labeled as true. This deals with the requirement that response data must have id and type keys which post data can leave out the id but must have all the required primary data attributes and patch data can leave out required attributes as it only sends changes.
  • exampleCollection: an example collection of the dataType.
  • exampleItem: an example item of the dataType.

traits Traits of pageable, sortable, sparse, filterable, includable are provided which describe the various standard query parameters, including recommended usage. For convenience, the all-the-things trait combines all the above-listed traits.

Usage example:

uses:
  col: libraries/jsonApiCollections.raml
/widgets:
  type: 
    col.collection: 
      dataType: wid.widgets
      exampleCollection: !include examples/WidgetCollectionExample.raml
      exampleItem: !include examples/WidgetItemExample.raml
  get:
    is: [ col.pageable, col.sparse ]

For reasons I don't quite understand, you must use this with the same uses key name (api) in your main api.raml and any other libraries that reference types defined in jsonApiLibrary.raml. For other reasons I don't understand, this can't just be part of jsonApiLibrary.raml.

OAuth 2.0 types library

The OAuth 2.0 types library currently only defines the OAuth2success and OAuth2error types for the token request endpoint.

Example

An example of using the JSON API libraries consists of the following RAML files:

Using the above-defined types, resourceTypes, and traits, we are able stay DRY and have a pretty concise root API document that focuses on what's unique about this API, reusing standard patterns for OAuth 2.0-protected resources that follow the JSON API 1.0 spec.

#%RAML 1.0
title: demo-jsonapi
description: a sample RESTful API that conforms to jsonapi.org 1.0
version: v1
#...
uses: 
  api: libraries/jsonApiLibrary.raml
  loc: libraries/LocationType.raml
  wid: libraries/WidgetType.raml
  col: libraries/jsonApiCollections.raml
  cu: libraries/columbiaLibrary.raml

# the API's resources:
/widgets:
  displayName: widgets
  description: stuff we have in inventory
  type: 
    col.collection: 
      dataType: wid.widgets
      exampleCollection: !include examples/WidgetCollectionExample.raml
      exampleItem: !include examples/WidgetItemExample.raml
  get:
    is: [ cu.oauth_read_any, col.all-the-things ]
  post:
    is: [ cu.oauth_create_any ]

Issues

RAML 1.0 types instead of JSON Schemas

While we would have liked to use json-schema.org representations for types, RAML 1.0 does not support type inheritance for JSON schemas:

A RAML processor MUST NOT allow types that define an XML or JSON schema to participate in type inheritance or specialization, or effectively in any type expression. Therefore, you cannot define sub-types of these types to declare new properties, add restrictions, set facets, or declare facets. You can, however, create simple type wrappers that add annotations, examples, display name, or a description.

Luckily the RAML 1.0 type system is based on json-schema.org so it is easy to generate json-schema.org from the RAML type definitions.

Only way to get the metadata is via parsing RAML

There appears to be no standard way to identify the metadata for a RAML 1.0 API as part of the API definition (e.g. as a well-known resource; Mulesoft's APIkit uses /console/api/?raml but this is not even documented anywhere and is not in the API's resource namespace). There is a dearth of client libraries that can parse compound RAML 1.0 documents (e.g. using libraries and other RAML fragments) into a compiled JSON representation. One that works well for javascript is raml-json-enhance-node. It compiles the RAML 1.0 to this JSON schema.

Perhaps this will be made moot by adoption of Open API 3.0.

{json:api} also appears to lack a standard metadata mechanism for discovering type schemas.

RAML 1.0 types are not resolvable into schemas

Even though jsonapi requires a type attribute (as does RAML 1.0), there is no standard place to find type definitions. Json-schema.org uses "{$ref": "#/definitions/<type>"} -- a JSON pointer -- for this.

RAML 1.0 type inheritance prohibits changing required properties to optional

{json:api} response and body types vary in which properties are required or optional. For example, a GET response requires the type and id properties and whatever attributes have been designated as required for the primary data. However, a POST allows the id to be optional as it is often system-generated (and returned with a 201 Created response). And, a PATCH only wants the attributes and relationships that have been changed to be supplied.

To mitigate this issue, each "user" type must actually be decalared as three types:

  • mytype: subclass of mytype_post which makes id required.
  • mytype_post: subclass of mytype_patch which overrides required attributes and relationships.
  • mytype_patch: base class which defines all attributes and relationships as optional.

OAS 3.0 refactoring

See OAS for a start at refactoring from RAML 1.0 to OAS 3.0.

TO DO

Still to do:

  • Define a local standard for finding API metadata if a global standard isn't available.
  • Refactor for OAS 3.0

Author

Alan Crosswell

Copyright (c) 2017 The Trustees of Columbia University in the City of New York

LICENSE

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.