Welcome to RAMLfications’s documentation!

Release v0.1.9 (What’s new?).

ramlfications is an Apache 2.0-licensed reference implementation of a RAML parser in Python intended to be used for parsing API definitions (e.g. for static documentation-generation).

If you’ve never heard of RAML, you’re missing out:

RESTful API Modeling Language (RAML) is a simple and succinct way of describing practically-RESTful APIs. It encourages reuse, enables discovery and pattern-sharing, and aims for merit-based emergence of best practices. The goal is to help our current API ecosystem by solving immediate problems and then encourage ever-better API patterns. RAML is built on broadly-used standards such as YAML and JSON and is a non-proprietary, vendor-neutral open spec.

ramlfications supports RAML version 0.8.

Why ramlfications and not pyraml-parser?

I chose to write a new library rather than wrestle with pyraml-parser as it was not developer-friendly to extend (in my PoV, others may have more success) and did not include required RAML features (e.g. uriParameters, parsing of security schemes, etc), as well as a lot of meta programming that is just simply over my head. However, I do encourage you to check out pyraml-parser! You may find it easier to work with than I did.

About

ramlfications’s documentation lives at Read the Docs, the code on GitHub. It’s tested on Python 2.6, 2.7, 3.3+, and PyPy. Both Linux and OS X are supported.

User’s Guide

Requirements and Installation

User Setup

The latest stable version can be found on PyPI, and you can install via pip:

$ pip install ramlfications

ramlfications runs on Python 2.6, 2.7, and 3.3+, and PyPy. Both Linux and OS X are supported. Currently, only RAML 0.8 is supported, but there are plans to support 1.0.

Continue onto usage to get started on using ramlfications.

Developer Setup

If you’d like to contribute or develop upon ramlfications, be sure to read How to Contribute first.

You can see the progress of ramlfications on our public project management page.

System requirements:

  • C Compiler (gcc/clang/etc.)
  • If on Linux - you’ll need to install Python headers (e.g. apt-get install python-dev)
  • Python 2.6, 2.7, 3.3+, or PyPy
  • virtualenv

Here’s how to set your machine up:

$ git clone git@github.com:spotify/ramlfications
$ cd ramlfications
$ virtualenv env
$ source env/bin/activate
(env) $ pip install -r dev-requirements.txt

Run Tests

If you’d like to run tests for all supported Python versions, you must have all Python versions installed on your system. I suggest pyenv to help with that.

To run all tests:

(env) $ tox

To run a specific test setup (options include: py26, py27, py33, py34, py35, pypy, flake8, verbose, manifest, docs, setup, setupcov):

(env) $ tox -e py26

To run tests without tox:

(env) $ py.test
(env) $ py.test --cov ramlfications --cov-report term-missing

Build Docs

Documentation is build with Sphinx, written in rST, uses the Read the Docs theme with a slightly customized CSS, and is hosted on Read the Docs site.

To rebuild docs locally, within the parent ramlfications directory:

(env) $ tox -e docs

or:

(env) $ sphinx-build -b docs/ docs/_build

Then within ramlfications/docs/_build you can open the index.html page in your browser.

Still have issues?

Feel free to drop by #ramlfications on Freenode (webchat) or ping via Twitter. “roguelynn” is the maintainer, a.k.a econchick on GitHub, and based in San Fran.

Usage

You can use ramlfications to parse and validate a RAML file with Python. With the command line, you can validate or visualize the RAML-defined API as a tree.

Parse

To parse a RAML file, include ramlfications in your project and call the parse function:

>>> import ramlfications
>>> RAML_FILE = "/path/to/my-api.raml"
>>> api = ramlfications.parse(RAML_FILE)
>>> api
RootNode(title='Example Web API')
>>> api.title
'My Foo API'
>>> api.version
'v1'
>>> api.security_schemes
[SecurityScheme(name='oauth_2_0')]
>>> oauth2 = api.security_schemes[0]
>>> oauth2.name
'oauth_2_0'
>>> oauth2.type
'OAuth 2.0'
>>> oauth2.settings.get("scopes")
['playlist-read-private', 'playlist-modify-public',..., 'user-read-email']
>>> oauth2.settings.get("accessTokenUri")
'https://accounts.foo.com/api/token'
>>> api.resources
[ResourceNode(method='get', path='/foo'), ResourceNode(method='put', path='/foo'), ..., ResourceNode(method='get', path='/foo/bar/{id}')]
>>> foo_bar = api.resources[-1]
>>> foo_bar.name
'/{id}'
>>> foo_bar.display_name
'fooBarID'
>>> foo_bar.absolute_uri
'https://api.foo.com/v1/foo/bar/{id}'
>>> foo_bar.uri_params
[URIParameter(name='id')]
>>> id_param = foo_bar.uri_params[0]
>>> id_param.required
True
>>> id_param.type
'string'
>>> id_param.example
'f00b@r1D'

You can pass in an optional config file to add additional values for certain parameters. Find out more within the Extended Usage:

>>> CONFIG_FILE = "/path/to/config.ini"
>>> api = ramlfications.parse(RAML_FILE, CONFIG_FILE)

For more complete understanding of what’s available when parsing a RAML file, check the Extended Usage or the API Definition.

Validate

Validation is according to the RAML Specification.

To validate a RAML file via the command line:

$ ramlfications validate /path/to/my-api.raml
Success! Valid RAML file: tests/data/examples/simple-tree.raml
$ ramlfications validate /path/to/invalid/no-title.raml
Error validating file /path/to/invalid/no-title.raml: RAML File does not define an API title.

To validate a RAML file with Python:

>>> from ramlfications import validate
>>> RAML_FILE = "/path/to/my-api.raml"
>>> validate(RAML_FILE)
>>>
>>> from ramlfications import validate
>>> RAML_FILE = "/path/to/invalid/no-base-uri-no-title.raml"
>>> validate(RAML_FILE)
InvalidRAMLError:
    InvalidRootNodeError: RAML File does not define the baseUri.
    InvalidRootNodeError: RAML File does not define an API title.

Note

When using validate within Python (versus the command line utility), if the RAML file is valid, then nothing is returned; only invalid files will return an exception. You can access the individual errors via the errors attribute on the exception.

