cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Did you know you can set a signature that will be added to all your posts? Set it here! X

Modify service code programmatically

ckulak
12-Amethyst

Modify service code programmatically

Hello,

Is there some simple solution for modifying the service implementation programmatically? I could create an empty service using AddServiceDefinition, but now I'm stuck with filling it.

I tried to do something like Export to XML > Modify XML > Import from XML, and the first two steps work fine, but for some reason I couldn't import the XML. Apparently the Importer servlet works differently from the regular TWX REST interfaces, and I can't figure out how to use it.

Is there any better way to do it, or maybe some example? Thanks!

Regards,

Constantine

ACCEPTED SOLUTION

Accepted Solutions
VladimirRosu
19-Tanzanite
(To:ckulak)

Hi Constantine,

Let me give an example of dynamic code execution that can be used in the platform.

For this to work you need to have a wrapper service, called let's say "Execute Service". You can run inside a single line, a javascript function called eval(string) which takes a string input parameter.

So you have 2 choices: you either store the command itself in a property so whenever you want to modify the implementation you set the property to a new value (can be an through an editor inside a mashup) or you can simply have the service accepting the command as a parameter.

Can this be applied in your use case?

View solution in original post

21 REPLIES 21
PaiChung
22-Sapphire I
(To:ckulak)

AddServiceDefinition is there to create a definition so that i can be bound as a Remote Service.

It isn't meant to allow for dynamic 'local' Service and Service Script creation.

ckulak
12-Amethyst
(To:PaiChung)

Thanks Pai. What would be the best way to create a local service with JS body?

PaiChung
22-Sapphire I
(To:ckulak)

Unfortunately I don't know of a way to do it permanently.

But you could store jscript in a datatable and execute that ... since this is pushing the boundaries of what is possible and what is recommended and what can become incredibly dangerous, email me and I can share an example.

jgorsline
12-Amethyst
(To:ckulak)

Constantine,

Any update on this? Was Pai Chung's post helpful? If so, could you click on the "correct answer" or "mark as helpful" button and let us know?

ckulak
12-Amethyst
(To:jgorsline)

Hello Jeremy, unfortunately there's still no good answer for this question.

ckulak
12-Amethyst
(To:ckulak)

So far, Costin's answer to this topic seems to be the closest to the solution: AddDynamicSubscription help

VladimirRosu
19-Tanzanite
(To:ckulak)

Hi Constantine,

Let me give an example of dynamic code execution that can be used in the platform.

For this to work you need to have a wrapper service, called let's say "Execute Service". You can run inside a single line, a javascript function called eval(string) which takes a string input parameter.

So you have 2 choices: you either store the command itself in a property so whenever you want to modify the implementation you set the property to a new value (can be an through an editor inside a mashup) or you can simply have the service accepting the command as a parameter.

Can this be applied in your use case?

Hello Vladimir,

If I remember correctly, eval() was not available in TWX Rhino.

/ Constantine

PaiChung
22-Sapphire I
(To:ckulak)

eval is available.

let me just stress again that this is very dangerous, because now you are going to allow non validated code to run.

ckulak
12-Amethyst
(To:PaiChung)

Hello Pai,

Indeed, I tested and it works! I believe it didn't work before, but that might have been my memory issue

Cool, that's all I need, thanks.

/ Constantine

VladimirRosu
19-Tanzanite
(To:ckulak)

I (obviously) agree with Pai. The context in which I used this was to build sequences of predefined services at runtime.

Having to write the service at runtime is definitely not  a suggested course of action, just technically possible if you implement it.

Vladimir, we'll use it in controlled and safe way, for sure. It's just easier than implementing some custom business rules engine. I marked your answer correct, thanks!

/ Constantine

eval it's evil but for a flexible solution it's the swiss tool, I can't live without it.

ckulak
12-Amethyst
(To:CarlesColl)

Currently my main concern about it is performance.

I'm using it on a lot of places, and it's not the bootle neck.

ptc-6292103
12-Amethyst
(To:ckulak)

//

// For this example, we'll have an Math service

// which takes two numbers, and an operation.

// The result will be that operation performed on the two inputs.

//

// We either need an Application Key,

// or user credentials to perform the reads and writes.

// App keys are a little safer.

// In this demo, we'll store it on the Entity as a Property.

var appKey = me.appKey;

//

// The service name needs to be unique and not already in use.

