Ajax call on $.ajax().complete - javascript

I have a problem with jQuery ajax function. I working with API that provides users and RBAC managment. By design this is separated functions, so when i create a user and assign a role for it i should call two requests - first i send 'create user' and it's return a {"success":"true", "id":"[id nuber]"} then i send 'assign role' with params like "{"item":"RoleName", "user_id":"[id from previous request]"}".
There is object "api" which have some methods for work with API. It is a simple wrapper which knocking on www.myurl.api/ and returns json. Because of it may take a long time api object methods takes a handlers that will be run on success and fail. If api now running a request then api.ready == false, otherwise api.aready == true. Result of last request stored in api.data as object.
Problem is that result not saved in api.data in case when two API request cascaded, like:
api.send(params, //params is json for user creation
function(){ //handler on this request result
... //creating another parms for assignment from api.data
api.send(params2, function(){//handler that works if api coorectly creates a new user
... //here i try send a request with params and it fails
})
}
);
code of api.send method:
send: function (entity, request, params, method, handler){
if (!method)
method='POST';
if (request.toLowerCase()=='get')
request = '';
if (request)
request += '-';
api.data = null;
params.apiKey = api.key;
api.ready = false;
api.handler = handler;
$.ajax({
url: this.url+request+ entity,
method: 'GET',
data: params
}).complete(function(msg) {
api.data = JSON.parse(msg.responseText);
if (api.data[0] && api.data[0].meta)
api.data.forEach(function (element, index, array){
element.meta = JSON.parse(element.meta)
});
api.ready = true;
api.handler.call();
});
}
and this is function that calls to create new user
function createUser(){
validateCreateForm();
if (!createValidated )
return;
var values = {
"username": $('#inputUsername').val(),
"password": $('#inputPassword').val(),
"comment": "Added by "+adderUsername
};
api.send('users','add', values, 'POST', function () {
if (api.data.success="true"){
//===========all in this if works ONLY if api works succesfully
//===========and api.data.id is exist and correct
message("success", "Was created username " + values.username);
$('#inputUsername').val('');
$('#inputPassword').val('');
//==========Problem is here
id = api.data.id; //in this var stores id
console.log('api.data.id is ' + id);//undefined, should be some int.
//if write something like id=42 rights will be correcttly assigned for user with id 42
//================================================================
if (!$('#inputRole').val())
return;
api.send('assignments',
'add',
{
"user_id": id,
"item_name": $('#inputRole').val()
},
'POST',
function () {
if (api.data.success="true"){
message("success", "Account was created and permissions granted");
}
else {
message("success", "Inner error. Please, try again later.");
}
}
);
}
else {
message("danger", "Inner error. Please, try again later.");
}
);
}

Related

Get intents, entities, contexts and all data

In the case, the actually conversation-simple have one function with all the values, but the function update every time if flows conversation.
I want create one function or other form to be able to capture all that data that is currently on the data.
In the case have Intents, context, entities, etc.
conversation.message(payload, function(err, data) {
if (err) {
return res.status(err.code || 500).json(err);
}
return res.json(updateMessage(payload, data));
});
});
The data inside updateMessage parameter have all I need, but if I create other function and try get this values, does not work.
In the case I use the values and get with app.js for open some REST webservice.
I try it:
function login (req, res) {
numberOrigin = null;
sessionid = null;
var dataLogin = {
data: { "userName":"xxxxx","password":"xxxxx","platform":"MyPlatform" },
headers: { "Content-Type": "application/json" }
};
client.registerMethod("postMethod", "xxxxxxxxxxxxxxx/services/login", "POST");
client.methods.postMethod(dataLogin, function (data, response) {
if(Buffer.isBuffer(data)){
data = data.toString('utf8');
console.log(data);
var re = /(sessionID: )([^,}]*)/g;
var match = re.exec(data);
var sessionid = match[2]
console.log(sessionid);
}
});
}
function openRequest(data, sessionid, numberOrigin ){
//console.log(data); dont show the values.. show the data response of login
var dataRequest = {
data: {"sessionID": sessionid,
"synchronize":false,
"sourceRequest":{
"numberOrigin":numberOrigin,
"description": JSON.stringify(data.context.email) } },
headers: { "Content-Type": "application/json" }
};
numberOrigin +=1;
client.post("xxxxxxxxxxxxxxxxxx/services/request/create", dataRequest, function (data, response) {
if(Buffer.isBuffer(data)){
data = data.toString('utf8');
console.log(data);
}
});
}
function updateMessage(res, input, data, numberOrigin) {
var email = data.context.email; // this recognize but this function is responsible for other thing
if (email === 'xxxxxxxxxxxx#test.com') {
console.log(data);
login(data);
openRequest(data, sessionid, numberOrigin)
}
}
In case, I just want get the values with my app.js for use inside REST. I got it with ajax but everything on the client side (index.html), and that made me show my credentials, so I decided to do it in REST for security my code..
If have some form to solved this, please let me know.
If have other form to do it, I'll be happy to know.
Thanks advance.
The issue is likely that you need to write to the response object res.. In the updateMessage function the response is passed in. In order for data to be sent back to the browser you need to write to the response. I have a demo app which calls the weather channel to get the weather based on an intent, similar to what you are trying to do with your login function. Please take a look at this code
https://github.com/doconnor78/conversation-simple-weather/blob/master/app.js#L130
You will need to pass the original res (response) object into the appropriate function and then write data to the response (res) once you get it from the third party service.