If you have additionally supported items beyond the standard (e.g. protocols beyond HTTP/S), you can still validate your code by passing in your config file.

$ cat api.ini
[custom]
protocols = FTP
>>> from ramlfications import validate
>>> RAML_FILE = "/path/to/support-ftp-protocol.raml"
>>> CONFIG_FILE = "/path/to/api.ini"
>>> validate(RAML_FILE, CONFIG_FILE)
>>>

You may also “turn off” validation when developing via the config file. To learn more about ramlfications configuration including turning off validation as well as default/supported configuration, check out Configuration.

Tree

To visualize a tree output of a RAML file:

$ ramlfications tree /path/to/my-api.raml [-C|--color light|dark] [-v|vv|vvv] [-o|--output]

The least verbose option would show something like this:

$ ramlfications tree /path/to/my-api.raml
==========
My Foo API
==========
Base URI: https://api.foo.com/v1
|– /foo
|  – /bar
|  – /bar/{id}

And the most verbose:

$ ramlfications tree /path/to/my-api.raml -vvv
==========
My Foo API
==========
Base URI: https://api.foo.com/v1
|– /foo
|  ⌙ GET
|     Query Params
|      ⌙ q: Foo Query
|      ⌙ type: Item Type
|  – /bar
|    ⌙ GET
|       Query Params
|        ⌙ q: Bar Query
|        ⌙ type: item type
|  – /bar/{id}
|    ⌙ GET
|       URI Params
|        ⌙ id: ID of foo

Update

At the time of this release (Dec 24, 2015), the MIME media types that ramlfications supports can be found on GitHub.

However, you do have the ability to update your own setup with the latest-supported MIME media types as defined by the IANA. To do so:

$ ramlfications update

Note

If you are running Python version 2.7.8 or earlier, or Python version 3.4.2 or earlier, it is encouraged to have requests[all] installed in your environment. The command will still work without the package using the standard lib’s urllib2, but does not perform SSL certificate verification. Please see PEP 467 for more details.

Options and Arguments

The full usage is:

$ ramlfications [OPTIONS] COMMAND [ARGS]

The RAMLFILE is a file containing the RAML-defined API you’d like to work with.

Valid COMMAND s are the following:

validate RAMLFILE

Validate the RAML file according to the RAML Specification.

-c PATH, --config PATH

Additionally supported items beyond RAML spec.

update

Update RAMLfications’ supported MIME types from IANA.

tree RAMLFILE

Visualize the RAML file as a tree.

-c PATH, --config PATH

Additionally supported items beyond RAML spec.

-C <light|dark>, --color <light|dark>

Use a light color scheme for dark terminal backgrounds [DEFAULT], or dark color scheme for light backgrounds.

-o FILENAME, --output FILENAME

Save tree output to desired file

-v

Increase verbose output of the tree one level: adds the HTTP methods

-vv

Increase verbose output of the tree one level: adds the parameter names

-vvv

Increase verbose output of the tree one level: adds the parameter display name

Extended Usage

To parse a RAML file, include ramlfications in your project and call the parse function:

>>> import ramlfications
>>> RAML_FILE = "/path/to/my-api.raml"
>>> api = ramlfications.parse(RAML_FILE)

Configuration

Perhaps your API supports response codes beyond what IETF supports (default for this parser). Or maybe you implemented your own authentication scheme that your API uses I hope not!.

Example configuration file:

[main]
validate = True

[custom]
append = True
resp_codes = 420, 421, 422
auth_schemes = oauth_3_0, oauth_4_0
media_types = application/vnd.github.v3, foo/bar
protocols = FTP
raml_versions = 0.8

Feed the configuration into the parse function like so:

>>> import ramlfications
>>> RAML_FILE = "/path/to/my-api.raml"
>>> CONFIG_FILE = "/path/to/my-config.ini"
>>> api = ramlfications.parse(RAML_FILE, CONFIG_FILE)

RAML Root Section

In following the RAML Spec’s Root Section definition, here is how you can access the following attributes:

The Basics

>>> api.title
'My Other Foo API'
>>>
>>> api.version
v2
>>> api.base_uri
'https://{domainName}.foo.com/v2'
>>> api.base_uri_parameters
[<URIParameter(name='domainName')>]
>>>
>>> api.protocols
['HTTPS']

API Documentation

>>> api.documentation
[<Documentation(title='The Foo API Docs')>]
>>> doc = api.documentation[0]
>>> doc.title
'The Foo API Docs'

Docs written in the RAML file should be written using Markdown. This also applies to any description parameter.

With ramlfications, documentation content and descriptions can either be viewed raw, or in parsed HTML.

>>> doc.content
'Welcome to the _Foo API_ specification. For more information about\nhow to use the API, check out [developer site](https://developer.foo.com).\n'
>>>
>>> doc.content.html
u'<p>Welcome to the <em>Foo API</em> specification. For more information about\nhow to use the API, check out <a href="https://developer.foo.com">developer site</a>.</p>\n'

Check out API Definition for full definition of RootNode and its associated attributes and objects.

Security Schemes

RAML supports OAuth 1, OAuth 2, Basic & Digest, and any authentication scheme self-defined with an x-{other} header.

To parse auth schemes:

>>> api.security_schemes
[<SecurityScheme(name='oauth_2_0')>]
>>> oauth2 = api.security_schemes[0]
>>> oauth2.name
'oauth_2_0'
>>> oauth2.type
'OAuth 2.0'
>>> oauth2.description
'Foo supports OAuth 2.0 for authenticating all API requests.\n'
>>> oauth2.description.html
u'<p>Foo supports OAuth 2.0 for authenticating all API requests.</p>\n'

And its related Headers and Responses:

>>> oauth2.described_by
{'headers': [<Header(name='Authorization')>], 'responses': [<Response(code='401')>, <Response(code='403')>]}
>>> first_header = oauth2.described_by['headers'][0]
>>> first_header
<HeaderParameter(name='Authorization')>
>>> first_header.name
'Authorization'
>>> first_headers.description
'Used to send a valid OAuth 2 access token.\n'
>>> first_headers.description.html
u'<p>Used to send a valid OAuth 2 access token.</p>\n'
>>> resps = oauth2.described_by['responses']
>>> resps
[<Response(code='401')>, <Response(code='403')>]
>>> resp[0].code
401
>>> resp[0].description.raw
'Bad or expired token. This can happen if the user revoked a token or\nthe access token has expired. You should re-authenticate the user.\n'

