The database components provide a comprehensive API for retrieving and updating data, either from a scripting environment or through web services.
You might want to extend the API, which you do by extending the appropriate class script and then configuring your row, table or database to use that class script.
You can add whatever additional functionality you require. As well as adding additional methods that are accessible from scripts, the API provides three specific extension mechanisms which cover the majority of the situations.
- On a row, write a custom view that returns a different set of data from a GET operation.
- On a table, write a custom filter that controls what data is returned.
- On a row or table, write a custom service that extends the web service API, for example to add business logic.
Extending the row API
To extend the row API, you need to:
- Create a script which contains a class that extends Row Class Script.
- Set the scriptClass binding on the row type to the new script.
To extend the row class, you will need code like this.
RowClass = application.require(application.getBinding('scriptRowClass'));
MyClass = RowClass.inherit(function(){
MyClass.parent.apply(this,arguments);
// Additional constructor code goes here
});
MyClass.prototype.myMethod() {
.. your new code for myMethod() ..
}
application.put('exports',MyClass);
To add a new view, you need to modify the views object in the instance. Because this is shared with RowClass, you need to clone it first, using something like this.
MyClass.prototype.views = MyClass.prototype.extend(
{},MyClass.prototype.views);
You can then add a view, which should return an object.
MyClass.prototype.views.myview = function() {
var o = {};
o.message = 'Hello world';
return o;
}
You can now retrieve data from you view through the script API using getData('myview') or through the REST web services using ?view=myview.
To add a new service, you need to extend the services object, in the same way that you extended the getters object. The service is passed a request and should return a stringifiable JavaScript object or an error. To make service functionality available from scripts, it usually makes sense for the service to read request parameters and then call another method to perform the action.
MyClass.prototype.services = MyClass.prototype.extend(
{},MyClass.prototype.services);
MyClass.prototype.services.myservice = function(request) {
var myparam = request.getString("myparam");
return this.myMethod(myparam);
}
MyClass.prototype.myMethod(myparam) {
.. do something and return an object ..
}
You can call the service through the REST API by perform a POST and using ?action=myservice. See the REST interface for details of how to interpret incoming request parameters.
You can call the underlying method (myMethod() in the example above) from a script, rather than calling services.myservice().
Extending the table API
To extend the table API, you need to:
- Create a script which contains a class that extends Table Class Script.
- Set the scriptTableClass binding on the row type to the new script (the class to use for the table is looked up from the row type).
Extending the table class script and extending the services are exactly the same as for the row class script, as in the row API example above, except that you use the class name TableClass, rather than RowClass.
On the table class, you can also add a new filter, which is used to select what row nodes should be returned as part of a GET operation. Like getters and services, there is a filters object which you will need to extend and then add your filter to.
As an example, consider the product and range example from Data formats, and imagine you wanted a list of products for a range. To do this, you will need to:
- Read a parameter. The parameters to a query are passed in as an object. In this case we will look for the range_id query.
- From the range_id, find the range node.
- Use the range node to find related products.
TableClass = application.require(application.getBinding('scriptTableClass')); ProductClass = TableClass.inherit(function(){ ProductClass.parent.apply(this,arguments); }); ProductClass.prototype.filters =
ProductClass.prototype.extend({},ProductClass.prototype.filters); /** * Return all the products for a range by passing {range_id: 'id'}
* If id is not passed or invalid, return no products. */ ProductClass.prototype.filters.range = function(query) { var rangeId = query.range_id; if ( !rangeId ) { return []; } var range = this.getTable('range').getNodeByKey(rangeId); if ( !range ) { return []; } return this.getNodesWhereLinked('range',range); } application.put('exports',ProductClass);