angularjs getting song with promise - javascript

I'm sending data to a server with and JSON object using $http.post().success().error();
The response from the server is returning an id which is a number (example : 51)
After I get the id I'm making another request to the server. This time the data is the id that I got from the previous http call.
second http call looks something like this: $http.get(data= Song_ID ).success().error();
The response from the server has a res.data.url string,
but what the server does is that when the first http call fires, the server is creating a short music sample. This operation of course takes some time, so in the meantime the server sends back the id of the song that is being created
with that id. I want to ask the server if it's finished creating the song already.
If the res.data.url contains a string, it means the song is ready, if not then the operation is still working.
how do I know when the operation has completed?
For now I'm doing an interval that every second sends a http call with the song id and I'm doing an if(res.data.url) { console.log('i have a url for you') }
But sending lots of ajax calls with interval seems wrong to me.
is there a better way to do it?
Should I use angular promise and how?
Here's how it looks like:
function getId() {
// first post to server
var postToServer = function () {
var song_id; // song id
// response first time
var onComplete = function (res) {
song_id = res.data.id;
getMySong(song_id); old stuff
// $location.path("/converting");
return res.data.id;
};
// error first time
var onError = function (res) {
console.log(res);
};
var song_params = songdata.getData()["answers"].toString();
console.log(songdata.getData()["params"]);
var req = {
method: 'POST',
url: 'myserver',
headers: {
'Content-Type': 'application/json'
},
data: angular.toJson({params: "1,1,1,1,1,1"})
};
(function () {
$http(req).then(onComplete, onError);
})();
};
return postToServer();
}
function getMySong(id) {
var interval; // interval
//here is that interval that checks for url
(function () {
interval = $interval(function () {
getUrlSong(id);
}, 1000);
})();
// song url response
var onResSong = function (res) {
console.log(res);
if (res.data.url) {
$interval.cancel(interval);
//$window.location.href = res.data.url;
window.songURL = res.data.url;
songUrl(res.data.url);
}
};
// song url response error
var onErrorSong = function (res) {
console.log(res);
};
// fetch song with the id
var getUrlSong = function (id) {
$http.get('myserver' + id).then(onResSong, onErrorSong)
};
}

Related

Node JS Express how can I send a 404 error if a bad request is made to third party API?

In my Node JS server I have this route handler that sends a request to a third party API to get a username:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function (res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(JSON.stringify(data));
response.send(objectParsed);
})
if(!player) {
res.status(404).send("Not found.");
}
})
apiRequest.end();
})
This works fine to get a user that exists. However, if I put in a fake username to my /players page, that page still loads with a 200 status instead of getting a 404 response. The page loads and looks broken because it's not actually getting any data from the API.
I feel like this is a dumb question .. In my research I have found how to handle errors if it's just the route, and not if it's the route dependent on the path parameter as in /players/:player
I found a question that was similar to mine (How to throw a 404 error in express.js?) and I tried using an If statement: if (!player){res.status(404).send("Not found."); } but no dice. Am I using this if statement in the wrong place?
How can I get my Node JS server to respond with a 404 if the user from the database doesn't exist?
You have to check the result of the API call and see if you got valid data back and send the 404 there. I also added a check to make sure something was passed for the player name and send back a 400 (bad request) if there's no player specified at all:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
if (!player) {
res.status(400).send("No player specified.");
return;
}
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function(res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(data);
// test objectParsed here
if (!some condition in objectParsed) {
res.status(404).send("No data for that player name.");
} else {
response.send(objectParsed);
}
});
});
apiRequest.end();
});
Also, you don't want JSON.parse(JSON.stringify(data)) here. Your data is already a string. Just do JSON.parse(data).
FYI, if you use a small http request library such as got(), this code gets a lot simpler as it accumulates the response and parses the JSON for you in one line of code as in:
let data = await got(options).json()

Ajax call on $.ajax().complete

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.");
}
);
}

Abstracting the making of requests with Node.js