Authentication settings (available for OAuth1, OAuth2, and any x-header that includes “settings” in the RAML definition).

>>> oauth2.settings.scopes
['foo-read-private', 'foo-modify-public',..., 'user-read-email-address']
>>> oauth2.settings.access_token_uri
'https://accounts.foo.com/api/token'
>>> oauth2.settings.authorization_grants
['code', 'token']
>>> oauth2.settings.authorization_uri
'https://accounts.foo.com/authorize'

Check out API Definition for full definition of SecuritySchemes, Header, Response and their associated attributes and objects.

Schemas

The RAML specification allows the ability to define schemas that can be used anywhere within the API definition. One may define a schema within the RAML file itself, or in another, separate file (local or over HTTP/S). ramlfications supports json and xsd filetimes in addition to parsing RAML.

See Non-RAML Parsing for more information about how ramlfications handles json and xsd formats.

Traits & Resource Types

Traits & resource types help when API definitions get a bit repetitive. More information can be found in the RAML spec for resource types and traits.

Resource Types
>>> api.resource_types
[<ResourceTypeNode(name='collection')>, <ResourceTypeNode(name='member')>]
>>> collection = api.resource_types[0]
>>> collection.name
'collection'
>>> collection.description
'The collection of <<resourcePathName>>'
>>> collection.usage
'This resourceType should be used for any collection of items'
>>> collection.method
'get'
>>> get.optional
False
Traits
>>> api.traits
[<TraitNode(name='filtered')>, <TraitNode(name='paged')>]
>>> paged = api.traits[1]
>>> paged.query_params
[<QueryParameter(name='offset')>, <QueryParameter(name='limit')>]
>>> paged.query_params[0].name
'offset'
>>> paged.query_params[0].description
'The index of the first track to return'
Mapping of Properties and Elements from Traits & Resource Types to Resources

When a resource has a trait and/or type assigned to it, or a resource type has another resource type or a trait assigned to it, it inherits its properties.

Also, the RAML Spec allows for parameters within Traits and ResourceTypes, denoted by double brackets within the Trait/ResourceType definition, e.g. <<parameter>>. After the parsing of the API definition, the appropriate parameters are filled in for the respective resource.

For example, a simplified RAML file:

#%RAML 0.8
title: Example API - Mapped Traits
version: v1
resourceTypes:
  - searchableCollection:
      get:
        queryParameters:
          <<queryParamName>>:
            description: |
              Return <<resourcePathName>> that have their <<queryParamName>>
              matching the given value
          <<fallbackParamName>>:
            description: |
              If no values match the value given for <<queryParamName>>,
              use <<fallbackParamName>> instead
  - collection:
      usage: This resourceType should be used for any collection of items
      description: The collection of <<resourcePathName>>
      get:
        description: Get all <<resourcePathName>>, optionally filtered
      post:
        description: Create a new <<resourcePathName | !singularize>>
traits:
  - secured:
      description: A secured method
      queryParameters:
        <<tokenName>>:
          description: A valid <<tokenName>> is required
  - paged:
      queryParameters:
        numPages:
          description: The number of pages to return, not to exceed <<maxPages>>
/books:
  type: { searchableCollection: { queryParamName: title, fallbackParamName: digest_all_fields } }
  get:
    is: [ secured: { tokenName: access_token }, paged: { maxPages: 10 } ]

When parsed, the Python notation would look like this:

>>> RAML_FILE = "/path/to/foo-api.raml"
>>> api = parse(RAML_FILE)
# accessing API-supported resource types
>>> api.resource_types
[<ResourceTypeNode(method='GET', name='searchableCollection')>,
<ResourceTypeNode(method='POST', name='collection')>,
<ResourceTypeNode(method='GET', name='collection')>]
>>> api.resource_types[0].query_params
[<QueryParameter(name='<<queryParamName>>')>,
<QueryParameter(name='<<fallbackParamName>>')>]
>>> api.resource_types[0].query_params[0].description
Return <<resourcePathName>> that have their <<queryParamName>> matching the given value
# accessing API-supported traits
>>> api.traits
[<TraitNode(name='secured')>, <TraitNode(name='paged')>]
>>> api.traits[0].query_params
[<QueryParameter(name='numPages')>]
>>> api.traits[0].query_params[0].description
The number of pages to return, not to exceed <<maxPages>>
# accessing a single resource
>>> books = api.resources[0]
>>> books
<ResourceNode(method='GET', path='/books')>
>>> books.type
{'searchableCollection': {'fallbackParamName': 'digest_all_fields', 'queryParamName': 'title'}}
>>> books.traits
[<TraitNode(name='secured')>, <TraitNode(name='paged')>]
>>> books.query_params
[<QueryParameter(name='title')>, <QueryParameter(name='digest_all_fields')>,
<QueryParameter(name='access_token')>, <QueryParameter(name='numPages')>]
>>> books.query_params[0].description
Return books that have their title matching the given value
>>> books.query_params[3].description
The number of pages to return, not to exceed 10

Resource types can also contain optional properties. Currently, ramlfications only supports the method parameter to be optional, but broadening that to all properties within a defined resource type and trait is coming soon.

Below are a few examples of applying a resource type that has a require method, and an optional method.

Example 1

A required method in a resource type with that method not explicitly defined/included in resource:

#%RAML 0.8
---
title: Example API
baseUri: http://example.com
version: v1
resourceTypes:
  - inheritgetmethod:
      description: get-method resource type example
      usage: Some sort of usage description
      get:
        description: This description should be inherited when applied to resources
        headers:
          X-Inherited-Header:
            description: This header should be inherited
/a-get-resource:
  description: Resource inherits from inheritedgetmethod
  type: inheritgetmethod
>>> len(api.resources)
1
>>> res = api.resources[0]
>>> res.name
'/a-get-resource'
>>> res.type
'inheritedgetmethod'
>>> res.method  # inherits the required method from its resource type
'get'
>>> # also inherits all of it's other properties, if defined
>>> res.description.raw
'This description should be inherited when applied to resources'
>>> res.headers
[Header(display_name='X-Inherited-Header')]

