Version 0.9
The RBR (RedBaRell) Language is a syntax used to describe web services.
It’s organized into:
RBR is organized into definitions containing variables or operations, and global definitions.
The general syntax is:
global name "value";
type binary python:Binary;
meta (
title "this",
version 1.0,
description "That"
);
path somepath (
method GET,
url /,
use python:somecode,
);
Every section or global ends with a semi-colon, and every definitin is separated by a comma.
There’s a special unique meta section that’s used to define a few metadata:
meta (
title "this",
version 1.0,
description "That"
);
In sections, for some variables, you can define multiple elements:
path Foo (
variable1 (
field1 value1,
operation,
field2 value2
)
);
Here’s a full example of a valid RBR file that defines an API to shorten URLs:
meta (
description """An URL Shortener""",
version 1.0
);
path shorten (
description "Shortens an URL",
method POST,
url /shorten,
request-body (
description "Contains the URL to shorten"
),
response-body (
description "The shortened URL"
),
response-headers (
set Content-Type "text/plain"
),
response-status (
describe 200 "Success",
describe 400 "I don't like the URL provided"
),
use python:shortme.shorten.shorten
);
RBR recognizes these types:
Paths are described using the Routes syntax (see XXX), and always start with /.
XXX more on paths.
Every location is suffixed with a type. Right now RBR recognizes:
Examples:
Every path definition can contain these variables:
The meta section can contain:
[1] | (1, 2, 3, 4, 5, 6, 7) Single value |
[2] | (1, 2, 3, 4, 5, 6) Multiple values in a subsection. Subsections can contain a description, some variables and some operations. |
[3] | (1, 2, 3) Mandatory |
Every section can contain extra custom fields, as long as they are suffixed by x- so they don’t conflict with a future version of the RBR DSL. Examples: x-author, x-request-max-size, etc. The reference implementation is not interpreting those fields, but they are loaded in the AST.
Each section can contain one or several operations. Operations can be used to:
You can check a request or response header or body, using one of these expressions:
The first form can be used to validate a body. For example, to check that the request body is json and return 400 if not, you can write:
request-body (
unless type is json return 400
)
The second form is to be used for headers:
request-headers (
unless X-Back-Off is int return 400
)
RBR provides a very few pre-defined types for these operations:
But you can define your own types. See Defining custom types.
The last form can be used to call some custom function. Basic authentication example:
request-headers (
unless Authorization validates with python:auth return 401
)
Will return a 401 unless auth() returns True.
You can alter the value of a header or body using alter with code, where code() is a callable that will get the value to alter, and return the result.
For example, if you want to return a compressed version of a response that contains a CSS stylesheet, you can write:
response-body (
alter with python:somemodule.compress_css
)
Where compress_css() is a function that returns a compressed version of the body.
You can directly set a header, using set header value. For instance, if you want to set the Content-Type of a response to “text/css”:
response-headers (
set content-type "text/css"
)
describe code text will let you describe every status code for the response.
Example:
response-status (
describe 200 "Success",
describe 400 "The request is probably malformed",
describe 401 "Authentication failure"
)
As explained earlier, every section and subsection in the DSL file can contain a description. Descriptions are useful to document the web services:
path capitalize (
description "A web service with several post/pre processing",
...
request-body (
description "Send a string in json and the server returns it Capitalized.",
),
response-body (
description "The string, Capitalized !",
)
);
RBR provides a very few pre-defined types for check operations:
To define a new type, you can use a type name value definition, where name is the name of the type and value a code location.
The code location is instanciated, then invoked everytime a type needs to be chacked. It receives the value and must return True or False.
Example:
type blob python:Blob;
Corresponding code:
class Blob:
def __call__(self, value):
return value.startswith('blob:')
The meta section allows you to define a title, a description and a version for your application.
Example:
meta (
title "RedBarrel Application",
version 1.1,
description """
This is a RedBarrel App !
"""
);
Requests to a given url can be proxied to another server.
Example:
path shorten (
description "Shortens an URL",
method POST,
url /shorten,
request-body (
description "Contains the URL to shorten"
),
response-body (
description "The shortened URL"
),
response-headers (
set Content-Type "text/plain"
),
response-status (
describe 200 "Success",
describe 400 "I don't like the URL provided"
),
use proxy:http://localhost:5000
);
The request and response can be checked as usual, and the request is eventually proxied to http://localhost:5000 then the response returned.
This is useful if you want to use another server to build the response for a given service.