Overview
CompoSR is a nodeJS middleware, built on top of express, for Corbel.
It uses the composr-core capabilities and offers developers the ability to make their own specific application API with a Corbel generic backend.
Composr is responsible for composing phrases of code than can be reused by multiple applications. Those phrases can use all the methods exposed by corbel-js and some extra useful libraries.
wiki: A composer (Latin com+ponere, literally "one who puts together") is a person who creates music.
wiki: In music and music theory, phrase and phrasing are concepts and practices related to grouping consecutive melodic notes, both in their composition and performance. A musical work is typically made up of a melody that consists of numerous consecutive phrases.
Diagram
QuickStart
Configuration
You can send the following environment variables (or define a environment config file under src/config/[ENV].json
).
Default config file
{
"rabbitmq.host": "RABBIT_HOST",
"rabbitmq.port": "RABBIT_PORT",
"rabbitmq.username": "RABBIT_USERNAME",
"rabbitmq.password": "RABBIT_PASSWORD",
"rabbitmq.reconntimeout": 10000,
"rabbitmq.event": "class io.corbel.event.ResourceEvent",
"bootstrap.retrytimeout": 10000,
"services.timeout": 3000,
"services.retries": 3,
"services.time": 100,
"timeout": 2000,
"corbel.composr.credentials": {
"clientId": "CLIENT_ID",
"clientSecret": "CLIENT_SECRET",
"scopes": "composr:comp:base"
},
"corbel.driver.options": {
"urlBase": "https://{{module}}corbel-domain.io/"
},
"logLevel": "error",
"logFile": "logs/composr.log",
"syslog" : false,
"bodylimit" : "50mb"
}
Environment variables
CREDENTIALS_CLIENT_ID
CREDENTIALS_CLIENT_SECRET
CREDENTIALS_SCOPES
URL_BASE
LOG_LEVEL
LOG_FILE
RABBITMQ_HOST
RABBITMQ_PORT
RABBITMQ_USERNAME
RABBITMQ_PASSWORD
SERVICES_TIMEOUT
SERVIES_RETRIES
SERVICES_TIME
Phrases
Phrases is one of the CompoSR strongest capabilities, they are JSON models that can define a dinamic endpoint.
Each Phrase has an endpoint and the list of HTTP verbs it can handle (POST, PUT, GET, DELETE) with their code associated.
See the following Phrase model.
{
"url": "user/:userID",
"get": {
"code": "res.status(200).send({title: 'hello world', user: req.params.userID});",
"doc": {
"description": "Phrase description",
"queryParameters": {
"param1": {
"type": "number",
"description": "Param description",
"default": 0
}
},
"responses": {
"200": {
"body": {
"application/json": {
"schema": "{\n\t"$schema": "http://json-schema.org/schema",\n\t"type": "object",\n\t"description": "A canonical song",\n\t"properties": {\n\t\t"title": {\n\t\t\t"type": "String"\n\t\t},\n\t\t"artist": {\n\t\t\t"type": "String"\n\t\t}\n\t},\n\t"required": ["title", "artist"]\n}"
}
}
}
}
}
}
}
Routing
Composr Phrases have a similar routing mechanism than expressJS. You can define optional and fixed parameters on the urls by following this conventions:
:param
: Mandatory parameter:param?
: Optional parameter
Some examples
user/:userId
user/status/:optionalParam?
thing/one
{
"url": "paramsExample/:pathparam",
"get": {
"code": "res.status(200).send('path param: ' + req.params.pathparam + ', query param: ' + req.query.queryparam);"
},
"post": {
},
"put": {
}
}
Writting CompoSR phrases
On execution time all the phrases are wrapped inside this Function
closure, meaning you can access any of it's params:
function phrase(req, res, next, corbelDriver, domain, require){
//Your phrase code
}
- req, res, next : express request parameters
- corbel: Corbel JavaScript SDK with an extended
corbel.generateDriver
function that generates corbelDriver
instances with the correct urlBase
. - corbelDriver : An instance of corbelDriver provided by corbel, instantiated with your
Authorization
header if provided - require : A package and snippet requirer
Phrases examples:
count
value in collections query
{
"url": "countExample",
"get": {
"code":
}
}
where code
should be a string with, this corbel-js code snippet:
corbelDriver.resources.collection('test:ComposrTest').get(undefined, {
aggregation: {
$count: '*'
}
}).then(function(response) {
res.status(200).send(response.data.count);
.catch(function(error) {
res.status(500).send(error);
});
Login a client
Phrase code :
var corbel = require('corbel-js');
var ComposrError = require('ComposrError');
var utils = require('composrUtils');
function validateParams(){
return Promise.resolve()
.then(function(){
if (!req.body || !req.body.jwt) {
throw new ComposrError('error:jwt:undefined', '', 401);
}
});
}
function loginClient(){
var corbelDriver = corbel.generateDriver({
iamToken: '',
domain : domain
});
return corbelDriver.iam.token().create({
jwt: req.body.jwt
});
}
validateParams()
.then(loginClient)
.then(function(response){
res.status(200).send(response.data);
})
.catch(function(err){
res.status(err.status).send(err);
});
How can I generate Phrase models ?
we are developing some cool tools that you will be able to use in order to avoid thinking about phrase models, and just worry about the code.
Code snippets
Code snippets are a minor form of phrases
, they are accesible through the require
function on your phrases.
var mySnippet = require('snippet-mySnippet');
Corbel domains
can only access it's own snippets, the Snippet syntax is the following one:
var myModel = function(options){
this.options = options;
}
exports(myModel);
Those snippets are also stored in Corbel as JSON models.
{
"id": "domain:myModelSnippet",
"codehash": "BASE64CodeHash"
}
Reference
API design best practices
Naming
- Use nouns not verbs
- Use plural nouns
Resource | GET (read) | POST (create) | PUT (update) | DELETE |
---|
/cars | Returns a list of cars | Create a new ticket | Bulk update of cars | Delete all cars |
/cars/711 | Returns a specific car | Method not allowed (405) | Updates a specific ticket | Deletes a specific ticket |
/purchase | Get al purchases | Create a new purchase | Bulk update of purschases | Delete all purchases |
/purchase/85 | Returns a purchase | Method not allowed (405) | Updates a specific purchase | Delete all purchases |
Resource GET
read POST
create PUT
update DELETE
/cars Returns a list of cars Create a new ticket Bulk update of cars Delete all cars
/cars/711 Returns a specific car Method not allowed (405) Deletes a specific ticket
Versioning your phrases
A simple way to achieve this is definning the phrase version in the url, like this
{
"url": "v1/paramsExample/:pathparam",
"get": { ... }
}
A phrase version should change only if the phrase contract is broken
Reference
Run in a docker container
-
clone repo
-
build image
docker build -t <username>/corbel-composr .
-
run container
docker run -d -p 3000:3000 --name="corbel-composr" <username>/corbel-composr
-
start/stop container
docker start/stop corbel-composr
Tests
npm test
Coverage
grunt test:coverage
Debug
Requires node-inspector
npm install -g node-inspector
-
Server
npm run debug
-
Tests
npm run test:debug
Logs
Logs are written to the linux syslog and in the logs folder.
You can set logFile
and logLevel
in your config file.
Available log levels can be found at winston's npm page:
You can disable syslog by setting syslog
property to false
in the config file.
Postman Playground
- Get postman
- Import corbel-composr collection:
https://raw.githubusercontent.com/corbel-platform/corbel-composr/master/doc/postman/postman.json
- Import evironment example:
https://raw.githubusercontent.com/corbel-platform/corbel-composr/master/doc/postman/environment.example.json
- Import globals:
https://raw.githubusercontent.com/corbel-platform/corbel-composr/master/doc/postman/globals.example.json
- Enjoy!