Example 2

Similar to the example above, a required method in a resource type where not explicitly defined in the resource, and the resource has another method defined (really confusing to explain, just check out the example):

#%RAML 0.8
---
title: Example API
baseUri: http://example.com
version: v1
resourceTypes:
  - inheritgetmethod:
      description: get-method resource type example
      usage: Some sort of usage description
      get:
        description: This description should be inherited when applied to resources
        headers:
          X-Inherited-Header:
            description: This header should be inherited
/a-resource:
  description: Resource inherits from inheritedgetmethod
  type: inheritgetmethod
  post:
    description: Post some foobar
>>> len(api.resources)
2
>>> first = api.resources[0]
>>> first.name
'/a-resource'
>>> first.type
'inheritedgetmethod'
>>> first.method  # inherits the required method from its resource type
'get'
>>> second = api.resources[1]
>>> second.name
'/a-resources'
>>> second.method
'post'

Example 3

Inheriting an optional resource type method:

#%RAML 0.8
---
title: Example API
baseUri: http://example.com
version: v1
resourceTypes:
  - inheritgetoptionalmethod:
      description: optional get-method resource type example
      usage: Some sort of usage description
      get?:
        description: This description should be inherited when applied to resources with get methods
        headers:
          X-Optional-Inherited-Header:
            description: This header should be inherited when resource has get method
/a-resource:
  description: Resource inherits from inheritoptionalmethod
  type: inheritgetoptionalmethod
  get:
    headers:
      X-Explicit-Header:
        description: This is a header in addition to what is inherited
>>> len(api.resources)
1
>>> res = api.resources[0]
>>> res.name
'/a-resource'
>>> res.method
'get'
>>> res.headers
[Header(display_name='X-Optional-Inherited-Header'), Header(display_name='X-Explicit-Header')]

Example 4

Let’s combine all permutations!:

#%RAML 0.8
---
title: Example API
baseUri: http://example.com
version: v1
resourceTypes:
  - inheritgetmethod:
      description: get-method resource type example
      usage: Some sort of usage description
      get:
        description: This description should be inherited when applied to resources
        headers:
          X-Inherited-Header:
            description: This header should be inherited
  - inheritgetoptionalmethod:
      description: optional get-method resource type example
      usage: Some sort of usage description
      get?:
        description: This description should be inherited when applied to resources with get methods
        headers:
          X-Optional-Inherited-Header:
            description: This header should be inherited when resource has get method
/a-resource:
  description: Resource inherits from inheritoptionalmethod
  type: inheritgetoptionalmethod
  get:
    headers:
      X-Explicit-Header:
        description: This is a header in addition to what is inherited
/another-resource:
  type: inheritgetmethod
  description: This resource should also inherit the Resource Type's get method properties
  post:
    description: post some more foobar
>>> len(api.resources)
3
>>> first = api.resources[0]
>>> first.name
'/a-resource'
>>> first.method
'get'
>>> second = api.resources[1]
>>> second.name
'/another-resource'
>>> second.method
'post'
>>> third = api.resources[2]
>>> third.method
'get'

Example 5

Last thing to note is that properties from the inherited Resource Type will not overwrite properties of the resource if they are explicitly defined under the resource:

#%RAML 0.8
---
title: Example API
baseUri: http://example.com
version: v1
resourceTypes:
  - inheritgetmethod:
      description: get-method resource type example
      usage: Some sort of usage description
      get:
        description: This description should *NOT* be inherited when applied to resources
        headers:
          X-Inherited-Header:
            description: This header should be inherited
/a-get-resource:
  description: Resource inherits from inheritedgetmethod
  type: inheritgetmethod
  get:
    description: This description will actually be used
>>> len(api.resources)
1
>>> res = api.resources[0]
>>> res.name
'/a-get-resource'
>>> res.type
'inheritedgetmethod'
>>> # inherited types will not overwrite properties if explicitly defined in resource
>>> res.description.raw
'This description will actually be used'
>>> res.headers
[Header(display_name='X-Inherited-Header')]

Check out API Definition for full definition of traits and resources, and its associated attributes and objects.

Resources

“Resources” are defined in the RAML Spec’s Resource Section and is a relative URI (relative to the base_uri and, if nested, relative to its parent URI).

For example, Foo API defines /foo/bar as a resource (a “top-level resource” to be exact). It also defines /{id} under /foo/bar, making /{id} a nested resource, relative to /foo/bar. The relative path would be /foo/bar/{id}, and the absolute URI path would be https://api.foo.com/v2/foo/bar/{id}.

>>> api.resources
[<Resource(method='GET', path='/foo')>,..., <Resource(method='DELETE', path='/foo/bar/{id}')>]
>>>
>>> foo_bar = resources[-1]
>>> foo_bar.name
'/{id}'
>>> foo_bar.description
'[Delete a foo bar](https://developer.foo.com/api/delete-foo-bar/)\n'
>>> foo_bar.description.html
u'<p><a href="https://developer.foo.com/api/delete-foo-bar/">Delete a foo bar</a></p>\n'
>>> foo_bar.display_name
'foo bar'
>>> foo_bar.method
'delete'
>>> foo_bar.path
'/foo/bar/{id}'
>>> foo_bar.absolute_uri
'https://api.foo.com/v2/foo/bar/{id}'
>>> foo_bar.uri_params
[<URIParameter(name='id')>]
>>>
>>> uri_param = foo_bar.uri_params[0]
>>> uri_param.required
True
>>> uri_param.type
'string'
>>> uri_param.example
'f0ob@r1D'
>>> foo_bar.parent
<Resource(method='GET', path='/foo/bar/')>

Note

The uri_params and base_uri_params on the api object (RootNode) and a resource object (ResourceNode) will always preserve order according to the absolute URI.

Check out API Definition for full definition of what is available for a resource object, and its associated attributes and objects.

Non-RAML Parsing

JSON

For json filetypes, ramlfications will also parse $ref keywords and bring in the referenced objects according to both Draft 3 and Draft 4 JSON Schema definition.

The following $ref values are supported:

Internal fragments

RAML File:

#%RAML 0.8
title: Sample API Demo - JSON Includes
version: v1
schemas:
    - json: !include includes/ref_internal_fragment.json
