How To Retrieve Data From Rest API In Appcelerator - api

I have the misfortune of further developing an existing mobile application in Appcelerator. The app uses a Rest API on a remote server to read and write data. The API works well in test environments and in production. I need to post data to the API and read the output. Here is an example of what the output of the API looks like after a POST command:
{
"equipment":
{
"result": "create",
"id": 419213
},
"_meta":
{
"offset": 0,
"limit": -1,
"total_results": 1,
"url": "http://localhost:8080/api/v1/equipment",
"utc_start_time": 1459449461115,
"nano_total_time": 74771
}
}
I am able to successfully post the data in Appcelerator. I have verified this in the database that the CRUD operation is acting on. However, I am unable to get the aforementioned data from the httpClient object that makes the call, despite following the directions in the outdated Titanium documentation.
Here is my Appcelerator code:
var payload = "name=atad&asset_number=adtasd&department_id=185080&property_id=10086&designator_id=379828&is_leased=N&is_assignable_asset=N&status=A";
var url = "http://localhost:8080/api/v1/equipment";
var client = Ti.Network.createHTTPClient({
onload : function(e) {
Ti.API.info(e); // {}
Ti.API.info(e.source); // []
Ti.API.info(JSON.stringify(e.source)); // {}
Ti.API.info(JSON.stringify(e.source.reponseText)); // null
Ti.API.info(JSON.stringify(e.source.reponseData)); // null
Ti.API.info(this); // []
console.log(JSON.stringify(this)); // {}
Ti.API.info(JSON.stringify(this.reponseText)); // null
Ti.API.info(this.reponseData); // null
}
,onerror : function(e){
Ti.API.info(e);
alert("error");
}
});
var auth = 'Basic ' + Ti.App.Properties.getString('auth');
client.open("POST", apiUrl);
client.setRequestHeader('Authorization', auth);
client.setRequestHeader('Content-Type', 'text/plain');
client.send(payload);
Here is the console output:
[INFO] : {
[INFO] : code = 0;
[INFO] : source = "[object TiNetworkHTTPClient]";
[INFO] : success = 1;
[INFO] : type = load;
[INFO] : }
[INFO] : [object TiNetworkHTTPClient]
[INFO] : {"method":"POST","url":"http://localhost:8080/api/v1/equipment"}
[INFO] : <null>
[INFO] : <null>
[INFO] : [object TiNetworkHTTPClient]
[INFO] : {"method":"POST","url":"http://localhost:8080/api/v1/equipment"}
[INFO] : <null>
[INFO] : <null>
The documentation here http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Network.HTTPClient explicitly says to use this.responseText, but that is clearly not giving me the results I need. I need that "id" that is being returned from the server.
How do I read the data that is returned from the server after a post API call?

There is just a typo in this.responseText

Have a look at this module: https://github.com/jasonkneen/RESTe
It makes your life easier and gives you a great way to the same syntax for future projects!
If you want to keep your syntax (which is fine too):
have a look at this example: https://github.com/m1ga/titanium-libraries/blob/master/api.js#L49
and the following lines. It shows you how to read the JSON. Basically you have to wait for if (this.readyState === 4) {} inside the onload and then read the this.responseText or JSON.parse() it.

It looks like the apiUrl variable is not defined?
Which could explain a lot or your current frustration.

Related

Karate - Polyglot Exception using js callSingle()

