Define Custom RAML Properties Using Annotations

1. Introduction

In this, the fourth article in our series on RAML – the RESTful API
Modeling Language – we demonstrate how to use annotations to define
custom properties
for a RAML API specification. This process is also
referred to as extending the metadata of the specification.

Annotations may be used to provide hooks for RAML processing tools
requiring additional specifications that lie outside the scope of the
official language.

2. Declaring Annotation Types

One or more annotation types may be declared using the top-level
annotationTypes property.

In the simplest of cases, the annotation type name is all that is
needed to specify it, in which case the annotation type value is
implicitly defined to be a string:

annotationTypes:
  simpleImplicitStringValueType:

This is the equivalent to the more explicit annotation type definition
shown here:

annotationTypes:
  simpleExplicitStringValueType:
    type: string

In other cases, an annotation type specification will contain a value
object that is considered to be the annotation type declaration.

In these cases, the annotation type is defined using the same syntax
as a data type with the addition of two optional attributes:
allowedTargets, whose value is either a string or an array of strings
limiting the types of target locations to which an annotation may be
applied, and allowMultiple, whose boolean value states whether or not
the annotation may be applied more than once within a single target
(default is false).

Here is a brief example declaring an annotation type containing
additional properties and attributes:

annotationTypes:
  complexValueType:
    allowMultiple: true
    properties:
      prop1: integer
      prop2: string
      prop3: boolean

2.1. Target Locations Supporting the Use of Annotations

Annotations may be applied to (used in) several root-level target
locations, including the root level of the API itself, resource types,
traits, data types, documentation items, security schemes,
libraries, overlays, extensions, and other annotation types.

Annotations may also be applied to security scheme settings,
resources, methods, response declarations, request bodies,
response bodies, and named examples.

2.2. Restricting an Annotation Type’s Targets

To restrict an annotation type to one or more specific target location
types, you would define its allowedTargets attribute.

When restricting an annotation type to a single target location type,
you would assign the allowedTargets attribute a string value
representing that target location type:

annotationTypes:
  supportsOnlyOneTargetLocationType:
    allowedTargets: TypeDeclaration

To allow multiple target location types for an annotation type, you
would assign the allowedTargets attribute an array of string values
representing those target location types:

annotationTypes:
  supportsMultipleTargetLocationTypes:
    allowedTargets: [ Library, Overlay, Extension ]

If the allowedTargets attribute is not defined on an annotation
type
, then by default, that annotation type may be applied to any of
the supporting target location types.

3. Applying Annotation Types

Once you have defined the annotation types at the root level of your
RAML API spec, you would apply them to their intended target locations,
providing their property values at each instance. The application of an
annotation type within a target location is referred to simply as an
annotation on that target location.

3.1. Syntax

In order to apply an annotation type, add the annotation type name
enclosed in parentheses () as an attribute of the target location and
provide the annotation type value properties that the annotation
type
is to use for that specific target. If the annotation type is in
a RAML library, then you would concatenate the library reference
followed by a dot (.) followed by the annotation type name.

3.2. Example

Here is an example showing how we might apply some of the annotation
types
listed in the above code snippets to various resources and
methods of our API:

/foos:
  type: myResourceTypes.collection
  (simpleImplicitStringValueType): alpha
  ...
  get:
    (simpleExplicitStringValueType): beta
  ...
  /{fooId}:
    type: myResourceTypes.item
    (complexValueType):
      prop1: 4
      prop2: testing
      prop3: true

4. Use Case

One potential use case for annotations would be defining and
configuring test cases for an API.

Suppose we wanted to develop a RAML processing tool that can generate a
series of tests against our API based on annotations. We could define
the following annotation type:

annotationTypes:
  testCase:
    allowedTargets: [ Method ]
    allowMultiple: true
    usage: |
      Use this annotation to declare a test case.
      You may apply this annotation multiple times per location.
    properties:
      scenario: string
      setupScript?: string[]
      testScript: string[]
      expectedOutput?: string
      cleanupScript?: string[]

We could then configure a series of tests cases for our /foos resource
by applying annotations as follows:

/foos:
  type: myResourceTypes.collection
  get:
    (testCase):
      scenario: No Foos
      setupScript: deleteAllFoosIfAny
      testScript: getAllFoos
      expectedOutput: ""
    (testCase):
      scenario: One Foo
      setupScript: [ deleteAllFoosIfAny, addInputFoos ]
      testScript: getAllFoos
      expectedOutput: '[ { "id": 999, "name": Joe } ]'
      cleanupScript: deleteInputFoos
    (testCase):
      scenario: Multiple Foos
      setupScript: [ deleteAllFoosIfAny, addInputFoos ]
      testScript: getAllFoos
      expectedOutput: '[ { "id": 998, "name": "Bob" }, { "id": 999, "name": "Joe" } ]'
      cleanupScript: deleteInputFoos

5. Conclusion

In this tutorial, we have shown how to extend the metadata for a RAML
API specification through the use of custom properties called
annotations.

First, we showed how to declare annotation types using the top-level
annotationTypes property and enumerated the types of target locations
to which they are allowed to be applied.

Next, we demonstrated how to apply annotations in our API and noted
how to restrict the types of target locations to which a given
annotation can be applied.

Finally, we introduced a potential use case by defining annotation
types
that could potentially be supported by a test generation tool and
showing how one might apply those annotations to an API.

For more information about the use of annotations in RAML, please
visit the
RAML
1.0 spec
.

You can view the full implementation of the API definition used for this
tutorial in
the
github project
.

« Previous

Leave a Reply

Your email address will not be published.