Traditionally I use jQuery for all my JS code, but I'm tasked to launch a simple API with node.js. Today is my first day with Node but I know enough about JS and closures to do OK. One of the tasks of the API is to authenticate across a third party service and being a python guy, I wanted to abstract all my outbound request calls like so:
EDIT
var http = require('http');
var init = function(nconf) {
var methods = {
/*
Helper method to create the request header
*/
headers: function(params) {
var content = JSON.stringify(params);
return {
'Content-Type': 'application/json',
'Content-Length': content.length
}
},
/*
Helper method to create the options object
which is used in making any type of
outbound http request
*/
options: function(host, path, method, params) {
return {
host: host,
port: 80,
path: path,
method: method,
headers: methods.headers(params)
}
},
/*
Helper method to abstract the making of
outbound http requests
*/
call: function(options, params, success, err) {
var req = http.request(options, success);
req.on('error', err);
req.write(params);
req.end();
},
/*
Helper method to parse the response
and return a json object
*/
parse: function(res, result) {
var responseString = '';
res.on('data', function(data) {
responseString += data;
});
res.on('end', function() {
result = JSON.parse(responseString);
});
},
/*
API method to return the latest
release and tag names
*/
latest: function(req, res, next){
// // var url = nconf.get('prod:authenticate');
//authenticate the test user
msg = methods.authenticate(nconf.get('test:user'), nconf.get("test:password"));
res.send(msg);
next();
},
/*
Method used by this API to authenticate users.
It is used to de-couple this API from the Database
Schema by calling out to the TTCPAS App and requesting it
to handle the authentication
*/
authenticate: function(username, password){
// create post parameters with API key
var params = {"username": username, "password": password, "api_key": nconf.get('api_key')};
//construct options object with params and header
var options = methods.options(nconf.get('ttcpas:host'), nconf.get('ttcpas:auth_url'), 'POST', params);
var result;
var success = function(res) {
res.setEncoding('utf-8');
methods.parse(res, result);
};
methods.call(options, params, success, function(err){});
while (typeof(result.statusCode) == 'undefined') {
//wait 1 second;
setTimeout(function(){
console.log("waiting on request at " + nconf.get('ttcpas:host') + nconf.get('ttcpas:auth_url'));
}, 1000);
}
//then down here
if (result.statusCode == 200) {return result};//success
if (result.statusCode == 403) {return "forbidden"};//forbidden
}
}
return methods;
};
module.exports.init = init;
#jfriend00 As I said I don't know how node.js is supposed to be styled. I wanted to just abstract as much as possible to make the code clean and reusable
Now when I do http://localhost:9000/latest/
I get:
{"code":"InternalError","message":"first argument must be a string or Buffer"}
Uhhh, this part will simply not work:
while (typeof(result.statusCode) == 'undefined') {
//wait 1 second;
setTimeout(function(){
console.log("waiting on request at " + nconf.get('ttcpas:host') + nconf.get('ttcpas:auth_url'));
}, 1000);
}
If result.statusCode is ever undefined, this will spin forever piling up setTimeout() calls in the event queue until eventually something fills up or you run out of memory.
Because node.js is primarily single threaded, you can't loop waiting for something to change. Because you never finish this while loop, no other node.js code gets to run so result.statusCode can never change. Thus, you have an infinite loop here.
All of your nodejs code needs to be event driven, not spin/wait loops. FYI, this is similar to browser-based Javascript.

Getting error while fetching data using angularjs and REST api from sharepoint list

I'm fetching data from the sharepoint list using angularjs in regular intervals. After sometime, I'm getting error in the console as SCRIPT7002: XMLHttpRequest: Network Error 0x2eff, Could not complete the operation due to error 00002eff. I'm using IE 10 as browser. Any idea about this error ?
Please find the method which i repeatedly calls to get data below.
// Method to get all the notifications for the user
var getUserNotifications = function () {
// Create a deferred object
var deferred = $q.defer();
// Url for getting the list with filters
var url = _spPageContextInfo.webServerRelativeUrl +
"_api/web/lists/getByTitle('" + list + "')/items?" + filters;
// API call to get the notification items from list
var method = $http({
method: "GET",
url: url,
headers: {
"Accept": "application/json;odata=verbose"
}
});
// Executing the method
method.then(
// Success function
function (data) {
var d = data.data.d.results;
notification = d;
// If timer is not started, then start it.
if (!isTimerStarted) {
setInterval(function () {
getUserNotifications();
}, 3000);
isTimerStarted = true;
}
deferred.resolve(d);
},
// Error
function (err) {
deferred.reject("Error");
}
)
return deferred.promise;
};

Dynamically append content to an element with jQuery