In the code presented below, I am trying to interact with a database through the use of a js function within the karate framework. The query used to get some data works as expected, likewise auxiliary information is accessible. The same function within the js file can be ran through groovy just fine but seems to explode when ran within karate.
JS Function to be ran in karate through a callSingle():
function() { let dbUser = Java.type('dev.dao.dbUser') return dbUser.getId(karate.properties['username'])
dbUser:
class dbUser {
static Integer getId(String email) {
db.sql.firstRow("select user_id from table.db_user where email = ?", [email]).userId
}
}
Karate-config.js
function fn() {
let config = {
project: karate.properties['vs.project'],
environment: karate.properties['vs.environment'],
baseUrl: karate.properties['vs.baseUrl'],
timeZoneId: karate.properties['vs.timeZoneId'],
env: karate.properties['vs.env'],
proxy: {
...
]
},
demoUserId: 1,
userId: karate.callSingle('classpath:proj/dev/js/dao/getDbUserId.js'),
createDBuid: karate.read('classpath:proj/dev/js/createDBuid.js'),
DBuid: karate.callonce('classpath:proj/dev/js/createDBuid.js')
}
}
Error returned calling the getDbUserId.js:
PolyglotError
<<<<
org.graalvm.polyglot.PolyglotException
com.intuit.karate.core.ScenarioBridge.callSingle(ScenarioBridge.java:243)
com.intuit.karate.core.ScenarioBridge.callSingle(ScenarioBridge.java:187)
.fn(Unnamed:18)

JSON Store is not creating in Android for latest version fix pack 7.1.0.00.20160919-1656

Below sample code is not working in latest fix on Android but if we remove the password field from options then it's working fine. We are getting below error on Android but it's working fine on IOS
{"src":"initCollection","err":-3,"msg":"INVALID_KEY_ON_PROVISION","col":"people","usr":"test","doc":{},"res":{}}
function wlCommonInit(){
/*
* Use of WL.Client.connect() API before any connectivity to a MobileFirst Server is required.
* This API should be called only once, before any other WL.Client methods that communicate with the MobileFirst Server.
* Don't forget to specify and implement onSuccess and onFailure callback functions for WL.Client.connect(), e.g:
*
* WL.Client.connect({
* onSuccess: onConnectSuccess,
* onFailure: onConnectFailure
* });
*
*/
// Common initialization code goes here
}
function onClick(){
alert("Click");
var collectionName = 'people';
// Object that defines all the collections.
var collections = {
// Object that defines the 'people' collection.
people : {
// Object that defines the Search Fields for the 'people' collection.
searchFields : {name: 'string', age: 'integer'}
}
};
// Optional options object.
var options = {
username:"test",
// Optional password, default no passw`enter code here`ord.
password : '123',
};
WL.JSONStore.init(collections, options)
.then(function () {
alert("Success in jstore");
})
.fail(function (errorObject) {
// Handle failure for any of the previous JSONStore operations (init, add).
alert("Failure in jstore : "+ JSON.stringify(errorObject));
});
};
Update: The iFix is now released. Build number is 7.1.0.0-IF201610060540 .
This is a known issue with the latest available iFix. It has been recently fixed and should be available soon.
Keep an eye out for a newer iFix release in the IBM Fix Central website for a fix for this issue.

Take screen shot , upload and tweet on titanium

I am making application on iOS and Android on Titanium.
What I want to do is
1) take screen shot
2) upload pics
3) post tweet including pics link
What I have done so far is.
1)I can take screenshot like this and save the photo in camera folder
Ti.Media.takeScreenshot(function(e){
Ti.Media.saveToPhotoGallery(e.media);
});
2)I have no idea about that
3)I can tweet with social.js
var social = require('social');
var twitter = social.create({
site : 'Twitter',
consumerKey : '*',
consumerSecret : '*'
});
twitter.share({
message : data,
success : function() {
Ti.API.info('Tweeted!');
},
error : function(error) {
Ti.API.info('You have already shared this app on Twitter.');
}
});
Currently I am trying to do on twitter however I would like to make for android version.
Thank you for the comment of #turtle.
I found I need to use Social_plus.js.
I have changed the code like this below
var social = require('/myfunc/social_plus');
var data = new Object();
Ti.Media.takeScreenshot(function(e){
Ti.Media.saveToPhotoGallery(e.media);
data['image'] = e.media;
});
data['text'] = 'lets tweet!!';
var twitter = social.create({
site : 'Twitter',
consumerKey : '*',
consumerSecret : '*'
});
twitter.shareImage({
message : data['text'],
image : data['image'],
success : function() {
Ti.API.info('Tweeted!');
},
error : function(error) {
Ti.API.info('Somehow error!.');
}
});
However, I got the error like this .
[ERROR] : Social.js: FAILED to send a request!
[INFO] : You have already shared this app on Twitter.
[ERROR] : {"errors":[{"code":195,"message":"Missing or invalid url parameter."}]}
c
I guess there is something wrong with the sentene
data['image'] = e.media;
But I can't find good information around here.