baseUri: http://json.example.com
/foo:
  displayName: foo resource

includes/ref_internal_fragment.json file:

{
  "references": {
    "internal": "yes"
  },
  "name": "foo",
  "is_this_internal?": [{"$ref": "#/references/internal"}]
}

ramlfications would produce the following:

>>> RAML_FILE = "api.raml"
>>> api = parse(RAML_FILE)
>>> api.schemas
[{'json': {u'is_this_internal?': [u'yes'],
u'name': u'foo',
u'references': {u'internal': u'yes'}}}]
Local file with & without fragments

Parsing references to local files via relative or absolute filepaths work fine, as well as prepending the URI with file:, e.g. file:local.schema.json.

RAML File that includes a JSON file under schemas:

#%RAML 0.8
title: Sample API Demo - JSON Includes
version: v1
schemas:
    - jsonexample: !include local.schema.json
baseUri: http://json.example.com
/foo:
  displayName: foo resource

The included local.schema.json file that refers to another JSON file via a relative filepath and a fragment:

{
  "$schema":"http://json-schema.org/draft-03/schema",
  "type": "object",
  "properties": {
    "album_type": {
      "type": "string",
      "description": "The type of the album: one of 'album', 'single', or 'compilation'."
    },
    "artists": {
        "type": "array",
        "description": "The artists of the album. Each artist object includes a link in href to more detailed information about the artist.",
        "items": [{ "$ref": "artist.schema.json#properties" }]
    }
  }
}

The referred artist.schema.json file:

{
  "$schema": "http://json-schema.org/draft-03/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of the artist."
    },
    "popularity": {
      "type": "integer",
      "description": "The popularity of the artist. The value will be between 0 and 100, with 100 being the most popular. The artist's popularity is calculated from the popularity of all the artist's tracks."
    },
    "type": {
      "type": "string",
      "description": "The object type: 'artist'"
    },
    "uri": {
      "type": "string",
      "description": "The Spotify URI for the artist."
    }
  }
}

Finally, ramlfications would produce the following (pretty printed for readability):

>>> RAML_FILE = "api.raml"
>>> api = parse(RAML_FILE)
>>> api.schemas
[{'jsonexample': {
  u'$schema': u'http://json-schema.org/draft-03/schema',
  u'properties': {
    u'album_type': {
      u'description': u"The type of the album: one of 'album', 'single', or 'compilation'.",
      u'type': u'string'
    },
    u'artists': {
      u'description': u'The artists of the album. Each artist object includes a link in href to more detailed information about the artist.',
      u'items': [{
        u'popularity': {
          u'type': u'integer',
          u'description': u"The popularity of the artist. The value will be between 0 and 100, with 100 being the most popular. The artist's popularity is calculated from the popularity of all the artist's tracks."
        },
        u'type': {
          u'type': u'string',
          u'description': u"The object type: 'artist'"
        },
        u'name': {
          u'type': u'string',
          u'description': u'The name of the artist.'
        },
        u'uri': {
          u'type': u'string',
          u'description': u'The Spotify URI for the artist.'
        }
      }],
    u'type': u'array'
    }
  },
  u'type': u'object'
  }
}]
Remote file with & without fragments (over HTTP or HTTPS only)

RAML file:

#%RAML 0.8
title: Sample API Demo - JSON Includes
version: v1
schemas:
    - json: !include local.schema.json
baseUri: http://json.example.com
/foo:
  displayName: foo resource

The included local.schema.json file (that’s local) that refers to another JSON file remotely:

{
    "$schema": "http://json-schema.org/draft-03/schema",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "The name of the artist."
        },
        "images": {
            "type": "array",
            "description": "Images associated with artist",
            "items": [{"$ref": "https://example.com/data#properties"}]
        }
    }
}

The remote file found on https://example.com/data#properties:

{
  "$schema": "http://json-schema.org/draft-03/schema",
  "type": "object",
  "properties": {
    "height": {
      "type": "integer",
      "description": "The image height in pixels. If unknown: null or not returned."
    },
    "url": {
      "type": "string",
      "description": "The source URL of the image."
    },
    "width": {
      "type": "integer",
      "description": "The image width in pixels. If unknown: null or not returned."
    }
  }
}

Finally, ramlfications would produce the following (pretty printed for readability):

>>> RAML_FILE = "api.raml"
>>> api = parse(RAML_FILE)
>>> api.schemas
[{'jsonexample': {
  u'$schema': u'http://json-schema.org/draft-03/schema',
  u'properties': {
    u'name': {
      u'type': u'string',
      u'description': u'The name of the artist.'
    },
    u'images': {
      u'type': 'array',
      u'description': 'Images associated with artist',
      u'items': [{
        u'height': {
          u'type': u'integer',
          u'description': u'The image height in pixels. If unknown: null or not returned.'
        },
        u'url': {
          u'type': u'string',
          u'description': u'The source URL of the image.'
        },
        u'width': {
          u'type': u'integer',
          u'description': u'The image width in pixels. If unknown: null or not returned.'
        }
      }]
    }
  }
}}]

XML

Documentation (and improved functionality) coming soon!

Configuration

Main Configuration

You are able to turn on/off the validation against the RAML specification within the config.ini file:

[main]
validation = False  # defaults to True if not set

This configuration is ignored when using the validate function directly (but other configurations are not ignored).

Supported Custom Validation Configuration

In support of the RAML spec, ramlfications will automatically support the following:

RAML Versions

Config variable: raml_versions
Config type: list of strings
Supported: 0.8

HTTP Methods

Config variable: http_methods
Config type: list of strings
Supported: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT

Authentication Schemes

Config variable: auth_schemes
Config type: list of strings
Supported: OAuth 1.0, OAuth 2.0, Basic Authentication, Digest Authentication

HTTP Response Codes

Config variable: resp_codes
Config type: list of integers
Supported: From Python stdlib BaseHTTPServer
100, 101,
200, 201, 202, 203, 204, 205, 206,
300, 301, 302, 303, 304, 305, 307,
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
500, 501, 502, 503, 504, 505

Protocols

Config variable: protocols
Config type: list of strings
Supported: HTTP, HTTPS

MIME Media Types