I would like to be able to have an ajax get update the text in a span tag each time it is fired.
$.ajax({
type: 'GET',
url: "JSON URL",
cache: false,
contentType: 'application/json',
dataType: 'json',
success: function(html){
$('#status_frame_span').prepend(html.status)
alert(html.status)
},
error: function(jq,stats,errmes) {
alert("Error" + errmes);
}
});
the first time it fires, the content of the json returned from the URL is properly prepended to the span. however for subsequent firings it is not updated.
How do I ensure that with each firing the content gets updated?
What triggers the call to the server? Is it a button or link inside of the HTML being updated? if it is, the event handler may be lost when the UI is updated. Or, something else is losing the event handler, which doesn't call the method to fire the get request, etc.
HTH.
Of course your view is updated only once: you are calling the server only once!
If, as your tags suggest, you are using long polling (please make sure that's the case, I'm not sure you have a very clear idea of what is an event, a poll and a distant call), then you need to make a new request each time you've received one!
In both your success and error handlers, you have to recursively make an AJAX call to the server. You also have to set a timeout for the calls, which could cancel them and start a new one after, for example, 30 seconds.
You should also implement some kind of throttling for recursive calls, unless you're 99.99% sure the server page will never send errors. Otherwise, you'll kill your client.
For the sake of completeness, I have to add this would be a great use-case for HTML5 SSE or WebSocket. But they're not ready for production usage yet.
it does not work that way - if the success callback is called - the connection has been closed so your long polling will be dead once the request is completed.
The idea behind long polling is that you keep the connection alive. Configure your server properly so that it will hold the connection open as long as possible (set timeout as high as possible).
Here's an approach from my coffee break (not tested):
Server
Every message has to end with the delimiter ::PART::
The server must be properly configured this means set the timeout as high as possible!
Client (Browser)
// setup longpoll, check all 250ms for new data in the stream
var myPoller = new LongPoll('some-url', 250);
// bind connection lost
myPoller.bind('longpoll:end', function(evt) {
alert('connection lost - trying reconnect');
});
// bind error event
myPoller.bind('longpoll:error', function(evt, errmsg) {
alert('error: ' + errmsg);
});
// bind data event
myPoller.bind('longpoll:data', function(evt, data) {
try {
// try to parse json
data = $.parseJSON(data);
// prepend
$('#status_frame_span').prepend(data.status);
} catch(e) {
// invalid json
alert('invalid json: ' + data);
}
});
longpoll.js
var LongPoll = function(url, timeout) {
// url we connect to
this.url = url;
// running?
this.isRunning = false;
// timer for checking the stream
this.timer = null;
// the length of the received data
this.dataLength = 0;
/*
The messages has to be delimited by the delimiter like:
first data::PART::second data::PART::third data::PART::
*/
this.delimiter = RegExp.new("::PART::", 'gm');
// residue from previous transmission
this.residue = ''
};
// connect to server
LongPoll.prototype.connect = function() {
var self = this;
// reset data length
this.dataLength = 0;
// reset residue
this.residue = '';
// start ajax request
this.xhr = $.ajax({
type: 'GET',
url: this.url,
cache: false,
contentType: 'application/json',
dataType: 'text',
success: function(){
// the connection is dead!
self.xhr = null;
// trigger event
$(self).trigger('longpoll:end');
// reconnect if still running
if(self.isRunning) {
self.connect();
}
},
error: function(jq,stats,errmes) {
// stop timer and connection
self.stop();
$(self).trigger('longpoll:error', errmes);
}
});
};
// process data
LongPoll.prototype.process = function(buffer) {
var self = this;
// check if there is anything new
if(buffer.length > this.dataLength) {
var newData = this.residue + buffer.substring(this.dataLength, buffer.length);
// reset residue
this.residue = '';
// store the new position
this.dataLength = buffer.length;
// split data
var dataParts = newData.split(this.delimiter);
// how many full parts?
var fullParts = newData.match(this.delimiter).length;
if(dataParts.length > fullParts) {
// pop residue (incomplete message)
this.residue += dataParts.pop();
}
$.each(dataParts, function(index, part) {
// broadcast data parts
$(self).trigger('longpoll:data', $.trim(data));
});
}
};
// check for data
LongPoll.prototype.receive = function() {
var self = this;
// connection still there?
if(this.xhr) {
// process buffer
this.process(this.xhr.responseText);
}
};
// start long poll
LongPoll.prototype.start = function() {
var self = this;
// set flag
this.isRunning = true;
this.timer = setInterval(function() { self.receive(); }, this.timeout);
this.connect();
};
// stop long poll
LongPoll.prototype.stop = function() {
// set flag
this.isRunning = false;
// clear timer
clearInterval(this.timer);
if(this.xhr) {
// abort request
this.xhr.abort();
}
};

Categories