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 | string | Equivalent 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 | object | returns 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();
var dhConstants = { DB: { SCRIPTDB:1, PARSE:2, SHEET:3, FUSION:4, ORCHESTRATE:5, DRIVE:6, IMPORTIO:7, PROPERTIES:8, DATASTORE:9, MEMORY:10, MONGOLAB:11, MYSQL:12, FIREBASE:13, SCRATCH:14 }, LOCKING : { AGGRESSIVE:1, ENABLED:2, DISABLED:3 }, TRANSACTIONS : { ENABLED:2, DISABLED:3 }, SETTINGS: { CACHEEXPIRY:60*5, // 5 minutes LOCKEXPIRY:(1+Math.pow(2,5+1))*2000, // till expiry of backoff attempts ULENGTH:6, KEEPIDS:false, VERSION:getLibraryInfo().info.name+':'+getLibraryInfo().info.version, MAXATTEMPTS:5, SLEEPFOR:2000, MAXPROPERTYNAME:100, UAKEY:'uaKey', CONSTRAINT:'__CONSTR$KEY$', NOSILO:'__NO$SILO$', EXTRAROWS:30, PROTECTEXPIRY:3*60*1000, MAX_NUMBER: 9007199254740992, // this stuff is for lucene query ranges, dont know how to do negative numbers. MIN_NUMBER: 0, MAX_STRING: '\uffff', MIN_STRING: '\0000', HIDDEN_KEY:'_$dbab$key$_' }, ACTIONS: ['query','save','remove','count','get','update'], CODE: { // negative errors are bad, postive are warning, 0 is good OK:0, CACHE:1, NULL_DATA:2, PUBLISH:3, ROLLBACK:4, TRANSACTION_INCAPABLE:5, DRIVER:-1, LOCK:-2, RESULT:-3, PROPERTY:-4, HTTP:-5, NOMATCH:2, OPERATION_NOT_ALLOWED:-6, ASSERT:-7, PARAMNOTIMPLEMENTED:-8, UNKNOWN_DRIVER:-9, HANDLE_GET:-10, REST_GET:-11, REST_ERROR:-12, NO_ACTION:-13, PARAMS_MISSING:-14, ACTION_FAILED:-15, UNDER_CONSTRUCTION:-16, OPERATION_UNSUPPORTED:-17, CLIENT_ERROR:-18, KEYS_AND_OBJECTS:-19, CHECKSUM:-20, TRANSACTION_ACTIVE:-21, TRANSACTION_ROLLBACK:-22, TRANSACTION_ROLLBACK_FAILED:-23, TRANSACTION_FAILURE:-24, TRANSACTION_ID_MISMATCH:-25, DRIVER_ASSERTION:-26, KEY_ASSERTION:-27 }, ERROR: { OK:'', CACHE:'this came from cache', DRIVER:'driver returned an error', LOCK:'unable to get a lock', RESULT:'error in driver results', PROPERTY:'invalid property/key', NOMATCH:'nothing matched the query', ASSERT:'synch or programming error', PARAMNOTIMPLEMENTED:'this driver has not implemented this parameter yet', HTTP:'some connection problem', OPERATION_NOT_ALLOWED:'policy disallows operation', UNKNOWN_DRIVER:'unknown driver type', HANDLE_GET:'could not get a datahandler', REST_GET:'could not get a restahandler', REST_ERROR:'some problem with the parameters', NO_ACTION:'no action has been performed yet', PARAMS_MISSING:'some parameters are missing', PUBLISH:'nothing to publish', ACTION_FAILED:'action not known or invalid', NULL_DATA:'null data was ignored', UNDER_CONSTRUCTION:'method still under construction for this driver - check back later', OPERATION_UNSUPPORTED:'this driver does not support method', CLIENT_ERROR:'reserved for client error message', KEYS_AND_OBJECTS:'number of keys do not match number of objects', CHECKSUM:'data has changed since getting keys', TRANSACTION_ACTIVE:'transaction already active', ROLLBACK:'operation(s) rolled back', TRANSACTION_ROLLBACK:'transaction failed, but rolled back successfully', TRANSACTION_ROLLBACK_FAILED:'transaction failed, and roll back failed', TRANSACTION_FAILURE:'some transaction failure', TRANSACTION_INCAPABLE:'transactions not enabled for this driver', TRANSACTION_ID_MISMATCH:'trying to end the wrong transaction', DRIVER_ASSERTION:'driver assertion failure', KEY_ASSERTION:"trying to save an object which already has a key" }, CONSTRAINTS: { LT:'$lt', GTE:'$gte', GT:'$gt', NE:'$ne', IN:'$in', NIN:'$nin', EQ:'$eq', LTE:'$lte' }, FUSION_CONSTRAINTS: { LT:'<', GTE:'>=', GT:'>', NE:'NOT EQUAL TO', IN:'IN', EQ:'=', LTE:'<=' }, DATASTORE_CONSTRAINTS: { LT:'LESS_THAN', GTE:'GREATER_THAN_OR_EQUAL', GT:'GREATER_THAN', LTE:"LESS_THAN_OR_EQUAL", EQ:"EQUAL" } }; var ENUMS = dhConstants;
For help and more information join our forum, follow the blog or follow me on Twitter