Community Tip - Learn all about the Community Ranking System, a fun gamification element of the PTC Community. X
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 Name | Description |
---|---|
principal.username | The 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 Name | Description |
---|---|
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