Passing data from service to controller

I have created a service with the purpose of accessing an API. I need to return data to my controller but am unsure how to do this as I completely new to sails.
My Service:
// API call to get the ID of contact within Get Response with email address
getContact: function(options) {
// establish connection to API
var api = new getResponse(apiKey, apiUrl);
var contact = api.getContactsByEmail(options.email, null, null, function (response) {
JSON.stringify(response);
console.log(JSON.stringify(response));
});
return contact;
},
I know the API call is working as when I log the response I get the correct response:
{"success":true,"data":{"error":null,"id":1,"result":{"sds":{"ip":null,"name":"Full Name","origin":"api","cycle_day":0,"email":"email#email.com","campaign":"id","created_on":"date","changed_on":null}}}}
My Controller:
index: function(req, res) {
var contact = GetresponseService.getContact({email: 'email#email.com'});
console.log(contact);
return res.send(contact);
}
I want to retrieve the ID value but when I log the value of contact I get undefined. I think my problem is related to the scope but not sure.
Can anyone point me in the right direction?
Because you are directly assigning a value from api.getContactsByEmail() which does not return a value.
By the nature of node.js, the function api.getContactsByEmail() gives you callback with the response. You have to get the value from within the anonymous callback function like this:
// API call to get the ID of contact within Get Response with email address
getContact: function(options) {
// establish connection to API
var api = new getResponse(apiKey, apiUrl);
var contact = "";
api.getContactsByEmail(options.email, null, null, function (response) {
contact = response;
JSON.stringify(response);
console.log(JSON.stringify(response));
return contact;
});
}
more ...
In my opinion, its better to return a callback instead of direct return of value.
// API call to get the ID of contact within Get Response with email address
getContact: function(options, callback) {
// establish connection to API
var api = new getResponse(apiKey, apiUrl);
var contact = "";
api.getContactsByEmail(options.email, null, null, function (response) {
contact = response;
JSON.stringify(response);
console.log(JSON.stringify(response));
if(typeof(callback) == "function")
callback(contact);
else
return contact; // return contact if there is callback func.
});
}
You can use it like:
index: function(req, res) {
var contact;
GetresponseService.getContact({email: 'email#email.com'}, function(contactResult) {
contact = contactResult;
console.log(contact);
return res.send(contact);
});
}
Everything looks like it should work, however I think you're running into an issue with this piece
var contact = api.getContactsByEmail(options.email, null, null, function (response) {
JSON.stringify(response);
console.log(JSON.stringify(response));
});
api.getContactsByEmail is asynchronous I assume, so this declarative statement won't work.
Instead declare contact and return it inside the callback, something like:
api.getContactsByEmail(options.email, null, null, function (response) {
JSON.stringify(response);
console.log(JSON.stringify(response));
var contact = response.contacts; //or however you access the contact(s) from the response variable
//You should also be watching for errors in your callbacks, but that's a different topic
return contact;
});
Read up on asynchronous calls in javascript and make sure you have a solid grasp on when data is accessible when using them.

