Different Titanium HTTPClient behavior from sdk 1.7.5 to 1.8.0.1 - sdk

I found two different behaviors for Titanium HTTPClient in sdk 1.7.5 and 1.8.0.1
Here's my complete code:
function Synchronizer () {
//server calls
var callStack = [{
url: 'http://url1',
name: 'foo1'
},
{
url: 'http://url2',
name: 'foo2'
},
{
url: 'http://url3',
name: 'foo3'
},
{
url: 'http://url4',
name: 'foo4'
}
];
//processing data
var dataStack = new Array();
//call stack pointer
var stackPointer = 0;
//HTTPClient
var httpClient = Titanium.Network.createHTTPClient({
onerror:function(e){
UIManager.SynchronizeUI.onError();
},
ondatastream:function(e) {
UIManager.SynchronizeUI.setProgressBarValue(stackPointer);
}
});
//....
//DataConsumer
var DataConsumer = new DataConsumer();
//Method for synchronization
this.synchronizeAll = function (usr, pwd) {
if(!Ti.Network.online){
Ti.API.info('Not connected to the internet');
return;
}
Ti.API.info('Avvia syncro');
stackPointer = 0;
//Autentication call
httpClient.open('POST', 'http://url/login');
httpClient.onload = function() {
// If authenticated
if(this.responseText.trim() == ('OK,' + usr)) {
Ti.API.info('Login successful');
consumeCallStack();
}
//else
else {
//...
}
}
httpClient.send({username: usr, password: pwd, ajaxlogin: 'true'});
Ti.API.info('Calling login..');
}
/*
* stack consumer
*/
function consumeCallStack() {
Ti.API.info('Execute call ' + stackPointer);
httpClient.open('GET', callStack[stackPointer].url);
httpClient.onload = function(){
alert(httpClient.responseText);
//data
var data = JSON.parse(this.responseText);
Ti.API.info('Retrieved data for ' + callStack[stackPointer].name);
//..
}
httpClient.send();
}
HTTPClient retrieves the data and the on error function isn't involved.
The problem is that after being authenticated the HTTPClient second call gets the JSON that I want if running with 1.7.5 while gets the same successful login page of the first call with 1.8.0.1. May be a cookie not enabled problem? Thank you very much!

Use the release notes to see which bug fixes relate to your issue:
Titanium 1.8.0.1 Release Notes
httpclient sync mode
iOS: Http request sent twice on ios 5

Related

SAPUI5 oData.V2 How to invoke a function after everything in a batch request is done?

I have an issue while making an SAPUI5 odata V2 batch request :
var that = this;
var oServiceModel = that.getModel("oServiceModel");
odataMod = this.getModel("Service");
odataMod.setUseBatch(true);
var aData = oServiceModel.getData();
var stupidService = _.filter(aData, function (ae) {
return ae.Info === "-N/A";
});
var i = 0 ;
_.forEach(stupidService, function (sap) {
oGlobalBusyDialog.setText("Deleting service :" + sap.ObjectID);
oGlobalBusyDialog.setTitle("Deleting Service");
oGlobalBusyDialog.open();
that.removeService(sap).then(function () {
if (i === 615) {
oGlobalBusyDialog.close();
}
}).catch(function () {});
});
my Delete function is like this:
removeService: function (service) {
var that = this;
return new Promise(
function (resolve, reject) {
odataMod.remove('/ProjectTaskServiceCollection(\'' + service.ObjectID + '\')/', {
success: function (oData) {
resolve(oData);
},
error: function (oResult) {
that.handleError(oResult);
oGlobalBusyDialog.close();
reject(oResult);
}
});
});
What's happening ,is that if I'm trying to delete 500 entry, and if 200 entry cannot be deleted, the error message gets displayed 200 times
How to make it in a way to only display the error message once ?
Also, I want to turn off the batch request once everything is done odataMod.setUseBatch(false); how to do it ?
*EDIT: *
I've manage to do :
var aDeffGroup = odataMod.getDeferredGroups();
//add your deffered group
aDeffGroup.push("deletionGroup");
for (var s = 0; s < 5; s++) {
odataMod.remove('/ProjectTaskServiceCollection(\'' + stupidService[s].ObjectID + '\')/', {
//pass groupid to remove method.
groupId: "deletionGroup"
});
}
odataMod.submitChanges({
// your deffered group id
groupId: "deletionGroup",
success: function() {
//Get message model data from Core and it contains all errors
// Use this data to show in dialog or in a popover or set this to your local model see below code
var aErrorData = sap.ui.getCore().getMessageManager().getMessageModel();
console.log(aErrorData);
}
});
yet stills my console.log(aErrorData); still prints multiple error message
Instead of doing individual deletion odata calls. Add these all remove methods in a single group, then call odatamod.submitChanges() method.
Example:
//get all deffered groups
var aDeffGroup = odataMod.getDeferredGroups();
//add your deffered group
aDeffGroup.push("deletionGroup");
//set it back again to odatamodel
odataMod.setDeferredGroups(aDeffGroup);
odataMod.remove('/ProjectTaskServiceCollection(\'' + service.ObjectID + '\')/', {
//pass groupid to remove method.
groupId: "deletionGroup"});
odataMod.submitChanges({
// your deffered group id
groupId:"deletionGroup",
success: function() {
//Get message model data from Core and it contains all errors
// Use this data to show in dialog or in a popover or set this to your local model see below code
var aErrorData = sap.ui.getCore().getMessageManager().getMessageModel();
});

Ajax calls working in Android but not iOS

I'm developing an app in Nativescript for the first time and running into an issue where AJAX calls work on Android but not iOS. I have a login.js file which requires a user-view-model (user-view-model.js), and when I test the code on Android it takes me to the "home" page but it hits the catch function on iOS.
login.js:
var dialogsModule = require("ui/dialogs");
var UserViewModel = require("../../shared/view-models/user-view-model");
var applicationSettings = require("application-settings");
var user = new UserViewModel({
email: "aaa#aaa.com",
password: "aaa"
});
var frameModule = require("ui/frame");
var page;
exports.loaded = function(args) {
page = args.object;
page.bindingContext = user;
};
exports.login = function () {
user.login().catch(function(error) {
dialogsModule.alert({
message: "Unfortunately we could not find your account.",
okButtonText: "OK"
});
return Promise.reject();
}).then(function(response) {
console.dir(response)
console.log("past response")
applicationSettings.setString("user_id", response.user_id);
applicationSettings.setString("first_name", response.first_name);
applicationSettings.setString("last_name", response.last_name);
applicationSettings.setString("user_type", response.user_type);
var topmost = frameModule.topmost();
topmost.navigate("views/home/home");
});
};
user-view-model.js:
var config = require("../../shared/config");
var fetchModule = require("fetch");
var observableModule = require("data/observable");
var http = require("http");
function User(info) {
info = info || {};
var viewModel = new observableModule.fromObject({
email: info.email || "",
password: info.password || ""
});
viewModel.login = function() {
let loginEmail = JSON.stringify(this.get("email")).replace(/['"]+/g, '');
let loginPassword = JSON.stringify(this.get("password")).replace(/['"]+/g, '');
console.log(loginEmail, loginPassword);
let loginUrl = config.serverPHPServiceUrl + "Login.php?user_id=" + loginEmail + "&password=" + loginPassword;
console.log(loginUrl);
// I tried this way first and wasn't able to login on iOS, which made me try the second method below.
// return fetchModule.fetch(loginUrl, {
// method: "POST",
// headers: {
// "Content-Type": "application/json"
// }
// }).then(handleErrors).then(function(response) {
// return response.json();
// }).then(function(data) {
// console.dir(data);
// console.log(data["results"][0]["user_id"])
// return data["results"][0];
// });
// This method works on Android but not iOS.
return http.getJSON(loginUrl).then(function(response) {
console.dir(response);
return response.results[0];
})
};
return viewModel;
};
function handleErrors(response) {
console.log("in errors")
if (!response.ok) {
console.log(JSON.stringify(response));
throw Error(response.statusText);
}
return response;
}
module.exports = User;
Is there anything fundamentally wrong with my code, or do asynchronous calls work differently on iOS vs Android in Nativescript? I did the Grocery tutorial and didn't run into this issue, so I didn't think this was the case. Does it matter that the backend is using PHP?
I fixed my issue: I started a new project with Angular 2 and ran into the same error, but then it gave me the error message "Error: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." I solved it by adding "https" to my url call, but this post has another solution.

Worklight JSON store encryption of collection not working in ios

I am using JSONStore in my application to store some sensitive data. To encrypt collection,we are passing options with username and password as mentioned below. In android so far its working fine but in ios devices we are getting blank page while retrieving data from collection (working fine in simulators). I'm not getting any errors also.Without passing options in ios, its working fine. Has anybody faced similar issue?
factory('todoJsonStorage',['$q', function ($q) {
'use strict';
var COLLECTION_NAME = 'Users';
var collections = {
Users: {
searchFields: {UserId: 'string', password: 'string'}
},
};
var options = {};
//Optional username
options.username = 'testuser';
//Optional password
options.password = 'test123';
//Optional local key generation flag
options.localKeyGen = true;
var inited = false;
//checks if inited and if not inits
function initJSONStore(){
var initDeferred = $q.defer();
if (inited){
initDeferred.resolve();
} else {
//Initialize the collection
WL.JSONStore.init(collections,options).then(function () {
console.log("-> JSONStore init successful");
initDeferred.resolve();
}).fail(function (errorObject) {
console.log("-> JSONStore error: " + errorObject.msg);
});
return initDeferred.promise;
};
}
return {
get: function () {
var deferred = $q.defer();
initJSONStore().then(function(){
WL.JSONStore.get(COLLECTION_NAME).findAll().then(function (res) {
if (res.length > 0){
deferred.resolve(JSON.parse(res[0].json.data || '[]'));
} else {
deferred.resolve(res);
}
}).fail(function (errorObject) {
console.log("JSONStore findbyid error: " + errorObject.msg);
});
});
return deferred.promise;
},
put: function (todos) {
WL.JSONStore.get(COLLECTION_NAME).clear();
WL.JSONStore.get(COLLECTION_NAME).add({data:JSON.stringify(todos)});
}
};
}])
If you are using iOS 10, you must enable the Keychain Sharing Capability, otherwise this should work out-of-the-box.

Notification does not pop up when blocking an HTTP request

Introduction
I'm trying to intercept certain HTTP requests and then notify the user and cancel these requests:
UPDATE: reproducible code
const {Cc, Ci, Cr} = require("chrome");
const self = require("sdk/self");
const data = self.data;
const notif = require("notifications");
const REGEX = {
'lookup.bluecava.com': "1",
'ds.bluecava.com/v50/AC/BCAC': "2"
};
// ------------------------------------------------------------------
// Initialize observer service
var observerService = Cc["#mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
function TracingListener() {
this.originalListener = null;
}
// Request observer
var httpRequestObserver = {
observe: function (aSubject, aTopic, aData) {
var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
if ("http-on-modify-request" == aTopic) {
var url = httpChannel.originalURI.spec;
for (var str_re in REGEX) {
var re = new RegExp(str_re, "g");
if (url.match(re)) {
console.log("Requested URL: " + url.match(re));
notif.notify({
title: "foo",
text: "foo"
});
aSubject.cancel(Cr.NS_BINDING_ABORTED); // Cancel request TODO: fix if uncommented notification won't pop up!
}
}
}
},
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIObserver) || aIID.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_NOINTERFACE;
}
};
// Register service
observerService.addObserver(httpRequestObserver,
"http-on-modify-request", false);
exports.main = function () {
console.log("Addon is running...");
};
// Unload addon event
exports.onUnload = function (reason) {
// Unregister service
observerService.removeObserver(httpRequestObserver,
"http-on-modify-request");
};
Problem
The problem is that, no matter the order of the call to the cancel method and the notify method, the notification won't pop up.
Possible cause
I guess that it has something to do with the asynchronous nature of javascript and that the method cancel blocks somehow the notification call. However, I don't find any callbacks neither in the notification nor in the cancel method that I can use to chain the calls. Any idea?

Login via Titanium sync adapter for Rails

I want to be able to log in my Titanium app with credentials from my Rails app database.
In Titanium, I created the following model:
exports.definition = {
config: {
'adapter': {
'type': 'myAdapter',
'base_url': 'http://server:3000/api/users/'
}
},
extendModel: function(Model) {
_.extend(Model.prototype, {
// custom functions
login: function() {
this.sync("login", this);
}
});
return Model;
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {});
return Collection;
}
}
The sync adapter I created, from the Twitter example given in the official Appcelerator doc:
// Global URL variable
var BASE_URL = 'http://server:3000/api/';
// Override the Backbone.sync method with the following
module.exports.sync = function(method, model, options) {
var payload = model.toJSON();
var error;
switch(method) {
// custom cases
case 'login':
http_request('POST', BASE_URL + 'login', payload, callback);
break;
// This case is called by the Model.fetch and Collection.fetch methods to retrieve data.
case 'read':
// Use the idAttribute property in case the model ID is set to something else besides 'id'
if (payload[model.idAttribute]) {
// If we have an ID, fetch only one tweet
http_request('GET', BASE_URL + '', {
id : payload[model.idAttribute]
}, callback);
} else {
// if not, fetch as many as twitter will allow us
http_request('GET', BASE_URL + '', null, callback);
}
break;
// This case is called by the Model.save and Collection.create methods
// to a initialize model if the IDs are not set.
// For example, Model.save({text: 'Hola, Mundo'})
// or Collection.create({text: 'Hola, Mundo'}) executes this code.
case 'create':
if (payload.text) {
http_request('POST', BASE_URL + 'update.json', {
status : payload.text
}, callback);
} else {
error = 'ERROR: Cannot create tweet without a status!';
}
break;
// This case is called by the Model.destroy method to delete the model from storage.
case 'delete':
if (payload[model.idAttribute]) {
// Twitter uses a POST method to remove a tweet rather than the DELETE method.
http_request('POST', BASE_URL + 'destroy/' + payload[model.idAttribute] + '.json', null, callback);
} else {
error = 'ERROR: Model does not have an ID!';
}
break;
// This case is called by the Model.save and Collection.create methods
// to update a model if they have IDs set.
case 'update':
// Twitter does not have a call to change a tweet.
error = 'ERROR: Update method is not implemented!';
break;
default :
error = 'ERROR: Sync method not recognized!';
};
if (error) {
options.error(model, error, options);
Ti.API.error(error);
model.trigger('error');
}
// Simple default callback function for HTTP request operations.
function callback(success, response, error) {
res = JSON.parse(response);
console.log("response |" + response);
console.log("res |" + res);
console.log("res str |" + JSON.stringify(res))
console.log("options |" + options);
if (success) {
// Calls the default Backbone success callback
// and invokes a custom callback if options.success was defined.
options.success(res, JSON.stringify(res), options);
}
else {
// res.errors is an object returned by the Twitter server
var err = res.errors[0].message || error;
Ti.API.error('ERROR: ' + err);
// Calls the default Backbone error callback
// and invokes a custom callback if options.error was defined.
options.error(model, err, options);
model.trigger('error');
}
};
};
// Helper function for creating an HTTP request
function http_request(method, url, payload, callback) {
// Generates the OAuth header - code not included
var header;
//= generate_header(method, url, payload);
var client = Ti.Network.createHTTPClient({
onload : function(e) {
if (callback)
callback(true, this.responseText, null);
},
onerror : function(e) {
if (callback)
callback(false, this.responseText, e.error);
},
timeout : 5000
});
// Payload data needs to be included for the OAuth header generation,
// but for GET methods, the payload data is sent as a query string
// and needs to be appended to the base URL
if (method == 'GET' && payload) {
var values = [];
for (var key in payload) {
values.push(key + '=' + payload[key]);
}
url = url + '?' + values.join('&');
payload = null;
}
client.open(method, url);
//client.setRequestHeader('Authorization', header);
client.send(payload);
};
// Perform some actions before creating the Model class
module.exports.beforeModelCreate = function(config, name) {
config = config || {};
// If there is a base_url defined in the model file, use it
if (config.adapter.base_url) {
BASE_URL = config.adapter.base_url;
}
return config;
};
// Perform some actions after creating the Model class
module.exports.afterModelCreate = function(Model, name) {
// Nothing to do
};
The rails app responds with JSON and it works but the problem is in the callback function:
[INFO] : response |{"email":"huhu#gmail.com","password":null}
[ERROR] : Script Error {
[INFO] : res |[object Object]
[INFO] : res str |{"email":"huhu#gmail.com","password":null}
[INFO] : options |[object Object]
[ERROR] : backtrace = "#0 () at file://localhost/.../xxx.app/alloy/sync/myAdapter.js:4";
[ERROR] : line = 30;
[ERROR] : message = "'undefined' is not a function (evaluating 'options.success(res, JSON.stringify(res), options)')";
[ERROR] : name = TypeError;
[ERROR] : sourceId = 216868480;
[ERROR] : sourceURL = "file://localhost/.../xxx.app/alloy/sync/myAdapter.js";
[ERROR] : }
So, does somebody have an idea why options is undefined...? Note that if I call the fetch method to get users using the read method, it works. By the way, is there a good way in authenticating somebody?
you are not passing in an options parameter in your model try making a change like this below
this.sync("login", this, {
success: function() {},
error: function(){}
});

Resources