I’m developing an application using Express and I need to call the Spotify endpoint to add to a user’s queue multiple times with different parameters, basically in a for loop. All requests return a 2xx status code, but not all of the tracks were actually added to my queue. It could be that only one was added, or multiple, but never all. I’m not really sure how to debug this because the call is returning a 2xx response code every time, so I’m wondering if perhaps there’s a general rule of thumb for making multiple calls to an API.
Here is where I call addToQueue in a loop:
let finishedRequests = 0
requests.forEach(async r => {
addToQueue(r.songId, accessToken, async () => {
try {
await RequestModel.findByIdAndUpdate(r._id, { $set: { serviced: true, accepted }})
return {
status: 200
}
} catch(err) {
return {
status: 500,
message: JSON.stringify(err)
}
}
})
.then(response => {
if (response.status === 500) {
return res.status(500).json({ err: err.message })
}
finishedRequests++
if (finishedRequests === requests.length) {
return res.status(200).send()
}
})
.catch(err => {
return res.status(err.status).json({ err: err.message})
})
And here is the addToQueue function:
const addToQueue = (songId, accessToken, successCallback) => {
return new Promise((resolve, reject) => {
const options = {
url: `https://api.spotify.com/v1/me/player/queue?uri=spotify:track:${songId}`,
headers: {
'Authorization': `Bearer ${accessToken}`
},
json: true
}
requestModule.post(options, async (error, response, body) => {
if (!error && response.statusCode >= 200 && response.statusCode < 300) {
resolve(successCallback())
} else {
if (error) {
reject({
status: 500,
mesage: JSON.stringify(error)
})
} else {
if (response.statusCode === 404) {
reject({
status: 404,
message: 'no queue'
})
}
reject({
status: response.statusCode,
message: 'spotify error'
})
}
}
})
})
}
Any help in making sure that all tracks are added to the queue is appreciated. Thanks
I was experiencing the same problem but solved it by making sure all requests are executed one after the other. You should try using a recursive function instead of the forEach. Once a request returns a positive status you would rerun the same function but only with the remaining songs as the parameter.
This way the requests will be added to the queue in the correct order and you won't add a bulk of songs in the same second. This also brings the benefit that you can be pretty sure that the songs will be in the right order when arriving in the Spotify queue.
I hope I could help!
Related
I am trying to send a post request to the App Store verifyReceipt endpoint in a Firebase cloud function. However, I get the following error in the cloud function log:
{ Error: read ECONNRESET
at TLSWrap.onread (net.js:622:25)
errno: 'ECONNRESET',
code: 'ECONNRESET',
syscall: 'read',
However, this error is only occasional. It does not happen every time, so the function obviously works, but something is going wrong sometimes and I'm not sure what.
Most other solutions relating to this were due to a promise error, but I do not believe that is the problem here. Below is the complete function:
exports.handleSubscriptionIAP_IOS_S2S = functions.https.onRequest((req, res) => {
let data = req.body;
console.log('received ios s2s notification with body:', data);
let base64String = data.unified_receipt.latest_receipt;
let boolStatus = true;
// The user has changed the auto renewal status, store the change
if(data.notification_type === 'DID_CHANGE_RENEWAL_STATUS') {
boolStatus = (data.auto_renew_status === 'true');
}
console.log(data.notification_type);
if(base64String) {
var options = {
method: 'post',
url: 'https://buy.itunes.apple.com/verifyReceipt',
data: ({
"receipt-data" : base64String,
"password" : "***",
"exclude-old-transactions" : true
})
};
var optionsSandbox = {
method: 'post',
url: 'https://sandbox.itunes.apple.com/verifyReceipt',
data: ({
"receipt-data" : base64String,
"password" : "***",
"exclude-old-transactions" : true
})
};
return axios(options)
.then((response) => {
if(response.data.status === 21007) {
return 'handle_sandbox';
}
// Got valid response from Apple, pass down chain
else {
return response;
}
})
.then((response) => {
// Send post request to sandbox endpoint
if(response === 'handle_sandbox') {
return axios(optionsSandbox);
}
// Pass response down chain
else {
return response;
}
})
.then((response) => {
// Handle response from Apple verifyReceipt endpoint here
// Both production and sandbox will end up here
// See here for values in response.data: https://developer.apple.com/documentation/appstorereceipts/responsebody/latest_receipt_info
console.log('received ios s2s notification verify receipt response with body:', response.data);
// Status 0 means request is valid
if(response.data.status === 0) {
// Get receipt info of latest receipt
// Only one object in array is return since we exclude old transactions
let latestReceipt = response.data.latest_receipt_info[0];
// Save receipt into Firestore
return db.collection('appStoreReceipts').doc(latestReceipt.original_transaction_id).set({
latest_receipt_info: latestReceipt,
expiresTimestamp: admin.firestore.Timestamp.fromMillis(parseInt(latestReceipt.expires_date_ms)),
originalTransactionID: latestReceipt.original_transaction_id,
autoRenewStatus: boolStatus,
base64Receipt: response.data.latest_receipt,
}, { merge: true });
}
else {
return null;
}
})
.then((result) => {
if(result) {
return res.status(200).end();
}
else {
return res.status(400).end();
}
})
.catch((error) => {
console.log('an error occured handling the subscription', error);
return res.status(400).end();
})
}
else {
console.log('invalid receipt', data);
return res.status(400).end();
}
});
Thank you for any help that you can give!
ECONNRESET just means that the other end of the connection closed it. That could mean any number of things. It's probably not your code's fault, unless you've done something so bad in this one request that Apple decided to close the connection. You should contact their support directly if you think they're doing this incorrectly.
Hello I want to connect my watson assisstant with an alexa device, for this I need Amazon development skill kit and AWS lambda. But i can't connect watson because i got problem with my promises and i can't see the logs of my code in the amazon developer console. And my assistant work on nodeJs application.
There is some codes that i tried :
const MyNameIsIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'SearchIntent';
},
async handle(handlerInput) {
assistant.createSession({
assistant_id: assistant_id
})
.then(res => {
session_id = res.session_id;
})
.catch(err => {
console.log(err);
});
assistant.message({
assistant_id: assistant_id,
session_id: session_id,
input: {
'message_type': 'text',
'text': "hello"
}
})
.then(res => {
console.log(JSON.stringify(res, null, 2));
speechText = res.output.generic.response.text;
})
.catch(err => {
speechText = err;
});
}, function(err){
speechText = "Problem with Api call";
});
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
},
};
And other way with promise:
try{
let res = await assistant.createSession({
assistant_id: assistant_id
});
session_id = res.session_id;
let message = await assistant.message({
assistant_id: assistant_id,
session_id: session_id,
input: {
'message_type': 'text',
'text': "hello"
}
});
speechText = message.output.generic.response.text;
}catch(err){
speechText = err;
}
The results of speechText should give me "Good day to you" it's a response that comes from Watson.but now Alexa says "Sorry, I can't understand the command. Please say again."
Do you have an others ways to try this with other way to do a promise? thanks you!
Some thoughts for consideration ...
Looking at the first attempt, what you have in handle() is two independent promise chains, efectively like this:
async handle(handlerInput) {
doSomething_1().then(...).catch(...);
doSomething_2().then(...).catch(...);
},
What you appear to need is to perform the two async operations in series:
handle(handlerInput) {
return doSomething_1().then(doSomething_2)....;
},
In full (with some guesswork/inventiveness):
handle(handlerInput) {
return assistant.createSession({ assistant_id })
.then(res => {
return assistant.message({
'assistant_id': assistant_id, // ??
'session_id': res.session_id,
'input': {
'message_type': 'text',
'text': 'hello'
}
})
.catch(err => {
err.message = 'Problem with assistant.message() call';
throw err; // send error with custom message down the down the error path.
}
}, err => {
err.message = 'Problem with assistant.createSession() call';
throw err; // send error with custom message down the down the error path.
}
.then(res => res.output.generic.response.text) // send res.output.generic.response.text down the success path.
.catch(err => err.message) // catch error from anywhere above and send err.message down the success path to appear as `speechText` below.
.then(speechText => { // `speechText` is the text delivered by the immediately preceeding .then() or .catch()
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
})
.catch(err => { // in case handlerInput.responseBuilder.speak().getResponse() throws
console.log(err);
});
}
Notes:
text is passed down the promise chain's success path and appears as speechText in the final .then(); no need for an outer variable.
custom error messages (if required) are injected with intermediate catches, which modify and (importantly) rethrow their errors.
a third intermediate catch intercepts the error path and sends err.message down the success path.
consumption of speechText is necessarily dependent on the sequence assistant.message().then(assistant.message) having completed.
code could be written with async/await but would difffer only in semantics; both versions would exploit promises.
The code is not necessarily a working solution, but should help in the understanding of the kind of data/error flow required by this application.
Good luck with it.
I have the following code:
User.getConfByID(userID)
.then((item)=>{
if(item.length == 0){
res.status(400).json({error:"NO_USER_FOUND"})
}else{
if(item[0].token == token){
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid){
res.status(401).json({error: "TOKEN_INVALID"})
}else{
return mariaDBTemplates.updateOneRowTemplate("User_confirmation", {confirmed:1}, "user_id", userID)
}
}else{
res.status(401).json({error: "TOKEN_NOT_SAME"})
}
}
})
.then(()=>{
res.status(200).json({success: "CONFIRMED"})
})
.catch((err)=>{
res.status(500).json({error: err.message})
})
You see I have different kinds of error messages with different kinds of status codes. When I run this code, it always gives me this warning:
Error: Can't set headers after they are sent
I think this is because i don't "break" the Promise after sending a response right?. But how can I solve this? Any suggestions?
Cheerio
your problem is with your promise chain. in your first .then, you always set the response with res, but the next .then in the chain tries to set the response again. Note that not returning anything from a promise is the same as return Promise.resolve(undefined);.
here's how I would do it:
User.getConfByID(userID)
.then((item) => {
if(item.length == 0)
return { statusCode: 400, body: { error: "NO_USER_FOUND" } };
else {
if(item[0].token == token) {
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid)
return { statusCode: 401, body: { error: "TOKEN_INVALID" } };
else {
//not sure what this returns, but it looks like this is
//what you're trying to return the 200 for
mariaDBTemplates.updateOneRowTemplate("User_confirmation", { confirmed: 1 }, "user_id", userID);
return { statusCode: 200, body: { success: "CONFIRMED" } };
}
} else
return { statusCode: 401, body: { error: "TOKEN_NOT_SAME" } };
}
})
.then((result) => {
res.status(result.statusCode).json(result.body);
})
.catch((err) => {
res.status(500).json({ error: err.message });
});
Also note that returning a value from a promise is the same as returning Promise.resolve(value);, and will continue the promise chain.
I send data from my input fields to my api:
$.ajax({
url: '/api/login',
type: 'GET',
dataType: 'json',
ContentType: 'application/json',
data: {formData},
success: (data) => {
console.log('SUCCESS')
console.log(data)
this.setState({
isInProcess: false
})
},
error: (jqXHR) => {
console.log(jqXHR)
console.log('ERROR')
this.setState({isInProcess: false})
}
})
on my server-side I have a function to see if I have required user in db:
async function findUser(data) {
try {
const user = await User.findOne({email: data.email,
password: data.password})
console.log('User was found')
return { user }
} catch (err) {
console.log('error', err)
throw err
}
}
which will be executed here:
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
return res.json(foundUserData)
} catch (err) {
return res.status(400).json(err)
}
})
It works fine, but if a user wasn't found in db i sends success anyway. Why?
await findUser(data.formData) won't throw error, return either null or user object. You may check something following
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
if(foundUserData && foundUserData.user) {
return res.json(foundUserData)
} else {
return res.status(400).json({message: 'User not found'});
}
} catch (err) {
return res.status(500).json(err)
}
})
It sends success because none of your queries error'ed, just because it didn't find anything does not mean that the query failed because it obviously succeeded in finding out if what ever you're looking for exists or not.
To send an error in case of not found you need to check if response is empty in which case you want to send error
When no user is find you get a null value. You may try to put more logic on your success parameter with that for example:
success: function (data) {
if(!!data && data != null) {
alert('Success');
} else {
alert('No data');
}
}
I am using Express and Passwordless module to setup passwordless authentication. Everything works, gte, but i am stuck at one issue.
As a part of the delivery method, I am using an external api to send the sms to the user. If the sms is sent correctly, everything works fine. However, if the sms fails to send, there is still no error, but the response from the api call tells me that the sms wa a failure. In case of failure, the api response looks like this:
{ warnings: [ { message: 'Message sending has failed', numbers: '91545454' } ], errors: [ { code: 51, message: 'No valid numbers specified' } ],
status: 'failure' }
Now, in my node/express app, if the api response has status = failure, I would like to send them to a different page where I will tell users that the sms failed.
My code looks like this:
passwordless.addDelivery(
function(tokenToSend, uidToSend, recipient, callback) {
var smscall = 'MY API CALL URL;
needle.get(smscall, function(error, response) {
if(error) {
console.log(error);
}
if (!error && response.statusCode == 200) {
console.log(response.body);
}
if(response.body.status == 'failure') {
console.log('Failed');
THIS IS WHERE I WOULD LIKE TO REDIRECT USER TO A DIFFERENT TEMPLATE OR RENDER A MESSAGE.
}
callback(error);
});
});
The requestToken code looks like below:
router.post('/', passwordless.requestToken(function(user, delivery, callback) {
callback(null, user);
}, { failureRedirect: '/error' }),
function (req, res) {
res.render('verify', { uid: req.passwordless.uidToAuth });
}
);
Try to call a callback with error in your last condition block.
if(response.body.status === 'failure') {
console.log('Failed');
return callback(new Error(response.body.status));
}
Just call res.render where you check for the failed sms status :
passwordless.addDelivery(
function(tokenToSend, uidToSend, recipient, callback) {
var smscall = 'MY API CALL URL;
needle.get(smscall, function(error, response) {
if(error) {
console.log(error);
}
if (!error && response.statusCode == 200) {
console.log(response.body);
}
if(response.body.status == 'failure') {
console.log('Failed');
return res.render('smsFailPage');
}
callback(error);
});
});