Config variable: media_types
Config type: list of strings that fit the regex defined under default media type: application\/[A-Za-z.-0-1]*+?(json|xml)
Supported: MIME media types that the package supports can be found on GitHub and is up to date as of the time of this release (Dec 24, 2015).

Note

If you would like to update your own setup with the latest IANA supported MIME media types, refer to Usage.

User-specified

You may define additional values beyond what ramlfications already supports above.

To do so, create your own ini file with a [custom] section.

Then add the attributes defined above that you want to support. You can only add support to the configuration values explained above.

Warning

Additionally supported values defined in your configuration will only add to the values that ramlfications will validate against; it will not overwrite values that the ramlfications supports as defined in the RAML spec.

An example config.ini file:

[custom]
raml_versions = 0.9, 1.0
http_methods = foo, bar
auth_schemes = oauth_3_0, my_auth
media_types = application/vnd.foobar.v2
protocols = FTP
resp_codes = 429, 440

Usage

To use your configuration from within Python:

>>> from ramlfications import parse, validate
>>> RAML_FILE = "path/to/api.raml"
>>> CONFIG_FILE = "path/to/api.ini"
>>> api = parse(RAML_FILE, CONFIG_FILE)
>>> validate(RAML_FILE, CONFIG_FILE)
>>>

To use via the command line:

$ ramlfications validate --config path/to/api.ini path/to/api.raml
$ ramlfications tree --config path/to/api.ini path/to/api.raml

API Definition

Main functions

The following three functions are meant for you to use primarily when parsing a RAML file/string.

ramlfications.parse(raml, config_file=None)

Module helper function to parse a RAML File. First loads the RAML file with loader.RAMLLoader then parses with parser.parse_raml() to return a raml.RAMLRoot object.

Parameters:
  • raml – Either string path to the RAML file, a file object, or a string representation of RAML.
  • config_file (str) – String path to desired config file, if any.
Returns:

parsed API

Return type:

RAMLRoot

Raises:
  • LoadRAMLError – If error occurred trying to load the RAML file (see loader.RAMLLoader)
  • InvalidRootNodeError – API metadata is invalid according to RAML specification.
  • InvalidResourceNodeError

    API resource endpoint is invalid according to RAML specification.

  • InvalidParameterError

    Named parameter is invalid according to RAML specification.

ramlfications.load(raml_file)

Module helper function to load a RAML File using loader.RAMLLoader.

Parameters:raml_file (str) – String path to RAML file
Returns:loaded RAML
Return type:dict
Raises:LoadRAMLError – If error occurred trying to load the RAML file
ramlfications.loads(raml_string)

Module helper function to load a RAML File using loader.RAMLLoader.

Parameters:raml_string (str) – String of RAML data
Returns:loaded RAML
Return type:dict
Raises:LoadRAMLError – If error occurred trying to load the RAML file
ramlfications.validate(raml, config_file=None)

Module helper function to validate a RAML File. First loads the RAML file with loader.RAMLLoader then validates with validate.validate_raml().

Parameters:
  • raml (str) – Either string path to the RAML file, a file object, or a string representation of RAML.
  • config_file (str) – String path to desired config file, if any.
Returns:

No return value if successful

Raises:
  • LoadRAMLError – If error occurred trying to load the RAML file (see loader.RAMLLoader)
  • InvalidRootNodeError

    API metadata is invalid according to RAML specification.

  • InvalidResourceNodeError

    API resource endpoint is invalid according to RAML specification.

  • InvalidParameterError

    Named parameter is invalid according to RAML specification.

  • InvalidRAMLError

    RAML file is invalid according to RAML specification.

Core

Note

The following documentation is meant for understanding the underlying ramlfications API. No need to interact directly with the modules, classes, & functions below.

parser

ramlfications.parser.parse_raml(loaded_raml, config)

Parse loaded RAML file into RAML/Python objects.

Parameters:loaded_raml (RAMLDict) – OrderedDict of loaded RAML file
Returns:raml.RootNode object.
Raises:errors.InvalidRAMLError when RAML file is invalid
ramlfications.parser.create_root(raml, config)

Creates a Root Node based off of the RAML’s root section.

Parameters:raml (RAMLDict) – loaded RAML file
Returns:raml.RootNode object with API root attributes set
ramlfications.parser.create_traits(raml_data, root)

Parse traits into Trait objects.

Parameters:
  • raml_data (dict) – Raw RAML data
  • root (RootNode) – Root Node
Returns:

list of raml.TraitNode objects

ramlfications.parser.create_resource_types(raml_data, root)

Parse resourceTypes into ResourceTypeNode objects.

Parameters:
  • raml_data (dict) – Raw RAML data
  • root (RootNode) – Root Node
Returns:

list of raml.ResourceTypeNode objects

ramlfications.parser.create_resources(node, resources, root, parent)

Recursively traverses the RAML file via DFS to find each resource endpoint.

Parameters:
  • node (dict) – Dictionary of node to traverse
  • resources (list) – List of collected ResourceNode s
  • root (RootNode) – The RootNode of the API
  • parent (ResourceNode) – Parent ResourceNode of current node
Returns:

List of raml.ResourceNode objects.

ramlfications.parser.create_node(name, raw_data, method, parent, root)

Create a Resource Node object.

Parameters:
  • name (str) – Name of resource node
  • raw_data (dict) – Raw RAML data associated with resource node
  • method (str) – HTTP method associated with resource node
  • parent (ResourceNode) – Parent node object of resource node, if any
  • api (RootNode) – API RootNode that the resource node is attached to
Returns:

raml.ResourceNode object

raml

class ramlfications.raml.RootNode

API Root Node

raw

Ordered dict of all raw data from the RAML file/string.

version

str of API version.

base_uri

str of API’s base URI.

base_uri_params

list of base URIParameter s for the base URI, or None. The order of base_uri_params will follow the order defined in the RootNode.base_uri.

uri_params

list of URIParameter s that can apply to all resources, or None. The order of uri_params will follow the order defined in the RootNode.base_uri.

protocols

list of str s of API-supported protocols. If not defined, is inferred by protocol from RootNode.base_uri.

title

str of API’s title.

docs

list of Documentation objects, or None.

schemas

list of dict s, or None.

media_type

str of default accepted request/response media type, or None.

resource_types

list of ResourceTypeNode objects, or None.

traits

