Using Axeda Scripto

    Scripto provides a RESTful endpoint for Groovy Custom Objects on the Axeda Platform.  Custom Objects exposed via Scripto can be accessed via a GET or a POST, and the script will have access to request parameters or body contents. Any Custom Object of the "Action" type will automatically be exposed via Scripto. The URL for a Scripto service is currently defined by the name of the Custom Object:

    GET: http://{{YourHostName}}/services/v1/rest/Scripto/execute/<customObjectName>

     

    Scripto enables the creation of "Domain Specific Services". This allows implementers to take the Axeda Domain Objects (Assets, Models, DataItems, Alarms) and expose them via a service that models the real-world domain directly (trucks, ATMs, MRI Machines, sensor readings). This is especially useful when creating a domain-specific UI, or when integrating with another application that will push or pull data.

     

    Authentication

     

    There are several ways to test your Scripto scripts, as well as several different authentication methods. The following authentication methods can be used:

     

    Request Parameter credentials: ?username=<yourUserName>&password=<yourPassword>

    Request Parameter sessionId (retrieved from the Auth service): ?sessionid=<sessionId>

    Basic Authentication (challenge): From a browser or CURL, simply browse to the URL to receive an HTTP Basic challenge.

     

    Request Parameters

     

    You can access the parameters to the Groovy script via two Objects, Call and Request. Request is actually just a sub-class of Call, so the values will always be the same regardless of which Object you use.  Although parameters may be accessed off of either object, Call is preferable when Chaining Custom Objects (TODO LINK) together.  Call also includes a reference to the logger which can be used to log debug messages.

    GET:  http://{{YourHostName}}/services/v1/rest/Scripto/execute/<Your Script Name>?sessionid=<Session Id>&serial_number=mySerialNumber

     

    Accessing Parameters through the Request Object

    import com.axeda.drm.sdk.scripto.Request
    // Request.parameters is a map of strings
    def serial_number = Request.parameters.serial_number
    assert serial_number == "mySerialNumber"
          

     

    Accessing Parameters through the Call Object

    import com.axeda.drm.sdk.customobject.Call
    // Call.parameters is a map of strings
    def serial_number = Call.parameters.serial_number
    assert serial_number == "mySerialNumber"
          

     

    Accessing the POST Body through the Request Object


    The content from a POST request to Scripto is accessible as a string via the body field in the Request object.  Use Slurpers for XML or JSON to parse it into an object.

    POST:  http://{{YourHostName}}/services/v1/rest/Scripto/execute/<Your Script Name>?sessionid=<Session Id>

     

    Response: { "serial_number":"mySerialNumber"}

     

    import com.axeda.drm.sdk.scripto.Request
    
    def body = Request.body
    
    def slurper = new JsonSlurper()
    def result = slurper.parseText(body)
    assert result.serial_number == "mySerialNumber"
          

     

    Returning Plain Text

     

    Groovy custom objects must return some content.  The format of that content is flexible and can be returned as plain text, JSON, XML, or even binary files. The follow example simply returns plain text.

    GET:  http://{{YourHostName}}/services/v1/rest/Scripto/execute/<Your Script Name>

     

    // Outputs:  hello
    return ["Content-Type":"text/plain","Content":"hello"]
          

     

    Returning JSON

     

    We use the JSONObject Class to format our Map-based content into a JSON structure. The eliminates the need for any concern around formatting, you just build up Maps of Maps and it will be properly formatted by the fromObject() utility method.

    GET:  http://{{YourHostName}}/services/v1/rest/Scripto/execute/<Your Script Name>

     

    import net.sf.json.JSONObject
    root = [
      items:[    num_1: “one”,    num_2: “two”            ]
    ]
    /** Outputs
    {
      "items": {  "num_1": "one", "num_2": "two"  }
    }
    **/
    return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(root).toString(2)]
          

     

    Link to JSONObject documentation

     

    Returning XML

     

    To return XML, we use the MarkupBuilder to build the XML response. This allows us to create code that follows the format of the XML that is being generated.

    GET:  http://{{YourHostName}}/services/v1/rest/Scripto/execute/<Your Script Name>?sessionid=<Session Id>

     

    import groovy.xml.MarkupBuilder
    def writer = new StringWriter()
    def xml = new MarkupBuilder(writer)
    xml.root(){
        items(){
            num_1("one")
            num_2("two")
        }
    }
    
    /** Outputs
    <root>
      <items>
        <num_1>one</num_1>
        <num_2>two</num_2>
      </items>
    </root>
    **/
    return ['Content-Type': 'text/xml', 'Content': writer.toString()]
          

     

    Link to Groovy MarkupBuilder documentation

     

    Returning Binary Content

     

    To return binary content, you typically will use the fileStore API to upload a file that you can then download using Scripto.  See the fileInfo section to learn more. In this example we connect the InputStream which is associated with the getFileData() method directly to the output of the Scripto script. This will cause the bytes available in the stream to be directly forwarded to the client as the body of the response.

    GET:  http://{{Your Host Name}}/services/v1/rest/Scripto/execute/{{Your Script Name}}?sessionid={{Session Id}}&fileId=123

     

    import static com.axeda.sdk.v2.dsl.Bridges.*
    import com.axeda.services.v2.*
    import com.axeda.sdk.v2.exception.*
    
    def contentType = parameters.type ?: 'image/jpg'
    return ['Content':fileInfoBridge.getFileData(parameters.fileId), 'Content-Type':contentType]
      

     

    The Auth Service - Authentication via AJAX

     

    Groovy scripts are accessible to AJAX-powered HTML apps with Axeda instance credentials.  To obtain a session from an Axeda server, you should make a GET call to the Authentication service. The service is located at the following example URL:

    https://{{YourHostName}}/services/v1/rest/Auth/login

     

    This service accepts a valid username/password combination in the incoming Request parameters and returns a SessionID. The parameter names it expects to see are as follows:

     

    Property NameDescription
    principal.usernameThe username for the valid Axeda credential.
    password The password for the supplied credential.

     

    A sample request to the Auth Service:

    GET: https://{{YourHostName}}/services/v1/rest/Auth/login?principal.username=YOURUSER&password=YOURPASS

     

    Would yield this response (at this time the response is always in XML):

     

    <ns1:WSSessionInfo xsi:type="ns1:WSSessionInfo" xmlns:ns1="http://type.v1.webservices.sl.axeda.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ns1:created>2013-08-12T13:19:37 +0000</ns1:created>
      <ns1:expired>false</ns1:expired>
      <ns1:sessionId>19c33190-dded-4655-b2c0-921528f7b873</ns1:sessionId>
    <ns1:sessionTimeout>
    1800
    </ns1:sessionTimeout>
    </ns1:WSSessionInfo>
          

     

    The response fields are as follows:

    Field NameDescription
    created The timestamp for the date the session was created
    expired A boolean indicating whether or not this session is expired (should be false)
    sessionId The ID of the session which you will use in subsequent requests
    sessionTimeout The time (in seconds) that this session will remain active for

     

    The Auth Service is frequently invoked from JavaScript as part of Custom Applications. The following code demonstrates this style of invocation.

    function authenticate(host, username, password) {
                try {
                    netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
                } catch (e) {
                    // must be IE
                }
                var xmlHttpReq = false;
                var self = this;
                // Mozilla/Safari
                if (window.XMLHttpRequest) {
                    self.xmlHttpReq = new XMLHttpRequest();
                }
                // IE
                else if (window.ActiveXObject) {
                    self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
                }
                var SERVICES_PATH = "/services/v1/rest/"
                var url = host + SERVICES_PATH + "Auth/login?principal.username=" + username + "&password=" + password;
                self.xmlHttpReq.open('GET', url, true);
                self.xmlHttpReq.onreadystatechange = function() {
                    if (self.xmlHttpReq.readyState == 4) {
                        getSessionId(self.xmlHttpReq.responseXML);
                    }
                }
                self.xmlHttpReq.send()
    }
    function getSessionId(xml) {
                var value
                if (window.ActiveXObject) {
                    // xml traversing with IE
                    var objXML = new ActiveXObject("MSXML2.DOMDocument.6.0");
                    objXML.async = false;
                    var xmldoc = objXML.loadXML(xml);
                    objXML.setProperty("SelectionNamespaces", "xmlns:ns1='http://type.v1.webservices.sl.axeda.com'");
                    objXML.setProperty("SelectionLanguage","XPath");
                    value =  objXML.selectSingleNode("//ns1:sessionId").childNodes[0].nodeValue;
                } else {
                    // xml traversing in non-IE browsers
                    var node = xml.getElementsByTagNameNS("*", "sessionId")
                    value = node[0].textContent
                }
                return value
    }
    authenticate ("http://mydomain.axeda.com", "myUsername", "myPassword")
          

     

     

    Calling Scripto via AJAX

     

    Once you have obtained a session id through authentication via AJAX, you can use that session id in Scripto calls. The following is a utility function which is frequently used to wrap Scripto invocations from a UI.

    function callScripto(host, scriptName, sessionId, parameter) {
                try {
                    netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
                } catch (e) {
                    // must be IE
                }
                var xmlHttpReq = false;
                var self = this;
                // Mozilla/Safari
                if (window.XMLHttpRequest) {
                    self.xmlHttpReq = new XMLHttpRequest();
                }
                // IE
                else if (window.ActiveXObject) {
                    self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
                }
                var url = host + SERVICES_PATH + "Scripto/execute/" + scriptName + "?sessionid=" + sessionId;
                self.xmlHttpReq.open('GET', url, true);
                self.xmlHttpReq.onreadystatechange = function() {
                    if (self.xmlHttpReq.readyState == 4) {
                        updatepage(div, self.xmlHttpReq.responseText);
                    }
                }
                self.xmlHttpReq.send(parameter);
    }
    function updatepage(div, str) {
                document.getElementById(div).innerHTML = str;
    }
    callScripto("http://mydomain.axeda.com", "myGroovyScriptName", "mySessionId", "myparameter=foo")
          

     

    A more modern jQuery-based example might look like the following:

     

    function callScripto(host, scriptName, sessionId, parameter) {
        var url = host + '/services/v1/rest/Scripto/execute/' + scriptName + '?sessionid=' + sessionId
        if ( parameter != null ) url += '&' + parameter
        $.ajax({url: url,
                  success:  function(response) {  updatepage(div, response); }
              });
    }
    function updatepage(div, str) {
        $("#" + div).innerHTML = str
    }
    callScripto("http://mydomain.axeda.com", "myGroovyScriptName", "mySessionId", "myparameter=foo")
     

     

    In Conclusion

     

    As shown above, Scripto offers a number of ways to interact with the platform.  On each version of the Axeda Platform, all supported v1 and v2 APIs are available for Scripto to interact with the Axeda domain objects and implement business logic to solve real-world customer problems.

     

    Bibliography (PTC.net account required)

     

        Axeda v2 API/Services Developer's Reference Version 6.8.3 August 2015

        Axeda® v1 API Developer’s Reference Guide Version 6.8 August 2014

        Documentation Map for Axeda® 6.8.2 January 2015