In JavaScript my Worklight Client can pass arbitrary objects to an adapter:
var payload = { able: 3488, baker: "wibble"};
var invocationData = {
adapter : 'xxx',
procedure : 'xxx',
parameters : [payload],
compressResponse : false
};
var options = {
onSuccess: onCallSuccess,
onFailure: onCallFailure
};
WL.Client.invokeProcedure(invocationData, options);
and the adapter can access the object
function xxx(p1) {
return {'answer': p1.able};
}
In the native APIs it seems that we are limited to primitive types:
public void setParameters(java.lang.Object[] parameters)
This method
sets the request parameters. The order of the object in the array will
be the order sending them to the adapter
Parameters: Object -
parameters An array of objects of primitive types ( String, Integer,
Float, Boolean, Double). The order of the objects in the array is the
order in which they are sent to the adapter.
Hence if my adapters are to be used by both JavaScript and Native clients they will need to accept any complex objects as JSON Strings. Unless there is an alternative I'm missing?
I don't see a better alternative than simply stringifying the object as you have suggested. Are you using objects other than JSON in your native side? If so what is the structure of the object?
Related
I am having a tough time in implementing signed upload to Cloudinary using Kotlin. I have implemented my backend to provide me a signture and timestamp. This is what I have done to build the config:
var config = HashMap<String, Any> ()
config.put("cloud_name", "my_cloud_name");
//config.put("apiKey", my_api_key);
config.put("use_filename", true);
Now, I am unable to do the MediaManager.init using the signature. Can anyone please help? The Java code says to do the below, but I am unable to reproduce the same in Kotlin:
MediaManager.init(this, new SignatureProvider() {
#Override
public Signature provideSignature(Map options) {
// call server signature endpoint
}
}, null);
This is how you intialize MediaManager with a signature provider in Kotlin:
MediaManager.init(thiscontext!!, object: SignatureProvider {
override fun provideSignature(options: MutableMap<Any?, Any?>?): Signature {
return myBackendConnector.signRequest(options)
}
override fun getName(): String {
return "myCustomSignatureProvider"
}
}, config)
This will work assuming your backend already has the api key (it should), and that the return type from your connector is Signature. Otherwise you'll need to adapt your backend's result to Signature (populate the POJO with the result your server provided).
Get the timestamp and signature from your backend then add those as options.
val options = mapOf(
"timestamp" to // timestamp from backend,
"signature" to // signature from backend,
// other options from backend
)
MediaManager.get()
.upload(uri)
.options(options)
.callback(uploadCallback)
.dispatch()
So this means not creating a SignatureProvider in the MediaManager.init().
I came to using this because I could not get this to work with the SignatureProvider in the MediaManager.init().
I'm trying to update some Azure IoT Device Twin properties like this:
static async void MainAsync()
{
DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(connectionString);
TwinCollection reportedProperties = new TwinCollection();
dynamic heatingModes = new[]
{
new { Id="OUT2", Name="Comfort" },
new { Id="OUT", Name="Away" },
};
reportedProperties["heatingMode"] = "AWAY";
reportedProperties["supportedHeatingModes"] = heatingModes;
await deviceClient.UpdateReportedPropertiesAsync(reportedProperties);
}
The above code does not work and none of the Device Twin properties are not updated.
If I comment out this line everything works fine and the heatingMode property is updated as expected:
reportedProperties["supportedHeatingModes"] = heatingModes;
I have also tried to use a regular (not dynamic) type for heatingModes, but it does not work either.
I also tried to manually serialize the object to JSON:
reportedProperties["supportedHeatingModes"] = JsonConvert.SerializeObject(heatingModes);
But, the resulting JSON was kind of ugly with escaped quotes:
Why doesn't the updating the supportedHeatingModes property work for objects based on complex types?
Any other workarounds?
Have a look at the MSDN document Understand and use device twins in IOT Hub , where is described:
All values in JSON objects can be of the following JSON types: boolean, number, string, object. Arrays are not allowed.
I'm using mobilefirst platform v7, and I send post request using the WLResourceRequest/sendFormParameters api, however, I can't get the submitted parameters from js adapter side...
belows are sample code:
var resourceRequest = new WLResourceRequest("adapters/businessAdapter/flightsearch", WLResourceRequest.POST);
var params={
"flightNum":'mu8899',
"departCity":'SHA',
"destCity" :'PEK'
};
resourceRequest.sendFormParameters(params).then(
callSuccess,
callFailure
);
js adapter code:
function flightsearch(params) {
WL.Logger.info("get params "+params);
var input = {
method : 'post',
returnedContentType : 'json',
path : 'restapi/api/flightsearch',
body :{
contentType: 'application/json; charset=utf-8',
content:params
},
headers: {"Accept":"application\/json"}
};
return WL.Server.invokeHttp(input);
}
The syntax you used is fine for Java adapters.
However, in the case of JavaScript adapters, procedure parameters are handled differently.
First, your adapter procedure should define the parameters that it expects:
function flightsearch(flightNum, departCity, destCity) {
///
}
Secondly, this procedure will be triggered using an HTTP GET or POST with a single parameter called params which needs to contain an array, representing all the procedure parameters in the correct order:
params:["mu8899","SHA","PEK"]
Now using JavaScript, this would translate to:
var resourceRequest = new WLResourceRequest("adapters/businessAdapter/flightsearch", WLResourceRequest.POST);
var params=[
'mu8899',
'SHA',
'PEK'
];
var newParams = {'params' : JSON.stringify(params)};
resourceRequest.sendFormParameters(newParams).then(
callSuccess,
callFailure
);
As you can see, we first build the JSON array (note, array not object) in the correct order, then we convert it to String and send it to the adapter with the parameter name 'params'.
I would like to pass a complete JSON object to a java adapter in worklight. This adapter will call multiple other remote resources to fulfill the request. I would like to pass the json structure instead of listing out all of the parameters for a number of reasons. Invoking the worklight procedure works well. I pass the following as the parameter:
{ "parm1": 1, "parm2" : "hello" }
Which the tool is fine with. When it calls my java code, I see a object type of JSObjectConverter$1 being passed. In java debug, I can see the values in the object, but I do not see any documentation on how to do this. If memory serves me, the $1 says that it is an anonymous inner class that is being passed. Is there a better way to pass a json object/structure in adapters?
Lets assume you have this in adapter code
function test(){
var jsonObject = { "param1": 1, "param2" : "hello" };
var param2value = com.mycode.MyClass.parseJsonObject(jsonObject);
return {
result: param2value
};
}
Doesn't really matter where you're getting jsonObject from, it may come as a param from client. Worklight uses Rhino JS engine, therefore com.mycode.MyClass.parseJsonObject() function will get jsonObject as a org.mozilla.javascript.NativeObject. You can easily get obj properties like this
package com.mycode;
import org.mozilla.javascript.NativeObject;
public class MyClass {
public static String parseJsonObject(NativeObject obj){
String param2 = (String) NativeObject.getProperty(obj, "param2");
return param2;
}
}
To better explain what I'm doing here, I wanted to be able to pass a javascript object into an adapter and have it return an updated javascript object. Looks like there are two ways. The first it what I answered above a few days ago with serializing and unserializing the javascript object. The other is using the ScriptableObject class. What I wanted in the end was to use the adapter framework as described to pass in the javascript object. In doing so, this is what the Adapter JS-impl code looks like:
function add2(a) {
return {
result: com.ibm.us.roberso.Calculator.add2(a)
};
The javascript code in the client application calling the above adapter. Note that I have a function to test passing the javascript object as a parameter to the adapter framework. See the invocationData.parameters below:
function runAdapterCode2() {
// x+y=z
var jsonObject = { "x": 1, "y" : 2, "z" : "?" };
var invocationData = {
adapter : "CalculatorAdapter",
procedure : 'add2',
parameters : [jsonObject]
};
var options = {
onSuccess : success2,
onFailure : failure,
invocationContext : { 'action' : 'add2 test' }
};
WL.Client.invokeProcedure(invocationData, options);
}
In runAdapterCode2(), the javascript object is passed as you would pass any parameter into the adapter. When worklight tries to execute the java method it will look for a method signature of either taking an Object or ScriptableObject (not a NativeObject). I used the java reflection api to verify the class and hierarchy being passed in. Using the static methods on ScriptableObject you can query and modify the value in the object. At the end of the method, you can have it return a Scriptable object. Doing this will give you a javascript object back in the invocationResults.result field. Below is the java code supporting this. Please note that a good chunk of the code is there as part of the investigation on what object type is really being passed. At the bottom of the method are the few lines really needed to work with the javascript object.
#SuppressWarnings({ "unused", "rawtypes" })
public static ScriptableObject add2(ScriptableObject obj) {
// code to determine object class being passed in and its heirarchy
String result = "";
Class objClass = obj.getClass();
result = "objClass = " + objClass.getName() + "\r\n";
result += "implements=";
Class[] interfaces = objClass.getInterfaces();
for (Class classInterface : interfaces) {
result += " " + classInterface.getName() ;
}
result += "\r\nsuperclasses=";
Class superClass = objClass.getSuperclass();
while(superClass != null) {
result += " " + superClass.getName();
superClass = superClass.getSuperclass();
}
// actual code working with the javascript object
String a = (String) ScriptableObject.getProperty((ScriptableObject)obj, "z");
ScriptableObject.putProperty((ScriptableObject)obj, "z", new Long(3));
return obj;
}
Note that for javascript object, a numeric value is a Long and not int. Strings are still Strings.
Summary
There are two ways to pass in a javascript object that I've found so far.
Convert to a string in javascript, pass string to java, and have it reconstitute into a JSONObject.
Pass the javascript object and use the ScriptableObject classes to manipulate on the java side.
I have implemented HTTP adapter in IBM Worklight. I want to display the result returned from server. I want to display HTML file. My code is
function getFeeds() {
var input = {
method : 'get',
returnedContentType : 'text',
path : "marketing/partners.html"
};
WL.Logger.debug("sdfsds");
return WL.Server.invokeHttp(input);
}
I want to receive(display) WL.Server.invokeHttp(input). After receiving it I want to parse the data.
Take a look at the Server-side Development Getting Started Modules. Inside the HTTP adapter – Communicating with HTTP back-end systems Module on Slide 15 - 'XSL Transformation Filtering' will show you how to filter data you get back from the backend. Further parsing and showing data has to be done on the client using onSuccess callback for WL.Client.invokeProcedure. There's a module for that too.
Here's an example of getting data and showing to a user:
var invocationData = {
adapter : 'adapter-name',
procedure : 'procedure-name',
parameters : []
};
var options = {};
options.onSuccess = function (response) {
//response is a JavaScript object
$("#id").html(response.invocationResponse.text);
}
options.onFailure = function (response) {
alert('Failed!'); //You probably want something more meaningful here.
}
WL.Client invokeProcedure(invocationData, options);
There are JavaScript libraries you can add to make searching for values inside the JSON response easier, such as: jspath and jquery-jspath. There's also XPath if you're working with XML.
If you retrieve it as plain text, once you got it back to your application, do something like
$("#container-id").html(response.invocationResponse.text);
This will inject the HTML you've retrieved to an element with id container-id.