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.
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
- Local file with & without fragments
- Remote file with & without fragments (over HTTP or HTTPS only)
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¶
raml_versions
0.8
HTTP Methods¶
http_methods
GET
, POST
, PUT
, DELETE
, PATCH
, HEAD
, OPTIONS
, TRACE
, CONNECT
Authentication Schemes¶
auth_schemes
HTTP Response Codes¶
resp_codes
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¶
protocols
HTTP
, HTTPS
MIME Media Types¶
media_types
application\/[A-Za-z.-0-1]*+?(json|xml)
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 withparser.parse_raml()
to return araml.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 withvalidate.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 currentnode
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 baseURIParameter
s for the base URI, orNone
. The order ofbase_uri_params
will follow the order defined in theRootNode.base_uri
.
-
uri_params
¶ list
ofURIParameter
s that can apply to all resources, orNone
. The order ofuri_params
will follow the order defined in theRootNode.base_uri
.
-
protocols
¶ list
ofstr
s of API-supported protocols. If not defined, is inferred by protocol fromRootNode.base_uri
.
-
title
¶ str
of API’s title.
-
docs
¶ list
ofDocumentation
objects, orNone
.
-
schemas
¶ list
ofdict
s, orNone
.
-
media_type
¶ str
of default accepted request/response media type, orNone
.
-
resource_types
¶ list
ofResourceTypeNode
objects, orNone
.
-
security_schemas
¶ list
ofSecurityScheme
objects, orNone
.
-
resources
¶ list
ofResourceNode
objects, orNone
.
-
raml_obj
¶ The
loader.RAMLDict
object.
-
Note
TraitNode
, ResourceTypeNode
, and
ResourceNode
all inherit the following BaseNode
attributes:
-
class
ramlfications.raml.
BaseNode
¶ -
name
¶ str
name ofNode
-
raw
¶ dict
of the raw data of theNode
from the RAML file/string
-
uri_params
¶ list
ofNode
’sURIParameter
objects, orNone
. The order ofuri_params
will follow the order defined in theResourceNode.absolute_uri
.
-
base_uri_params
¶ list
ofNode
’s baseURIParameter
objects, orNone
. The order ofbase_uri_params
will follow the order defined in theResourceNode.absolute_uri
.
-
query_params
¶ list
ofNode
’sQueryParameter
objects, orNone
-
form_params
¶ list
ofNode
’sFormParameter
objects, orNone
.
-
description
¶ str
description ofNode
.
-
protocols
¶ list
ofstr
s of supported protocols. Defaults toRootNode.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 toBaseNode.name
-
type
¶ str
name of inheritedResourceTypeNode
object, orNone
.
-
usage
¶ str
usage of resource type, orNone
-
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
), orNone
-
secured_by
¶ list
ofstr
s ordict
s of assigned security scheme, orNone
.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 assignedparameters.SecurityScheme
objects, orNone
.
-
-
class
ramlfications.raml.
ResourceNode
¶ -
parent
¶ parent
ResourceNode
object if any, orNone
.
-
method
¶ str
HTTP method for resource, orNone
.
-
display_name
¶ str
of user-friendly name of resource. Defaults toname
.
-
path
¶ str
of relative path of resource.
-
absolute_uri
¶ str
concatenation of absolute URI of resource:RootNode.base_uri
+path
.
-
is_
¶ list
ofstr
s ordict
s of resource-assigned traits, orNone
.
-
type
¶ str
of the name of the assigned resource type, orNone
.
-
resource_type
¶ The assigned
ResourceTypeNode
object fromResourceNode.type
, orNone
-
secured_by
¶ list
ofstr
s ordict
s of resource-assigned security schemes, orNone
.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}
whereid
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
wherebaz
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, orNone
.
-
display_name
¶ str
of a user-friendly name for display or documentation purposes.If
displayName
is not specified in RAML data, defaults toname
.
-
min_length
¶ int
of the parameter’s minimum number of characters, orNone
.Note
Only applies when the parameter’s primative
type
isstring
.
-
max_length
¶ int
of the parameter’s maximum number of characters, orNone
.Note
Only applies when the parameter’s primative
type
isstring
.
-
minimum
¶ int
of the parameter’s minimum value, orNone
.Note
Only applies when the parameter’s primative
type
isinteger
ornumber
.
-
maximum
¶ int
of the parmeter’s maximum value, orNone
.Note
Only applies when the parameter’s primative
type
isinteger
ornumber
.
-
example
¶ Example value for parameter, or
None
.Note
Attribute type of
example
will match that oftype
.
-
default
¶ Default value for parameter, or
None
.Note
Attribute type of
default
will match that oftype
.
-
repeat
¶ bool
if parameter can be repeated. Defaults toFalse
.
-
pattern
¶ str
of a regular expression that parameter of typestring
must match, orNone
if not set.
-
enum
¶ list
of valid parameter values, orNone
.Note
Only applies when parameter’s primative
type
isstring
.
-
type
¶ str
representation of the primative type of parameter. Defaults tostring
if not set.
-
required
¶ bool
if parameter is required.Note
Defaults to
True
ifURIParameter
, else defaults toFalse
.
-
-
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 theBody
from the RAML file/string
-
schema
¶ dict
of body schema definition, orNone
if not set.Note
Can not be set if
mime_type
ismultipart/form-data
orapplication/x-www-form-urlencoded
-
example
¶ dict
of example of schema, orNone
if not set.Note
Can not be set if
mime_type
ismultipart/form-data
orapplication/x-www-form-urlencoded
-
form_params
¶ list
ofFormParameter
objects accepted in the request body.Note
Must be set if
mime_type
ismultipart/form-data
orapplication/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 theResponse
from the RAML file/string
-
description
¶ str
of the response description, orNone
.
-
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 theSecurityScheme
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
orNone
(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!
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
toyaml.SafeLoader
(Issue 26) - Update documentation to reflect rearrangement of errors (Issue 27)
- Remove
default
parameter from being required forbaseURIParameters
(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:
0.1.4 (2015-05-27)¶
Added:
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 createramlfications/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.
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 extendramlfications
.bug
: A bug or issue withinramlfications
.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.
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
!