var serviceName = "MyMath";

//

// What are the inputs to the service?

// We'll define them nicely here, but manipulate this object later.

var parameters = {

    "op" : "STRING",

    "x" : "NUMBER",

    "y" : "NUMBER"

};

//

// What datatype does the service return?

// If it's an infotable,

// then you'll also have to specify the data shape

// as part of the resultType's aspect,

// but I won't demonstrate that here.

var output = "NUMBER";

//

// What is the actual service script?

// We'll define it here as an array of lines, and then join them together.

var serviceScript = [

    "var result = (function() {",

    "  switch(op) {",

    "    case \"add\": return x + y;",

    "    case \"sub\": return x - y;",

    "    case \"mult\": return x * y;",

    "    case \"div\": return x / y;",

    "    default: return op in Math ? Math[op](x, y) : 0;",

    "  };",

    "})();",

].join("\n");

////////

//

// Let's convert the friendly parameter definition

// into the structure that ThingWorx uses:

var parameterDefinitions = Object.keys(parameters).reduce(function(parameterDefinitions, parameterName, index) {

    var parameterType = parameters[parameterName];

    parameterDefinitions[parameterName] = {

        "name": parameterName,

        "aspects": {},

        "description": "",

        "baseType": parameterType,

        "ordinal": index

    };

    return parameterDefinitions;

}, {});

//

//  Now let's set up our service definition and implementation.

var definition = {

    "isAllowOverride": false,

    "isOpen": false,

    "sourceType": "Unknown",

    "parameterDefinitions": parameterDefinitions,

    "name": serviceName,

    "aspects": {

        "isAsync": false

    },

    "isLocalOnly": false,

    "description": "",

    "isPrivate": false,

    "sourceName": "",

    "category": "",

    "resultType": {

        "name": "result",

        "aspects": {},

        "description": "",

        "baseType": output,

        "ordinal": 0

    }

};

var implementation = {

    "name": serviceName,

    "description": "",

    "handlerName": "Script",

    "configurationTables": {

        "Script": {

            "isMultiRow": false,

            "name": "Script",

            "description": "Script",

            "rows": [{

                "code": serviceScript

            }],

            "ordinal": 0,

            "dataShape": {

                "fieldDefinitions": {

                    "code": {

                        "name": "code",

                        "aspects": {},

                        "description": "code",

                        "baseType": "STRING",

                        "ordinal": 0

                    }

                }

            }

        }

    }

};

////////

//

// Here are the URLs we'll need in order to make updates.

// You can change the thing name ('ServiceModifier' here)

// to something else.

// If you use credentials instead of an app key,

// then you can remove the appKey parameter here,

// but you'll have to add the username and password

// to the two ContentLoaderFunctions calls.

var url = {

    export : "http://127.0.0.1:8080/Thingworx/Things/ServiceModifier?Accept=application/json&appKey="+appKey,

    import : "http://127.0.0.1:8080/Thingworx/Things/ServiceModifier?appKey="+appKey

};

//

// We can download the entity to modify as a JSON object.

// Older versions of ThingWorx might not support this.

var config = Resources.ContentLoaderFunctions.GetJSON({

    url : url.export,

});

//

// We have to modify both the 'effectiveShape',

// as well as the 'thingShape'.

config.effectiveShape.serviceDefinitions[serviceName] = definition;

config.effectiveShape.serviceImplementations[serviceName] = implementation;

config.thingShape.serviceDefinitions[serviceName] = definition;

config.thingShape.serviceImplementations[serviceName] = implementation;

// Finally, we can push our updates back into ThingWorx.

Resources.ContentLoaderFunctions.PutText({

    url : url.export,

    content : JSON.stringify(config),

    contentType : "application/json",

});

// The end.

ckulak
12-Amethyst
(To:ptc-6292103)

Awesome, that's exactly what I was looking for! Thanks.

/ Constantine

PaiChung
22-Sapphire I
(To:ptc-6292103)

Can you actually do this though? I believe this needs an Administrator account to do the Import/Export?

ckulak
12-Amethyst
(To:PaiChung)

It would be nice to see how they do it in Utilities, but I'm too lazy to check it.

As far as I know, you need a user account who has permission to Read and Update the Entity.

ckulak
12-Amethyst
(To:ckulak)

There's a document with the "correct answer" now: https://community.thingworx.com/docs/DOC-3853

Announcements


Top Tags