Apstrata’s Server-Side Scripts

Server-side scripting is another cool feature of Apstrata. It allows any developer to add back-end logic to his/her applications while leveraging the power of Apstrata’s API. In addition to this, Apstrata’s server-side scripting capability provides the developers with other advantages:

  • No deployment and configuration efforts: the scripts are written in JavaScript and all that is needed is to upload them to your Apstrata account,
  • Multi tenancy: since you can deploy multiple scripts for different client applications,
  • Built-in security: as we will see it shortly, you can leverage Apstrata’s authentication and authorization (ACLs) in your scripts, which gives you control on who can read/edit or run your scripts,
  • Background jobs: you can ask for asynchronous execution of your scripts, scheduling them to run at specific times or intervals,
  • Orchestration: you can factor out the logic to coordinate (orchestrate) the execution of multiple services (Apstrata APIs or any other web service and or server-side scripts) and build your own APIs through Apstrata server-side scripts.

How to create server-side scripts?

While you could write a script using any IDE and just upload it to your Apstrata account by calling the SaveScript API, the easiest way to create a script is by using the Apstrata workbench. Log in to the workbench using your Apstrata authentication key and secret, then click on “Manage Account” > “Scripts” > “My Scripts”. In the panel that opens, either click on “New” to start writing a new script, or select an existing script, which in both cases results in opening the script editor. Although the capabilities of the editor are limited, they cover most of what you need to get started, including the highlighting of the JavaScript keywords and the analysis of the JavaScript syntax.

