How to write a driver

Start by copying an existing driver, closest to the underlying datastore. You can find the list of existing drivers here. The driver is called by the DataHandler and must provide a method for the main functionality it implements. 

Let's walk through each one - the driver you are writing - lets call it foo.

The constructor 

The returns an instance of DriverFoo.
 
var DriverFoo = function (handler,tableName,id,optOb)  {
  var siloId = tableName;
  var self = this;
  var parentHandler = handler;
  var enums = parentHandler.getEnums(); 
...
...
...
    return self;
}

 argument type purpose
 handler DataHandler  The DataHandler object that calls the driver
 tableName stringEquivalent to a table, a parse.com class or some other unique way of segregating data handled by this driver instance. This is known as the siloId
 id string An optional string that uniquely identifies the set of tables. This is roughly equivalent to a database name or a set of tables.
 optOb object An object that may be used in your driver, such as a datbase object

Mandatory methods

 name returns purpose
 getDriveHandle objectreturns the handle used by the driver. A handle is the object the driver uses to access the native API and may have been passed by the DataHandler in optOb, or constructed in the Driver - for example a sheet object
getTableName string returns the siloId
remove(queryOb, queryParams) resultObject remove data that match the query object and query parameters
save(data) resultObject saves the array of objects in data
query(queryOb,queryParams, optKeepIds) resultObject returns data that match the query object and query parameters. Sometimes databases add fields like created date and so on. If optKeepIds is false, these must be first be removed from the returned data
count(queryOb,queryParams) resultObject returns the count of objects that match the queryOb and queryParams. 
 getVersion string returns the handler name and version




resultObject


Each method should return a resultObject. This is created by a method of the DataHandler, and is called like this. 

parentHandler.makeResults (handleCode,handleError,resultArray,driverIds,handleKeys);

 name type purpose
 handleCode enums.CODE the success/error code from the available choices in the data handler ENUM
 handleError string The error message associated with enums.CODE is generated automatically. Anything here is supplemental
 resultArray array of objects The results from the driver action
 driverIds array of objects if keepIds is true in a query, you need to return an array of any additional ids and other control data that the database api adds to the record (which you will have stripped out of the resultArray)
 handleKeys array of keys  if keepIds is true in a query, you need to return an array of key values that you could later use to identify a specific object

 

queryParams

This is an object like {sort:'country',limit:20,skip:100}. You should use parentHandler.getQueryParams (queryParams) to return an array of validated objects from your param object, sorted in the correct order (for example sort comes before limit). it Will return a resultObject. If resultObject.handleCode is enums.CODE.OK then all the parameters were valid. You can access and execute them through 

resultObject.data.forEach (function (p) {

    //p.param - the name
    //p.value - the supplied value
    //depending on the parameter  these are also available
    //p.sortKey
    //p.sortDescending 
    //p.limit
    //p.skip

});

At a minimum your driver should implement skip, limit and sort. Other parameters are passed through as provided. If you see one you havent implemented, you should error with enums.CODE.PARAMNOTIMPLEMENTED

Useful functions 

These are available from the DataHandler and can be called from the driver.
 name returns purpose
parentHandler .makeResults (handleCode, handleError,resultArray) resultObject should be called at the end of your function to construct a standard resultObject format
parentHandler.getQueryParams (queryParams) resultObject used to validate the values given for queryParams and sort then in the correct order (for example so that sort comes before limit). resultObject.data will contain an array of objects that are the parameters and values to use to modify the query search results.
parentHandler.generateUniqueString() string you may need a unique string. This will generate one.
parentHandler.getEnums();  object gets the constants defined for all drivers.
 parentHandler.rateLimitExpBackoff ( callBack, sleepFor , maxAttempts, attempts ) whatever your callback returns Does an automatic exponential back off if you callback() fails for some rate limited error. For more details see Backing off on rate limiting

There are also data handler functions for object flattening and constraints processing as described in Dealing with constraints

Error handling

All errors are defined in enums.CODE and these codes should be passed as handleCode to parentHandler.makeResults along with any additional comments in handleError.
 
A typical error might be
    catch(err) {
      handleError = err;
      handleCode =  enums.CODE.DRIVER;
    }


Constants for error handling.

The can be accessed through parentHandler.getEnums(); 

For more on this, see Database abstraction with google apps script. If you would like to contribute a driver to the library, or to improve an existing one, please contact me on  G+ or our forum 

For help and more information join our forum,follow the blog or follow me on twitter .


Comments