AJAX calls are not executed on server side in same order as called on client side

I have a ASP.NET MVC application with following javascript function in a view:
function OnAmountChanged(s, fieldName, keyValue, url) {
console.log("Get Number: " + s.GetNumber());
console.log(" ");
$.ajax({
type: "POST",
url: url,
data: { key: keyValue, field: fieldName, value: s.GetNumber() },
success: function () {
reloadShoppingCartSummary();
}
});
}
With a up / down button I can increase / decrease the number of my field which calls this function for every click.
With the console.log I can see that the JavaScript function is called in the correct order (the order I changed the number value).
But by debugging on server side I noticed that the order it executes the calls is different.
What can cause this problem?
And how can I solve or workaround this problem?
This is not surprising at all and not something you can expect. If you want requests to be processed in a specific order, then you have to send one request, wait for it's response, then send the next. You can't just send all of them and expect them to be processed in a specific order.
In your specific case, if a request is "in-flight", you would have to either disable the button or you would queue up the next request and play it back when the previous request finishes.
There are also various strategies for how you send the data that can prevent, adapt to or detect out of sequence issues, but which to use and how to do it depends upon the specific data you're sending and how it works on both the client and server side of things.
There are many possible coding strategies for dealing with this issue. Here's one method that queues any requests that come in while another request is in process, forcing them to be processed in order by the server:
var changeQueue = [];
changeQueue.inProcess = false;
function OnAmountChanged(s, fieldName, keyValue, url) {
// if already processing a request, then queue this one until the current request is done
if (changeQueue.inProcess) {
changeQueue.push({s:s, fieldName: fieldName, keyValue: keyValue, url: url});
} else {
changeQueue.inProcess = true;
console.log("Get Number: " + s.GetNumber());
console.log(" ");
$.ajax({
type: "POST",
url: url,
data: { key: keyValue, field: fieldName, value: s.GetNumber() },
success: function () {
reloadShoppingCartSummary();
}, complete: function() {
changeQueue.inProcess = false;
if (changeQueue.length) {
var next = changeQueue.shift();
OnAmountChanged(next.s, next.fieldName, next.keyValue, next.url);
}
}
});
}
}
FYI, I think we can probably get promises to do the queuing work for us, but I'm working out some details for doing that. Here's one idea that's in process: http://jsfiddle.net/jfriend00/4hfyahs3/. If you press the button multiple times rapidly, you can see that it queues the presses and processes them in order.
Here's a specific idea for a generic promise serializer:
// utility function that works kind of like `.bind()`
// except that it works only on functions that return a promise
// and it forces serialization whenever the returned function is called
// no matter how many times it is called in a row
function bindSingle(fn) {
var p = Promise.resolve();
return function(/* args */) {
var args = Array.prototype.slice.call(arguments, 0);
function next() {
return fn.apply(null, args);
}
p = p.then(next, next);
return p;
}
}
function OnAmountChanged(s, fieldName, keyValue, url) {
console.log("Get Number: " + s.GetNumber());
console.log(" ");
return $.ajax({
type: "POST",
url: url,
data: { key: keyValue, field: fieldName, value: s.GetNumber() }
}).then(reloadShoppingCartSummary);
}
var OnAmountChangedSingle = bindSingle(OnAmountChanged);
So, to use this code, you would then pass OnAmountChangedSingle to your event handler instead of OnAmountChanged and this will force serialization.

Fetch data on different server with backbone.js