Before you start coding, pay attention that you need to wrap your JavaScript code into a specific XML structure, composed of an ACL section and a code section, as shown in the below snippet. We will examine the ACL part in more details later on in this tutorial. For now, just remember to insert your JavaScript code into the “<code><![CDATA[" section. If you forget to do this, the workbench will display an alert informing you that your document is not well formed.

<script> <scriptACL> <execute>anonymous</execute> <read>group:developers</read> <write>group:developers</write> </scriptACL> <code><![CDATA[ // Your code goes here ]]> </code> </script>

Once you are done coding, enter a name for the script in the “Name” input field then click on “Save”.

Your first server-side script

We will start by implementing a simple script that creates a new document in the default store of your account (or any other store). The document will be schema-less and will contain two fields: “amount” (of type “numeric”) and “date”, that will contain the current date. The identifier of the document (the “documentKey”), as well as the amount will be retrieved from the request. (The complete script is available here)

Retrieving a parameter from the request: the “request” object

From within server-side scripts, you have access to first class Astrata objects such as the“request” object. This object wraps the HTTP request that triggered the execution of your script and is used to retrieve the parameters sent along the request:

var documentId = request.parameters["documentId"]; // we assume that the document id parameter is called “documentId” var amount = request.parameters["amount"];

Preparing the parameters to pass to the API

Since we need to create a document, we have to invoke the “SaveDocument” API, and pass it the required parameters: the document identifier, the amount field and the date field. Since we do not want to store these fields as strings, we also need to pass the corresponding expected respective types along the parameters of the request sent to “SaveDocument” :

// we need this field to be stored as a numeric, so we parse it to an int amount = parseInt(amount); // we create an object that contains the various parameters var saveDocumentParams = {   “apsdb.documentKey”: documentId, //apsdb.documentKey is an apstrata key word that refers to the document identifier   “apsdb.store”: “DefaultStore”, //apsdb.store is an apstrata key word. You actually do not need to specify the store when you are using the default store   “amount”: amount,   “date”: new Date(),   “amount.apsdb.fieldType”: “numeric”, // notice how we specify the type of a given field -> [fieldName].apsdb.fieldType   “date.apsdb.fieldType”: date  }

Invoking the targeted API: the “apsdb” object

In order to invoke any Apstrata API that is available on the current Apstrata cluster, you can leverage the first class “apsdb” object, and more specifically, the “callApi()” function that it provides. This function expects the name of the API to invoke, a JavaScript object containing the parameters to pass and optionally, the files that you need to upload to the document. So let us invoke this function in our script, feeding it with the “saveDocumentParams” object, and storing the result in a variable (“response”):

var response = apsdb.callApi(“SaveDocument”, saveDocumentParams, null); // notice that we passed ‘null’ as a last parameter as we have no files to upload. return response; // our script will return the response received from the execution of apsdb.callApi()

Processing the response of the invoked API

The invocation of “apsdb.callApi()” always returns a response containing the outcome of the call. This outcome is divided in two parts: “metadata” and “result”. The “metadata” part contains the status of the invocation and the “result” part contains the outcome of the API execution, if any. For example, if you remove the “parseInt()” function in our example and run the script (therefore sending a string instead of a numeric), you will receive the following (in the response variable created above):

{ “metadata”: { “status”: “failure”, “errorCode”: “INVALID_FIELD_VALUE”, “errorDetail”: “Field [amount] cannot contain values that are not numeric” } }

As you can observe, the metadata section informs you that an error occurred during the execution of the SaveDocument API and gives you some details on that error. Hence, in order to determine the status of the “apsdb.callApi()” execution, you just need to check the value of “response.metadata.status” (“failure” or “success”).

Now if you restore the “parseInt()” function and run the script again, you should normally receive a response resembling the following:

{ “metadata”: { “status”: “success” }, “result”: { “document”: { “key”: “[your_document_key]” } } }

As you can see, the response now contains a result section filled with the outcome of the SaveDocument API.

In order to know what to expect in this section further to an API call, please consult the corresponding documentation in the Apstrata wiki.

Returning a response from your script

When your script has finished executing, Apstrata will automatically send a response adopting the same structure as the response returned by “apsdb.callApi()” but the “result” section will be null.

The “metadata” section will notify you of any error that resulted from the execution of the script (could be due to a technical concern or to an error in the script logic).

As for the “result” section, since “null” is not very informative to the caller on the client side, it is obviously more interesting to return a more specific result. Depending on the case, you can simply return the response you received further to an API call in your script (such as we did it in the “Invoke targeted API” paragraph), or you can send your own result object. Note that in the former case, the “result” section of the response you obtain from the execution of your script will itself be subdivided into a “metadata” and a “result” section (since all what we did is to returnL the response of the API call). If you decide to build your own response, it is recommended to divide it into a “metadata” and “result” sections as well. Also note that your “metadata” section should preferably at least contain the “status”, “errorCode” and errorDetail attributes in order to standardize the way responses are processed on the client side.

How to execute a script?

You can call the scripts you create on your Apstrata account as any Apstrata API. Simply build the URL of your request such as the following: [http://varick.apstrata.com/rest/apsdb/[your_auth_key]/RunScript?apsdb.scriptName=[your_script_name]&apsws.time=[timestamdp]&script_nameapsws.authSig=[your_signature]&apsws.responseType=jsoncdp&apsws.authMode=simple&[your_parameters]

This latter option is however not really handy for testing in development mode, therefore, you should rather resort to the workbench to run your scripts. From the script editor, save your work by clicking on “Save” then click on “Run”. This opens a panel where you can enter the parameters of your request (name and value). You can even impersonate a user (run the script as if you were this particular user) by selecting a specific in the “RunAs” selection list (we will examine this option in more details in another part of this tutorial). Click on “Go” once you are done.

As you can observe, the workbench will open a new panel further to the call, where the URL of your request to the script and the response returned from your script are displayed.

Tips

Logs

Since it is hard to plug a debugger onto a server-side script, you need an alternative to be able to debug your code. This is provided by the first class “log” object (a property of the “apsdb” object), which will become one of your best friends ;)

In order to start logging events, you first need to set the log level of your script using “apsdb.log.setLogLevel([the_level])”. 5 levels are available, ranging from 0 (no logging) to 4 (all logging calls will be taken into account). To log an event, call the “apsdb.log.debug([message], ?logObject)” function inside your code. All you logs will be sent in the “metadata.scriptLog” attribute of the response (it is an array that contains one object per log call)

A good practice is to make logging configurable in your scripts. For example, you could send the log level as a parameter of your request (which is something you would only do when testing), and set the log level accordingly, as demonstrated in the below example (based on the code we have written so far):

// Note: insert the below code after the retrieval of ‘amount’ and ‘documentId’ in your script // if we sent a logLevel parameter with the request, activate logs and set log level var logLevel = request.parameters["logLevel"]; if (logLevel) { apsdb.log.setLogLevel(logLevel); }else { apsdb.log.setLogLevel(0); } // log the value of ‘amount’ apsdb.log.debug(“Amount received”, {amountReceived: amount});

If you run the script, you should receive a response similar to what follows. Notice the “message” and “handback” attributes, which contain respectively the message and the log object you specified when calling “apsdb.log.debug([your_message], [log_object])”;

“metadata”: { “requestId”: “ec97d7c9-970d-4a6b-a6f4-bc517de4ba48″, “status”: “success”, “scriptLog”: [ { "timestamp": "2012-09-13 10:57:04.267", "level": "debug", "component": "tutorial.part1.firstScript", "message": "Amount received", "handback": { "amountReceived": "5" } }, { "timestamp": "2012-09-13 10:57:04.268", "level": "debug", "component": "tutorial.part1.firstScript", "message": "Entering API call: SaveDocument" }, { "timestamp": "2012-09-13 10:57:04.321", "level": "debug", "component": "tutorial.part1.firstScript", "message": "Exiting API call" } ], “statusCode”: “200″ }, “result”: { “result”: { “document”: { “key”: “x” } }, “metadata”: { “status”: “success” } }

Handling exceptions

Another concern that all developers encounter is the occurrence of exceptions during the execution of their server-side scripts. This can be very frustrating as all you get is a generic “script error” message in your “metadata.errorCode” section of the response, with no further details. A good practice is hence to surround your code with a try/catch block, which enables you to capture any exception and process it before the script returns. The catch block would usually wrap the exception into a metadata section that you will send as a response, as shown in the below snippet:

<script> <scriptACL>      <execute>anonymous</execute>      <read>group:developers</read>      <write>group:developers</write> </scriptACL> <code><![CDATA[ try { // your code goes here }catch(exception) { return { "status": "failure", "error": "EXCEPTION_OCCURRED", "errorDetails": exception } } ]]> </code> </script>

What’s next?

This first part of the tutorial covered the basics of server-side scripting with Apstrata. In the next parts, we will handle more advanced concepts, such as importing scripts, permissions, transactions, remote calls, redirection, etc.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>