WL.Logger.Send() its Not callback the WLClientLogReceiver adapter

I have enabled the nativeOptions: {capture: true} in initOptions.js
logger : {enabled: true, level: 'debug', stringify: true, pretty: false,
tag: {level: false, pkg: true}, whitelist: [], blacklist: [], nativeOptions: {capture: true}}
In my main js file i have the following code.
function wlCommonInit(){
// Common initialization code goes here
WL.Logger.setNativeOptions({'capture': true});
var logger = WL.Logger.create({pkg: 'mypackage'});
logger.debug('Hello world - debug');
//[mypackage] Hello world
logger.log('Hello world - log');
//[mypackage] Hello world
logger.info('Hello world - info');
//[mypackage] Hello world
logger.warn('Hello world - warn');
//[mypackage] Hello world
logger.error('Hello world - error');
//[mypackage] Hello world
WL.Logger.send(); }
WL.Logger.send() suppose to call my adapter "WLClientLogReceiver". But i am not getting any call for this adapter.
Please let me know, i need to enable any other settings to upload my client side captured log to server.
function log(deviceInfo, logMessages) {
return true;}
<procedure name="log" securityTest="wl_unprotected" audit="true" />
logger : {enabled: true, level: 'debug', stringify: true, pretty: false, tag: {level: false, pkg: true}, whitelist: [], blacklist: [], nativeOptions: {capture: true}}
You have enabled the native capture as true in initOptions.js so no need to set it again.
You can log using your package that will help you in filtering the messages based on the package in your WLClientLogReceiver adapter.
var myloggerObject = WL.Logger.create({pkg: 'mypackage'});
myloggerObject.debug("Hello world");
you can specify your level in your js file to be logged in client device.
In the adapter you will get the log messages as an json array.
function log(deviceInfo, logMessages) {
/* The adapter can choose to process the parameters,
for example to forward them to a backend server for
safekeeping and further analysis.
The deviceInfo object may look like this:
{
"appName": "wlapp",
"appVersion": "1.0",
"deviceId": "66eed0c9-ecf7-355f-914a-3cedac70ebcc",
"model": "Galaxy Nexus - 4.2.2 - API 17 - 720x1280",
"systemName": "Android",
"systemVersion": "4.2.2",
"os.arch": "i686", // Android only
"os.version": "3.4.0-qemu+" // Android only
}
The logMessages parameter is a JSON array
that contains JSON object elements, and might look like this:
[{
"timestamp" : "17-02-2013 13:54:23:745", // "dd-MM-yyyy hh:mm:ss:S"
"level" : "ERROR", // ERROR||WARN||INFO||LOG|| DEBUG
"package" : "your_tag", // typically a class name
"msg" : "the message", // a helpful log message
"threadid" : 42, // (Android only)the current thread
"metadata" : { "$src" : "js" } // metadata placed on the log call
}]
*/
//sample log and filtering method
var logs= [{
"timestamp" : "17-02-2013 13:54:23:745", // "dd-MM-yyyy hh:mm:ss:S"
"level" : "ERROR", // ERROR||WARN||INFO||LOG|| DEBUG
"package" : "your_tag", // typically a class name
"msg" : "the message", // a helpful log message
"threadid" : 42, // (Android only)the current thread
"metadata" : { "$src" : "js" } // metadata placed on the log call
},
{
"timestamp" : "17-02-2013 13:54:23:745", // "dd-MM-yyyy hh:mm:ss:S"
"level" : "ERROR", // ERROR||WARN||INFO||LOG|| DEBUG
"package" : "mypackage", // typically a class name
"msg" : "my package message", // a helpful log message
"threadid" : 42, // (Android only)the current thread
"metadata" : { "$src" : "js" } // metadata placed on the log call
}
];
var filteredLogs = logs.filter(function(log){
if(log.package == mypackage) //comparing the package and returns the object
{ return log; }
});
WL.Logger.error(filteredLogs);// This is send only the filtered array to your server
}
If you log using metadata such as filename along with the debug message you will get those in the array in metadata attribute.
It is suggested to stringify and parse the object to avoid errors before parsing the device logs in the adapter.
var logs = JSON.stringify(JSON.parse(logs));
var filteredLogs = logs.filter ...
Hope this will work for you.
Make sure you test it using the device.
The send function is not attached to the LogInstance prototype, which is what you're using when you use a logger instance created with WL.Logger.create(). Please call
WL.Logger.send();
instead.
(Above was posted prior to OP's edit.)
Since setNativeOptions is an asynchronous call (it calls down through a Cordova plugin), it is possible it has not successfully turned capture on prior to completion of the subsequent logger calls. So at the time of the call to WL.Logger.send(); nothing has been collected yet.
Do this:
function wlCommonInit() {
// Common initialization code goes here
WL.Logger.setNativeOptions({'capture': true})
.then(function() {
var logger = WL.Logger.create({pkg: 'mypackage'});
logger.debug('Hello world - debug');
//[mypackage] Hello world
logger.log('Hello world - log');
//[mypackage] Hello world
logger.info('Hello world - info');
//[mypackage] Hello world
logger.warn('Hello world - warn');
//[mypackage] Hello world
logger.error('Hello world - error');
//[mypackage] Hello world
WL.Logger.send();
});
}
Be sure to check the server-side logs. The audit="true" in the adapter's descriptor file will print the parameters passed to the adapter inline in the server logs (messages.log on WebSphere Liberty).

HTTP Adapter Error "Runtime: Failed to parse JSON string"

I am using IBM Worklight Studio, and trying to create HTTP Adapter which retrieve JSON object from external http server.
When I just access target http server with HTTP Get access(with browser, for example), I know their response is like following array style JSON format:
[
{ "xxx":"aaa", "yyy":"bbb", ... },
{ "xxx":"ccc", "yyy":"ddd", ... },
:
{ "xxx":"eee", "yyy":"fff", ... }
]
And I had created HTTP Adapter which would retrieve above information
var input = {
method : 'get',
returnedContentType : 'json',
path : path
};
return WL.Server.invokeHttp(input);
Now I tried to invoke this adapter with "Run As -> Invoke Worklight Procedure", then I got this error message:
{
"errors": [
"Runtime: Failed to parse JSON string\n\n[\n {\n
(raw JSON data) } ],
"info": [],
"isSuccessful": false,
"warnings": []
}
And in my log console, worklight says following error messages:
FWLSE0101E: Caused by: java.io.IOException: Expecting '{' on line 2, column 2 instead, obtained token: 'Token: ['
From above information, it seems that worklight would expect that returned JSON object need to start with "{", not "[".
Is this my guess right? Are there any workaround for this?
Thanks for advance.
Worklight knows how to handle JSON objects that start with [ (JSON arrays). In such case Worklight will return the response as:
{ "array" : [*the json array*]}
Looking at the code of the HTTP Adapter, I see that there is a bug with parsing JSON arrays that do not start with [.
I do not see a workaround for this problem, except changing the response returned from the http server.
I opened an internal bug about this, thank you for helping us find this bug.
You can change returnedContentType to "plain", this will make WL server return content as a big string and not attempt to parse it.
Then in your adapter you can use var obj = JSON.parse(response.text)