I can't see what the problem with this is.
I'm trying to fetch data on a different server, the url within the collection is correct but returns a 404 error. When trying to fetch the data the error function is triggered and no data is returned. The php script that returns the data works and gives me the output as expected. Can anyone see what's wrong with my code?
Thanks in advance :)
// function within view to fetch data
fetchData: function()
{
console.log('fetchData')
// Assign scope.
var $this = this;
// Set the colletion.
this.collection = new BookmarkCollection();
console.log(this.collection)
// Call server to get data.
this.collection.fetch(
{
cache: false,
success: function(collection, response)
{
console.log(collection)
// If there are no errors.
if (!collection.errors)
{
// Set JSON of collection to global variable.
app.userBookmarks = collection.toJSON();
// $this.loaded=true;
// Call function to render view.
$this.render();
}
// END if.
},
error: function(collection, response)
{
console.log('fetchData error')
console.log(collection)
console.log(response)
}
});
},
// end of function
Model and collection:
BookmarkModel = Backbone.Model.extend(
{
idAttribute: 'lineNavRef'
});
BookmarkCollection = Backbone.Collection.extend(
{
model: BookmarkModel,
//urlRoot: 'data/getBookmarks.php',
urlRoot: 'http://' + app.Domain + ':' + app.serverPort + '/data/getBookmarks.php?fromCrm=true',
url: function()
{
console.log(this.urlRoot)
return this.urlRoot;
},
parse: function (data, xhr)
{
console.log(data)
// Default error status.
this.errors = false;
if (data.responseCode < 1 || data.errorCode < 1)
{
this.errors = true;
}
return data;
}
});
You can make the requests using JSONP (read about here: http://en.wikipedia.org/wiki/JSONP).
To achive it using Backbone, simply do this:
var collection = new MyCollection();
collection.fetch({ dataType: 'jsonp' });
You backend must ready to do this. The server will receive a callback name generated by jQuery, passed on the query string. So the server must respond:
name_of_callback_fuction_generated({ YOUR DATA HERE });
Hope I've helped.
This is a cross domain request - no can do. Will need to use a local script and use curl to access the one on the other domain.

How can should I pass two functions as parameters, both can be optional, in javascript

I want to build up an flexible small API to backend for the client. To provide some convenience in using default success-/error- handlers or writing own ones, I planned to support the user with the option to pass both handlers one or none. I tried as follows.
Snippet of file "API" to backend:
function someRPCcall(method, url, data, successHandler, failedHandler) {
// checking if successHandler and failedHandler are defined and passed functions
if (paramType1 === 'undefined' || paramType1 === 'null') {
successHandlerHelper = defaultSuccessRESTHandler;
} else if (paramType1 === 'function') {
successHandlerHelper = successHandler;
}
if (paramType2 === 'undefined' || paramType2 === 'null') {
failedHandlerHelper = defaultFailedRESTHandler;
} else if (paramType2 === 'function') {
failedHandlerHelper = failedHandler;
}
ajaxCall(method, url, data, successHandlerHelper, failedHandlerHelper);
}
function ajaxCall(method, url, data, success, failed) {
console.log("in ajaxCcall");
$.ajax({
type: method,
contentType: "application/json",
data: data,
url: url,
success: success,
error: failed
dataType: "json"
});
console.log("ajaxCall - call done");
}
Snippet of file of client code
someRPCcall will be indirectly called by functions in another file:
someFunctionRPCcall("bla", null, errorHandler);
whereas searchWordOccurrenceRPCcall is calling someRPCcall
Above the failureHandler would be a selfdefined Handler, but following calls also should be possible:
someFunctionRPCcall("bla", null, successHandler, errorHandler);
someFunctionRPCcall("bla", null, successHandler);
someFunctionRPCcall("bla", null);
I heard about taking an object, in which the functions would be defined....
This code does not call the self defined handlers, but I guess this is another problem (using apply is missing or something like that)
The question I wanted to put here is:
Is there a way to pass both functions as optional parameters? How about that proposal useing objects?
Thanks
As you mentioned, you can get your function to take an object, eg:
function someRPCcall(args) {
var method = args.method || "POST";
var url = args.url || "default";
var data = args.data || {};
var successHandler = args.success || defaultSuccessRESTHandler;
var failedHandler = args.failed || defaultFailedRESTHandler;
ajaxCall(method, url, data, successHandler, failedHandler);
}
You will notice above that if a property is not present on the args object then it is given a default.
Then call it as follows:
someRPCcall({
url : "the url",
failed: function() { ... }
});
Of course, you could throw an exception if some values are not set, eg url:
function someRPCcall(args) {
if(!args.url) {
throw "url must be set";
}
}

Categories