list of TraitNode objects, or None.

security_schemas

list of SecurityScheme objects, or None.

resources

list of ResourceNode objects, or None.

raml_obj

The loader.RAMLDict object.

Note

TraitNode, ResourceTypeNode, and ResourceNode all inherit the following BaseNode attributes:

class ramlfications.raml.BaseNode
name

str name of Node

raw

dict of the raw data of the Node from the RAML file/string

root

Back reference to the Node’s API RootNode object.

headers

list of Node’s Header objects, or None

body

list of Node’s Body objects, or None

responses

list of Node’s Response objects, or None

uri_params

list of Node’s URIParameter objects, or None. The order of uri_params will follow the order defined in the ResourceNode.absolute_uri.

base_uri_params

list of Node’s base URIParameter objects, or None. The order of base_uri_params will follow the order defined in the ResourceNode.absolute_uri.

query_params

list of Node’s QueryParameter objects, or None

form_params

list of Node’s FormParameter objects, or None.

media_type

str of supported request MIME media type. Defaults to RootNode’s media_type.

description

str description of Node.

protocols

list of str s of supported protocols. Defaults to RootNode.protocols.

class ramlfications.raml.TraitNode

Object representing a RAML Trait.

In addition to the BaseNode-defined attributes, TraitNode also has:

usage

Usage of trait

class ramlfications.raml.ResourceTypeNode

Object representing a RAML Resource Type.

In addition to the BaseNode-defined attributes, ResourceTypeNode also has:

display_name

str of user-friendly name of resource type, defaults to BaseNode.name

type

str name of inherited ResourceTypeNode object, or None.

method

str of supported method.

Removes the ? if present (see optional).

usage

str usage of resource type, or None

optional

bool resource type attributes inherited if applied resource defines method (i.e. there is a ? in resource type method).

is_

list of assigned trait names (str), or None

traits

list of assigned TraitNode objects, or None

secured_by

list of str s or dict s of assigned security scheme, or None.

If str, then the name of the security scheme.

If dict, the key is the name of the scheme, the values are the parameters assigned (e.g. relevant OAuth 2 scopes).

security_schemes

list of assigned parameters.SecurityScheme objects, or None.

class ramlfications.raml.ResourceNode
parent

parent ResourceNode object if any, or None.

method

str HTTP method for resource, or None.

display_name

str of user-friendly name of resource. Defaults to name.

path

str of relative path of resource.

absolute_uri

str concatenation of absolute URI of resource: RootNode.base_uri + path.

is_

list of str s or dict s of resource-assigned traits, or None.

traits

list of assigned TraitNode objects, or None.

type

str of the name of the assigned resource type, or None.

resource_type

The assigned ResourceTypeNode object from ResourceNode.type, or None

secured_by

list of str s or dict s of resource-assigned security schemes, or None.

If a str, the name of the security scheme.

If a dict, the key is the name of the scheme, the values are the parameters assigned (e.g. relevant OAuth 2 scopes).

security_schemes

Parameters

Note

The URIParameter, QueryParameter, FormParameter, and Header objects all share the same attributes.

class ramlfications.parameters.URIParameter

URI parameter with properties defined by the RAML specification’s “Named Parameters” section, e.g.: /foo/{id} where id is the name of the URI parameter.

class ramlfications.parameters.QueryParameter

Query parameter with properties defined by the RAML specification’s “Named Parameters” section, e.g. /foo/bar?baz=123 where baz is the name of the query parameter.

class ramlfications.parameters.FormParameter

Form parameter with properties defined by the RAML specification’s “Named Parameters” section. Example:

curl -X POST https://api.com/foo/bar -d baz=123

where baz is the Form Parameter name.

class ramlfications.parameters.Header

Header with properties defined by the RAML spec’s ‘Named Parameters’ section, e.g.:

curl -H 'X-Some-Header: foobar' ...

where X-Some-Header is the Header name.

name

str of the name of parameter.

raw

dict of all raw data associated with the parameter from the RAML file/string.

description

str parameter description, or None.

display_name

str of a user-friendly name for display or documentation purposes.

If displayName is not specified in RAML data, defaults to name.

min_length

int of the parameter’s minimum number of characters, or None.

Note

Only applies when the parameter’s primative type is string.

max_length

int of the parameter’s maximum number of characters, or None.

Note

Only applies when the parameter’s primative type is string.

minimum

int of the parameter’s minimum value, or None.

Note

Only applies when the parameter’s primative type is integer or number.

maximum

int of the parmeter’s maximum value, or None.

Note

Only applies when the parameter’s primative type is integer or number.

example

Example value for parameter, or None.

Note

Attribute type of example will match that of type.

default

Default value for parameter, or None.

Note

Attribute type of default will match that of type.

repeat

bool if parameter can be repeated. Defaults to False.

pattern

str of a regular expression that parameter of type string must match, or None if not set.

enum

list of valid parameter values, or None.

Note

Only applies when parameter’s primative type is string.

type

str representation of the primative type of parameter. Defaults to string if not set.

required

bool if parameter is required.

Note

Defaults to True if URIParameter, else defaults to False.

class ramlfications.parameters.Body

Body of the request/response.

mime_type

str of the accepted MIME media types for the body of the request/response.

raw

dict of all raw data associated with the Body from the RAML file/string

schema

dict of body schema definition, or None if not set.

Note

Can not be set if mime_type is multipart/form-data or application/x-www-form-urlencoded

example

dict of example of schema, or None if not set.

Note

Can not be set if mime_type is multipart/form-data or application/x-www-form-urlencoded

form_params

list of FormParameter objects accepted in the request body.

Note

Must be set if mime_type is multipart/form-data or application/x-www-form-urlencoded. Can not be used when schema and/or example is defined.

class ramlfications.parameters.Response

Expected response parameters.

code

int of HTTP response code.

raw

dict of all raw data associated with the Response from the RAML file/string

description

str of the response description, or None.

headers

list of Header objects, or None.

body

list of Body objects, or None.

method

str of HTTP request method associated with response.

class ramlfications.parameters.Documentation

User documentation for the API.

title

str title of documentation

content

str content of documentation

class ramlfications.parameters.SecurityScheme

Security scheme definition.

name

str name of security scheme.

raw

