Projects:Relax
<< Back to Dashboard | << Projects Viewer
|
ColdBox Relax: ReSTful Tools For Lazy Experts
What is Relax? ColdBox Relax is a set of ReSTful tools for lazy experts. We pride ourselves in helping you (the developer) work smarter and ColdBox Relax is a tool to help you document your projects faster. ColdBox Relax provides you with the necessary tools to automagically model, document and test your ReSTful services. One can think of ColdBox Relax as a way to describe ReSTful web services, test ReSTful web services, monitor ReSTful web services and document ReSTful web services–all while you relax!
Requirements
- ColdBox Relax is a ColdBox 3 Module and can be installed in any ColdBox 3 application.
- ColdFusion 8 and above.
- MySQL, MSSQL, Oracle or PostgresSQL for Log Viewing Capabilities (Optional).
Installation
Download the ColdBox Relax module and drop into your ColdBox modules folder. Start your application and visit the relax entry point:
http://myapp.com/index.cfm/relax/home
- Download - http://coldbox.org/forgebox/view/coldbox-relax
- Source - https://github.com/coldbox/coldbox-relax
Capabilities
So what can you do with Relax?
- Define your ReSTful services via our awesome Relax DSL (Programmatic or Implicit)
- Generate ColdBox Routes from described ReSTful routes
- Using this DSL we will document your entire ReSTful web service and you will be able to export the documentation in the following formats:
- HTML
- Trac Wiki Markup
- Mediawiki Wiki Markup (CodexWiki)
- Using the DSL you will also have access to our RelaxURL (pronounced "Relaxer") tool, from which you can test your ReSTful web services
- Use the RelaxURL tool to test against ANY ReSTful service
- Keep a history of your latest Relaxed test requests so you can rebuild your request and test again
- LogBox integration so you can use the RelaxLogs viewer to monitor and see your log db entries
- Programmatic DSL for configuration of your RESTful services
Videos
Relax Intro
Version 1.5 Features
Version 1.4 Features
How to start?
To start using Relax you should start by configuring the Relax module for operation, then create our Relax DSL for our ReSTful web services. Finally, we can move into the RelaxLogs setup so we can monitor the logs produced by our RESTful service.
Relax Module Configuration
Let's open the module's configuration file ModuleConfig.cfc and see what we can modify in it. Look for the settings structure, this is the configuration structure for the Relax module and you have some control over it. Below is the default configuration structure:
// Relax Configuration Settings settings = { // Relax Version: DO NOT ALTER version = this.version, // The location of the relaxed APIs, in instantiation path apiLocationPath = "#moduleMapping#.resources", // Default API to load defaultAPI = "myapi", // History stack size, the number of history items to track in the RelaxURL maxHistory = 10, // logbox integration information needed for log viewer to work // this means that it can read tables that are written using the logbox's DB Appender. relaxLogs = { // THE CF DATASOURCE NAME datasource = "relax", // THE DB TO USE FOR LOGS, AVAILABLE ADAPTERS ARE: mysql, mssql, postgres, oracle adapter = "mysql", // THE TABLE WHERE THE LOGS ARE table = "api_logs", // PAGING MAX ROWS maxRows = 50, // PAGING CARROUSEL BANDGAP bandGap = 3 } };
Relax Module Settings
And here are the settings that you can modify:
| Setting | Type | Required | Default | Description |
|---|---|---|---|---|
| apiLocationPath | instantiation path | true | #moduleMapping#.resources | The instantiation path prefix to the location where all your Relaxed services are defined under. By default it looks at the resources folder in the module. |
| defaultAPI | folderName | true | myapi | The name of the folder that holds the default API to load when Relax loads. By default it points to our sample api declaration. |
| maxHistory | numeric | true | 10 | The default number of history items to keep on a stack when making requests via the RelaxURL. |
| relaxLogs | struct | true | --- | The structure used to configure the RelaxLogs log viewer integration. |
RelaxLogs Settings
RelaxLogs structure settings:
| Setting | Type | Required | Default | Description |
|---|---|---|---|---|
| datasource | string | true | --- | The datasource registered in the ColdFusion administrator to use for connectivity |
| table | string | true | --- | The database table where all the log entries are located in. |
| adapter | string | true | --- | The adapter to use for talking to the correct database implementation. The available adapters are:
|
| maxrows | numeric | true | 50 | The maximum number of rows to retrieve in the RelaxLogs as paging is provided. |
| bandGap | numeric | true | 3 | The number of pages to display left and right of the paging carrousel when paging is provided. |
Note: To use the RelaxLogs you will need to make some configuration adjustments. Essentially, you need to configure RelaxLogs to work with a database table with columns specified by the LogBox Database Appender.
Relax DSL
Once you have configured the Relax module setup and ready for operation you can start by defining your RESTful service. You will do this by creating a Simple CFC–usually in the root of the resource folder, for simplicity–named Relax.cfc or Relax.json. In this component or JSON file, you will create several public variables (using the this scope) that will define your service's parameters, headers, resources and so much more. You can also use our programmatic DSL, which is much cleaner than implicit data structures. This CFC will represent your RESTful service and ColdBox Relax will generate documentation from it, create testable URLs for you and it is a great way for you to model your service. Please note that if you use JSON for defining your service, it must validate according to the same structure elements you will see here. http://www.jsonlint.com is a great way for you to format and validate your JSON representations.
The implicit data structure definitions could be deprecated in future versions in favor of the programmatic DSL.
| Variable | Type | Description |
|---|---|---|
| relax | struct | The structure used to define the ReSTful service |
| globalHeaders | array of structs | Where you define global headers your ReSTful service expects |
| globalParameters | array of structs | Where you define global parameters your ReSTful service expects |
| resources | array of structs | Where you define your ReSTful resource patterns. |
component{
// relax dsl
this.relax = {};
// Global Headers
this.globalHeaders = [];
// Global Parameteres
this.globalParameters = [];
// Resources
this.resources = [];
}
relax configuration
The following properties are used to understand your ReSTful service:
| Variable | Type | Description |
|---|---|---|
| title | string | The title for your ReSTful service. Used for documentation purposes |
| description | html or string | A string or HTML description of your service. This can be whatever you like. |
| entryPoint | URL or struct of URLs | The main URL entry point for your ReSTful service. e.g. http://api.coldbox.org or if your API service has different URLs or tiers you can make this key be a named structure. |
| extensionDetection | boolean | A flag that denotes if this ColdBox ReSTful service can detect the format extension via the resource URL. By default ColdBox detects an extension in any resource request. Used for documentation and routes generation. e.g. http://api.coldbox.org/my/resource.json, http://api.coldbox.org/my/resource.xml |
| validExtensions | list | A list of extensions this ReSTful service can transform to. Used for documentation and routes generation. |
| throwOnInvalidExtension | boolean | A flag that denotes if the ReSTful service will throw a 406 error when an invalid extension is used or to ignore the error. |
Here is a sample of a single entry point:
// This is where we define our ReSTful service, this is usually // our first place before even building it, we spec it out. this.relax = { // Service Title title = "My ReSTful Service", // Service Description description = "A very cool ReSTful Service", // Service entry point entryPoint = "http://www.myapi.com", // Does it have extension detection via ColdBox extensionDetection = true, // Valid format extensions validExtensions = "xml,json,jsont,wddx", // Does it throw exceptions when invalid extensions are detected throwOnInvalidExtension = false };
Here is a sample of a multiple entry point service:
// This is where we define our ReSTful service, this is usually // our first place before even building it, we spec it out. this.relax = { // Service Title title = "My ReSTful Service", // Service Description description = "A very cool ReSTful Service", // Service entry point entryPoint = { dev = "http://dev.myapi.com", staging = "http://stg.myapi.com", production = "http://www.myapi.com" }, // Does it have extension detection via ColdBox extensionDetection = true, // Valid format extensions validExtensions = "xml,json,jsont,wddx", // Does it throw exceptions when invalid extensions are detected throwOnInvalidExtension = false };
globalHeaders configuration
This section is optional and only used if you would like to describe the HTTP headers that your service will listen to in a global manner. For example, if you require a X-Auth-Token to ensure every request has been authenticated you would want to take advantage of this feature. As with the variables defined before these are an array of structures with the following keys:
| Variable | Type | Description |
|---|---|---|
| name | string | The name of the HTTP Header |
| description | html or string | A string or HTML description of your HTTP header. This can be whatever you like. |
| type | string | The type of the HTTP header, usually it could be string, boolean, numeric, binary, etc. |
| required | Boolean | Whether the header is required for any calls or not. The RelaxURL console will use this value if used. |
| default | any | A default value the header is associated with if not passed or sent to the service. |
Here is a sample:
// Global API Headers this.globalHeaders = [ // Sample global header, Available keys: name,description,required,default,type {name="apikey",description="The apikey needed for request authentication.",required=true} ];
globalParameters configuration
This section is optional and only used if you would like to describe the HTTP parameters that your service will listen to in a global manner. For example, if you require a security token to ensure every request has been authenticated, or a format variable you would want to take advantage of this feature. As with the variables defined before these are an array of structures with the following keys:
| Variable | Type | Description |
|---|---|---|
| name | string | The name of the HTTP Parameter |
| description | html or string | A string or HTML description of your HTTP Parameter. This can be whatever you like. |
| type | string | The type of the HTTP Parameter, usually it could be string, boolean, numeric, binary, etc. |
| required | Boolean | Whether the Parameter is required for any calls or not. The RelaxURL console will use this value if used. |
| default | any | A default value the Parameter is associated with if not passed or sent to the service. |
Here is a sample:
// Global API Parameters this.globalParameters = [ // Sample global parameter, Available keys: name,description,required,default,type {name="apikey",description="The apikey needed for request authentication.",required=true}, {name="format",description="An optional format variable used for content negotiation",required=false, default="json"} ];
resources configuration
The following section is essential as it describes the ReSTful resources you will expose through your service. Please remember that order is important as it will be used to generate the ColdBox routes file, which in turn are your ReSTful routes. We will create a array of structures with the following keys for each resource:
| Variable | Type | Description |
|---|---|---|
| pattern | string | The ColdBox route pattern to define, you can use ALL the same parameters you do in a ColdBox URL Mapping route. |
| handler | string | The event notation for the package+event to redirect this request to. This key is optional as the handler can be embedded also in the pattern as :handler. |
| action | string or JSON or Implicit Struct | The normal action or ReSTful action (json) to redirect this request to. This key is optional as the action can be embedded also in the pattern as :action or left blank to default it to index. ReSTful routing can also be used, which is what we recommend, by making this entry a JSON structure: e.g. action="{'get':'list', 'post':'create', 'delete':'remove'}" |
| description | HTML or string | A description about the resource you are exposing. Try to be descriptive, use HTML or make it pretty. |
| methods | list | A list of allowed HTTP methods this resource can listen to. |
| defaultMethod | string | The default method this resource responds to. By default it is set to GET. |
| defaultFormat | string | The default format this resource responds to. By default format is empty. |
| headers | array of headers | AAn array of structures where you define HTTP headers this resource can listen to. The header structure to use is the same as the global headers described before: name, description, type, required and default. |
| parameters | array of parameters | AAn array of structures where you define HTTP URL/FORM parameters this resource can listen to. The structure to use is the same as the global headers described before: name, description, type, required and default. |
| placeholders | array of placeholders | AAn array of structures where you describe the pattern placeholders (:placeholder). These placeholders get translated by ColdBox URL mappings into request collection variables, so you can use this array to define them for documentation purposes. The structure to use is the same as the global headers described before: name, description, type, required and default. |
| response | structure | A structure that can hold two keys: schemas and samples for defining them for the resource. |
Here is a sample:
// Define our Relaxed ReSTful resources in order just like you are defining routes // Each header, parameter or placeholder is a structure with the following keys: // {name="",type="",required="",default="",description=""} this.resources = [ {pattern="/api/users",handler="rest.user",action="list", defaultMethod="GET", defaultFormat="json" description="Returns all users", methods="GET",headers=[],parameters=[],placeholders=[]}, {pattern="/api/user/:username",handler="rest.user",action="{'get':'view','post':'create','put':'update','delete','remove'}", description="The representation for system users. You can also interact with creation, updating and deletion via this resource", methods="GET,POST,PUT,DELETE", headers=[], parameters=[ {name="firstName",description="The user firstname. Only used on PUT and POST operations",required="false"}, {name="lastName",description="The user lastname. Only used on PUT and POST operations",required="false"}, {name="email",description="The user email. Only used on PUT and POST operations",required="false"} ], placeholders=[{name="username",description="The resource username to interact with",required=true}]} ];
That's it! Once your DSL is defined, you can start up your relax module and get cooking with it. Here are some screenshots and videos about Relax.
Schemas & Samples
You can define samples and schemas for your resources by using the response structure by using the schemas and samples keys, which in turn are array of definitions.
response={
schemas=[
{format="json", description="The following will be returned when the format requested is JSON.", body=fileRead("#dirPath#schemas/user/user.json") },
{format="xml", description="The following will be returned when the format requested is XML.", body=fileRead("#dirPath#schemas/user/user.xsd") }
],
samples=[
{format="json", description="The basic user information will be returned in a flat object.", body=fileRead("#dirPath#samples/user/user.json")},
{format="json", description="If the requested user is not found, or some other error has occurred, the resopnse will be like the following.", body=fileRead("#dirPath#samples/user/failure.json")}
]
}
As you can see, you can define multiple declarations for each category. Each containing:
| Variable | Type | Description |
|---|---|---|
| format | string | The format of the schema or sample |
| description | string | The description of the schema or sample |
| action | string | The contents of the schema or sample, usually in the specified format like JSON or XML. |
Programmatic DSL
A part from the implicit configuration, you can use our more versatile and clean programmatic DSL to configure your relaxed resources. The Relax DSL configuration object which is mixed into a simple Relax CFC Definition Object so you can use all the methods in this CFC to define RESTful web services. All functions can be concatenated to create a nice programmatic DSL.
service()
The relaxed service information. From here you will define the RESTful service endpoints, extension detection, valid formats, and more.
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| title | any | Yes | --- | The title of the RESTful service |
| description | any | Yes | --- | The description of the RESTful service |
| entryPoint | any | Yes | --- | A simple URL or a structure of entry points |
| extensionDetection | any | No | true | Will this API do extension detection |
| validExtensions | any | No | json,jsont,xml,html,htm,rss | The valid extensions to detect |
| throwOnInvalidExtensions | any | No | false | Throw on invalid extensions or not |
defaultMethod()
Define a default HTTP method for a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| method | any | Yes | --- | The HTTP method that will be the default |
placeholder()
Add a placeholder to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| name | any | Yes | --- | The name of the placeholder |
| description | any | No | The description of the placeholder | |
| required | any | No | false | Is the placeholder required |
| default | any | No | The default value of the placeholder | |
| type | any | No | string | The type of the placeholder |
schema()
Append a schema to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| format | any | Yes | --- | The format of the schema |
| description | any | No | The description of the schema | |
| body | any | No | The body contents of the schema |
globalHeader()
Add a global header to the relax definition
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| name | any | Yes | --- | The name of the header |
| description | any | No | The description of the header | |
| required | any | No | false | Is the header required or not |
| default | any | No | The default value of this header | |
| type | any | No | string | The type of the incoming header |
resource()
Add a new resource to the API definition, you can then concatenate more methods to the same resource: methods(),description(),header(),parameter(),placeholder(),schema(),sample()
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| pattern | any | Yes | --- | The SES pattern to register for the resource |
| handler | any | No | --- | The description of the parameter |
| action | any | No | --- | Is the parameter required or not |
setPlaceHolders()
Set all the required placeholders of a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| placeholders | array | Yes | --- | Set the placeholders of a resource |
sample()
Append a sample to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| format | any | Yes | --- | The format of the sample |
| description | any | No | The description of the sample | |
| body | any | No | The body contents of the sample |
setParams()
Set all the required parameters of a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| params | array | Yes | --- | Set the parameters of a resource |
globalParam()
Add a global parameter to the relax definition
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| name | any | Yes | --- | The name of the parameter |
| description | any | No | The description of the parameter | |
| required | any | No | false | Is the parameter required or not |
| default | any | No | The default value of this parameter | |
| type | any | No | string | The type of the incoming parameter |
methods()
Add methods to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| methods | any | Yes | --- | The methods list to allow |
description()
Add a description to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| description | any | Yes | --- | The description |
defaultFormat()
Define a default return format for a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| format | any | Yes | --- | The format that will be the default |
setHeaders()
Set all the required headers of a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| headers | array | Yes | --- | Set the headers of a resource |
header()
Add a header to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| name | any | Yes | --- | The name of the header |
| description | any | No | The description of the header | |
| required | any | No | false | Is the header required |
| default | any | No | The default value of the header | |
| type | any | No | string | The type of the header |
param()
Add a param to a resource
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| name | any | Yes | --- | The name of the param |
| description | any | No | The description of the param | |
| required | any | No | false | Is the param required |
| default | any | No | The default value of the param | |
| type | any | No | string | The type of the param |
Screenshots
RelaxLogs
The RelaxLogs section of the module is a nice log viewer that can tap to any table that was used or has the columns the LogBox Database Appender talks to. This is a great way to monitor your service log entries and report on them. So let's configure it:
LogBox Service Configuration
In order for our RelaxLogs to be able to communicate with your log tables you must enable logging in your application. This is done via LogBox–which is already in all ColdBox applications. To get things working you simply need to add a Appender (or destination) that will store the log entries in a database. If you are not building a ColdBox application–which you should–then have no worries because LogBox can work with any ColdFusion application or framework. Logging your ReSTful service in this manor is helpful as it allows you to maintain a history of your ReSTful service's status and operation. All production sites are recommended to follow these steps:
1. LogBox Setup
The following is the programmatic approach to adding the LogBox Database Appender so you can log entries to the database of your choice.
//LogBox DSL logBox = { // Define Appenders appenders = { // A simple tracer appender coldboxTracer = { class="coldbox.system.logging.appenders.ColdboxTracerAppender" }, // Database Appender Registration dbAppender = { class="coldbox.system.logging.appenders.DBAppender", properties = { // The datasource to connect to dsn = "relax", // The table to log to table="api_logs", // If the table does not exist, then create it autocreate=true, // The type to use for long text inserting. textDBType="longtext" } } }, // Root Logger root = { levelmax="INFO", appenders="coldboxTracer" } };
Once you define the Appender in your configuration, LogBox can send messages to this destination. We suggest your review your LogBox skills before anything to understand how Loggers work and how you can even declare categories that point to specific destinations.
2. Custom LogBox Category
We suggest you create a special LogBox category and assign it this Database Appender destination. This will provide you with the ability that only log messages with this category will go to your database. We mostly do this to not overpopulate our DB with other kinds of log messages.
//LogBox DSL logBox = { // Define Appenders appenders = { // A simple tracer appender coldboxTracer = { class="coldbox.system.logging.appenders.ColdboxTracerAppender" }, // Database Appender Registration dbAppender = { class="coldbox.system.logging.appenders.DBAppender", properties = { // The datasource to connect to dsn = "relax", // The table to log to table="api_logs", // If the table does not exist, then create it autocreate=true, // The type to use for long text inserting. textDBType="longtext" } } }, // Root Logger root = { levelmax="INFO", appenders="coldboxTracer" }, // Custom LogBox Category categories = { myRestService = { levelMax="DEBUG", appenders="dbAppender"} } };
3. Logger Injection
Once this is done, we can inject our logger objects that LogBox provides into our handlers or domain objects that will send messages to our log tables. This is done via ColdBox's WireBox Dependency Injection engine and our Autowire DSL. The important part of this section is that we inject the logger with the category we setup in our configuration.
component name="Awesome REST Handler"{ // inject correct logger property name="logger" inject="logbox:logger:myRestService"; // log any call to any event in this awesome REST handler function preHandler(event,action){ if( logger.canDebug() ){ logger.debug("A request to relax/logs/help/ was made.", getHTTPRequestData() ); } } }
As you can see we first check if we can debug and then send a debug message to LogBox. The second argument of any Logger method can be any variable (simple or complex) that we would like to log. In our case, since it is a ReSTful service, we send the request data out for logging. Good for debugging.
4. Go Relax!
You did the hard work, now go relax! You can now configure the relaxLogs structure in the modules configuration (as shown before) and you are ready to roll.
Joseph Lamoree said
at 04:44:30 PM 25-Apr-2011
Joseph Lamoree said
at 06:19:07 PM 26-Apr-2011
Luis Majano said
at 07:16:57 PM 26-Apr-2011
Joseph Lamoree said
at 01:39:40 PM 28-Apr-2011

SideBar
User Login 




Comments (