dict of all raw data associated with the SecurityScheme from the RAML file/string

type

str of type of authentication

described_by

Header s, Response s, QueryParameter s, etc. that is needed/can be expected when using security scheme.

description

str description of security scheme.

settings

dict of security schema-specific information

class ramlfications.parameters.Content(data)

Returns documentable content from the RAML file (e.g. Documentation content, description) in either raw or parsed form.

Parameters:data (str) – The raw/marked up content data.
raw

Return raw Markdown/plain text written in the RAML file

html

Returns parsed Markdown into HTML

Loader

class ramlfications.loader.RAMLLoader

Extends YAML loader to load RAML files with !include tags.

load(raml)

Loads the desired RAML file and returns data.

Parameters:raml – Either a string/unicode path to RAML file, a file object, or string-representation of RAML.
Returns:Data from RAML file
Return type:dict

Validate

Functions are used when instantiating the classes from ramlfications.raml.

ramlfications.validate.validate_mime_type(value)

Assert a valid MIME media type for request/response body.

Tree

ramlfications.tree.tree(load_obj, color, output, verbosity, validate, config)

Create a tree visualization of given RAML file.

Parameters:
  • load_obj (dict) – Loaded RAML File
  • color (str) – light, dark or None (default) for the color output
  • output (str) – Path to output file, if given
  • verbosity (str) – Level of verbosity to print out
Returns:

ASCII Tree representation of API

Return type:

stdout to screen or given file name

Raises:
  • InvalidRootNodeError

    API metadata is invalid according to RAML specification.

  • InvalidResourceNodeError

    API resource endpoint is invalid according to RAML specification.

  • InvalidParameterError

    Named parameter is invalid according to RAML specification.

Project Information

Changelog

0.1.9 (2015-12-24)

Happy holidays!

  • Fix resource type inheritance (Issue 23 & Issue 47)
  • Preserve order of baseUriParameters and uriParameters in API root and resource nodes (Issue 37)
  • Fix missing URI parameters if not defined/declared inline (Issue 56)
  • Fix how arguments are passed into pytest in setup.py (PR 55 - thank you matiasb!)

0.1.8 (2015-10-07)

  • Fix incorrect/incomplete behavior optional properties of Resource Types (Issue 44).

  • Fix protocols inheritance (Issue 44).

  • Partial fix for Issue 23 - incorrect resource type inheritance

    • When a resource type is defined with one method that is optional and is applied to a resource that does not have that method defined, the resource’s method should not inherit from the optional method
    • When a resource inherits a resource type but explicitly defines named parameters, the named parameters in the resource should overwrite those that are inherited

0.1.7 (2015-08-20)

Added:

0.1.6 (2015-08-03)

Added:

  • waffle.io page to documentation for project management overview

Fixed:

  • Parse errors when RAML file would have empty mappings (Issue 30)
  • Switch yaml.Loader to yaml.SafeLoader (Issue 26)
  • Update documentation to reflect rearrangement of errors (Issue 27)
  • Remove default parameter from being required for baseURIParameters (Issue 29)
  • Pin mock library for tox tests (Issue 22)
  • Experimenting with speeding up pypy tests within tox on Travis

0.1.5 (2015-06-05)

Fixed:

  • Configuration parsing for validation/production. Thanks vrajmohan!
  • Parsing of response bodies (fixes Issue 12). Thanks Igor!

0.1.4 (2015-05-27)

Added:

  • Support for recursive !includes in RAML files (0.1.3 would handle the error, now actually supports it. Thanks Ben for your PR!).

0.1.3 (2015-05-14)

Added:

  • New #ramlfications channel on freenode (web chat link)! Come chat, I’m lonely.
  • Documentation for configuration and the update command.

Fixed:

  • Handle recursive/cyclical !includes in RAML files for now (PR)
  • Encoding issues from upgrading to tox 2.0
  • tests/test_utils.py would create ramlfications/data/supported_mime_types.json; now mocked out.

0.1.2 (2015-04-21)

Fixed:

  • pypy 2.5.x would fail a parser test because order of list was not expected

0.1.1 (2015-04-21)

New:

  • Added ability to parse IANA-supported MIME media types
  • Added update command for user to update IANA-supported MIME types if/when needed

0.1.0a1 (2015-04-18)

Initial alpha release of ramlfications!

License and Hall of Fame

ramlfications is licensed under the Apache 2.0 license. The full license text can be also found in the source code repository.

Credits

ramlfications is written and maintained by Lynn Root and thanks various contributors:

How To Contribute

Every open source project lives from the generous help by contributors that sacrifice their time and ramlfications is no different.

To make participation as pleasant as possible, this project adheres to the Code of Conduct by the Python Software Foundation.

Here are a few hints and rules to get you started:

  • Current status of the project (including issues & PRs) can be seen on our waffle.io page.

  • Meaning of GitHub labels:
    • help wanted: Feeling generous with your time? These issues are up for grabs. Ask any questions in the comments of the issue.
    • in progress: This issue is currently being worked on.
    • ready: Issue/bug has been confirmed, and is available for anyone to work on.
    • feature: Feature/idea to extend ramlfications.
    • bug: A bug or issue within ramlfications.
    • no repro: A filled bug that can not be reproduced (feel free to comment/reopen if you find you are seeing this bug).
    • wontfix: A filled issue deemed not relevant to the project in some way.
    • duplicate: Either duplicate issue or PR.
  • Take a look at the Wishlist for inspiration.

  • Any GitHub issue that is labeled ready is up for grabs.

  • Add yourself to the AUTHORS.rst file in an alphabetical fashion. Every contribution is valuable and shall be credited.

  • If your change is noteworthy, add an entry to the changelog.

  • No contribution is too small; please submit as many fixes for typos and grammar bloopers as you can!

  • Always add tests and docs for your code. This is a hard rule; patches with missing tests or documentation won’t be merged – if a feature is not tested or documented, it doesn’t exist.

  • Obey PEP 8 and PEP 257.

  • Write good commit messages.

Note

If you have something great but aren’t sure whether it adheres – or even can adhere – to the rules above: please submit a pull request anyway!

In the best case, we can mold it into something, in the worst case the pull request gets politely closed. There’s absolutely nothing to fear.

Thank you for considering to contribute to ramlfications!

Indices and tables