Not same value in the same variable - node.js

I am running into issues putting many items in DynamoDB
I am using this lib : https://github.com/baseprime/dynamodb
I get the error : The conditional request failed
Here is my code
const addInput = input => {
console.log('input', input);
return new Promise((resolve, reject) => {
Input.create(
{
id: input.id,
playlist: input.playlist
},
{
overwrite: false
},
(err, inputRes) => {
if (err) {
console.log('input2', input);
console.log('inputRes', inputRes);
console.log('err', err);
reject(err);
}
resolve(inputRes.get('id'));
}
);
});
};
And my logs shows for the same lambda request id
input { id: 1567787730645,
playlist: 2010}
input2 { id: 1567786871913,
playlist: 80}
inputRes undefined
err { ConditionalCheckFailedException: The conditional request failed ..
How can the input variables have different values ?

Here's the fix:
const addInput = input => {
const data = { id: input.id, playlist: input.playlist };
console.log('input', input);
return new Promise((resolve, reject) => {
Input.create(data,
{
overwrite: false
},
(err, inputRes) => {
if (err) {
console.log('input2', input);
console.log('inputRes', inputRes);
console.log('err', err);
reject(err);
}
resolve(inputRes.get('id'));
});
});
};
Exaplanation
You make the Input.create() inside the Promise's function. This function is not invoked when the promise is created. Instead, it is invoked at some unknown time in the future. This means that after addInput() any changes made to the object passed to addInput() will affect the id, playlist values passed to Input.create(). By copying these values at the beginning of addInput(), and not inside the Promise's function, you fix this problem.

Related

How to ensure that one function completes before other function

I am fairly new to Node.js, and what I am trying to achieve is to have two separate functions. One for Auth and one for sending data (So that I don't run into rate login limits if I were to simply use a callback after conn.login finishes). I tried to set this up in node like this:
var _request = {
url: '/services/data/v45.0/actions/custom/flow/Test1',
method: 'POST',
body: JSON.stringify({
"inputs": [{}]
}),
headers: {
"Content-Type": "application/json"
}
};
var conn = new jsforce.Connection({
clientId: process.env.cliendId,
clientSecret: process.env.clientSecret,
version: "45.0"
});
function sfdcAuth() {
conn.login(process.env.sfdcUser, process.env.sfdcUserPass, (err, userInfo) => {
if (err) {
console.log(err)
}
conn = conn;
console.log("Done")
});
}
function sfdcQuery() {
conn.request(_request, function(err, resp) {
console.log(resp);
console.log(err)
});
}
sfdcAuth()
sfdcQuery()
But because js is asynchronous it runs the second function without waiting for the first function to finish.
The simplest way is to pass your second function as a callback to your first function, which it can call when it’s done:
function sfdcAuth(callback) {
conn.login(process.env.sfdcUser, process.env.sfdcUserPass, (err, userInfo) => {
if (err) {
console.log(err);
}
// Invoke callback when done
callback();
});
}
function sfdcQuery() {
conn.request(_request, function(err, resp) {
console.log(resp);
console.log(err);
});
}
// Pass second function as callback to the first
sfdcAuth(sfdcQuery);
You could also make use of promises:
function sfdcAuth(callback) {
return new Promise((resolve, reject) => {
conn.login(process.env.sfdcUser, process.env.sfdcUserPass, (err, userInfo) => {
if (err) {
reject(err);
}
resolve(userInfo);
});
});
}
function sfdcQuery() {
return new Promise((resolve, reject) => {
conn.request(_request, function(err, resp) {
if (err) {
reject(err);
}
resolve(resp);
});
});
}
// Wait for promise to resolve before invoking second function
sfdcAuth()
.then(result => {
// Do something with result
return sfdcQuery();
})
.then(result => {
// You can continue the chain with
// the result from "sfdcQuery" if you want
})
.catch(err => {
// Handle error
});

When I try to call a function from a class in an async function there are endless requests being sent

I have a function inside of a class that is supposed to create a new auto scaling group in AWS. When I try to call the function, my bash terminal keeps sending endless number of requests.
createASG = async function(csm)
{
const response = {
poolAsgName: csm._info.body["poolAsgName"],
region: csm._info.body["region"],
initialSize: csm._info.body["initialSize"],
MaxSize: csm._info.body["maxSize"]
}
const newauto = new AutoScalingGroupHandler(response.poolAsgName,response.region);
try{
return await newauto._createNewASG(response.initialSize,response.MaxSize);
}
catch(err){
logger.error(err);
return;
}
};
_createNewASG:
_createNewASG(initialSize,MaxSize)
{
return new Promise((resolve, reject) => {
const params = {
AutoScalingGroupName: this._poolAsgName,
LaunchConfigurationName: 'test',
MaxSize: MaxSize,
MinSize: initialSize,
AvailabilityZones: [this._region]
};
this._awsAutoScaling.createAutoScalingGroup(params, function (err, data) {
if (err) {
reject(err);
} else{
resolve(data);
}
});
});
}
I expected that the function would return a successful response, but instead my bash terminal continues spamming "Max request reach retry again in ..." in an endless loop of some sorts.

Dialogflow v2 Nodejs Client Library UpdateIntent when adding Training Phrases

I am trying to use the updateIntent function that is part of the Dialogflow v2 Client library for Node.js . The reason I am trying to use it, is to be able to add training phrases to an intent.
I cannot seem to get passed this one. Here is the code I am using for it!:
My GetIntent Function:
async function getIntent(intentId) {
try {
let responses = await intentsClient.getIntent({name: intentId, intentView: 'INTENT_VIEW_FULL'})
const response = responses[0]
// console.log(response)
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
return new Promise((resolve, reject) => {
reject(err)
})
}
}
My UpdateIntent Function:
async function updateIntent(intent) {
const request = {
intent: intent,
languageCode: 'en-US',
updateMask: {
paths: ['trainingPhrases']
},
intentView: 'INTENT_VIEW_FULL'
}
try {
let responses = await intentsClient.updateIntent(request)
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
console.log(err)
return new Promise((resolve, reject) => {
reject(err)
})
}
}
The Function that Calls it:
async function testUpdateTraining () {
try {
let intent = await getIntent('projects/small-talk-1-406ae/agent/intents/ac7f0b68-de5c-4b6f-9393-358dd2b0c1bd')
let trainingPhrase = { parts: [{ text: 'How should I behave on the trails?'}],
type: 'EXAMPLE'}
intent.trainingPhrases.push(trainingPhrase)
try {
let updatedIntent = await updateIntent(intent)
} catch (e) {
console.log(e)
console.log('failed to update the intent')
}
} catch (err) {
console.log('failed to get intent')
}
}
Now the weird thing is - I am getting a 200 response from the client library call. The Api doc states that upon a successful response you will get an intent object. I am getting an intent object with the training phrases inside...
[![{ inputContextNames: \[\],
events: \[\],
trainingPhrases:
\[ { parts: \[Array\],
name: 'ad0d1f6a-78cf-4e0b-84ca-ec62a45c75dc',
type: 'EXAMPLE',
timesAddedCount: 0 },
{ parts: \[Array\],
name: 'e33cce4b-96ee-4e35-a151-5b09ff603817',
type: 'EXAMPLE',
timesAddedCount: 0 },
{ parts: \[Array\],
name: '7d9b7c56-5fa8-4791-986f-e57b9f90d431',
type: 'EXAMPLE',
timesAddedCount: 0 } \],
outputContexts: \[\],
parameters: \[\],
messages:
\[ { platform: 'PLATFORM_UNSPECIFIED',
text: \[Object\],
message: 'text' } \],
defaultResponsePlatforms: \[\],
followupIntentInfo: \[\],
name: 'projects/small-talk-1-406ae/agent/intents/ac7f0b68-de5c-4b6f-9393-358dd2b0c1bd',
displayName: 'faq.offroad.card1answer',
priority: 500000,
isFallback: false,
webhookState: 'WEBHOOK_STATE_UNSPECIFIED',
action: 'faq.offroad.card1answer',
resetContexts: false,
rootFollowupIntentName: '',
parentFollowupIntentName: '',
mlDisabled: true }][1]][1]
This is what dialogflow has. Only two training phrases here, the one I added programmatically does not show up.
So my question is, how can I format the request so I can update the training phrases without a problem? Is there an example I can run off?
After trying out a lot, understood that my code worked because i removed update mask. And the languageCode as well, because it was giving me an error.
The code is as below and works fine.
Check it up.
This is the getIntent function:
async function getIntent(intentId) {
try {
let responses = await intentsClient.getIntent({
name: intentId,
intentView: 'INTENT_VIEW_FULL'
})
const response = responses[0];
console.log(util.inspect(response, false, null, true /* enable colors */ ));
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
return new Promise((resolve, reject) => {
reject(err)
})
}
}
The function that calls it:
async function testUpdateTraining () {
try {
let intent = await getIntent('<your_ID>')
let trainingPhrase = {
parts: [{
text: 'let me call you?'
}],
type: 'EXAMPLE'
}
intent.trainingPhrases.push(trainingPhrase)
try {
let updatedIntent = await updateIntent(intent)
} catch (e) {
console.log(e)
console.log('failed to update the intent')
}
} catch (err) {
console.log('failed to get intent')
}
}
The UpdateIntent function:
async function updateIntent(intent) {
const request = {
intent: intent,
intentView: 'INTENT_VIEW_FULL'
}
try {
let responses = await intentsClient.updateIntent(request)
return new Promise((resolve, reject) => {
resolve(responses)
})
} catch (err) {
console.log(err)
return new Promise((resolve, reject) => {
reject(err)
})
}
}
You can try:
updateMask: {
paths: ['training_phrases']
}
since paths naming convetion is "snake"

Making second api call after first api call returns data

I have two seperate functions that make API calls to different endpoints for return JSON Data using BlueBirdPromise.
const searchVenues = (type) => {
logger.debug('getVenues : type = ' + type);
const config = {
url: urlAPIServer + '/venue/available',
qs: {
type,
},
headers: {
'x-api-key': dataApiKey
}
};
return new BluebirdPromise((resolve, reject) => {
request.get(config, (err, response, body) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(JSON.parse(body));
}
});
});
};
const getVenuesWithCuisine = () => {
logger.debug('getVenuesWithCuisine');
const config = {
url: urlAPIServer + '/venue/viewvenuewithcuisine',
headers: {
'x-api-key': dataApiKey
}
};
return new BluebirdPromise((resolve, reject) => {
request.get(config, (err, response, body) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(JSON.parse(body));
}
});
});
};
Invoking the funtions seperately to get the data from the API and bind it to variable.
searchVenues(venueType).then((venues) => {
checkContextTimeout(context);
conversationContext.venueType = venueType;
conversationContext.venues = venues;
context.skill = conversationContext;
});
getVenuesWithCuisine().then((venueswithcuisines) => {
conversationContext.venue_details = venueswithcuisines[0}["venue_details"];
conversationContext.cuisines = venueswithcuisines[1]["cuisines"];
conversationContext.venueType = venueType;
conversationContext.venues = venuesJson.venues;
continueConversation(request, response, context);
});
The problem with above implementation is, if for some reason getVenuesWithCuisine completed first before searchVenues the continueConversation is getting invokes making conversationContext.venues = venues as null.
How can i make these API calls synchronous so that the second API call is made only after the first one returns data.
You can use Promise.all instead of doing it synchronously. That will resolve once both of your promises resolve and give you the results.
http://bluebirdjs.com/docs/api/promise.all.html
Promise.all([searchVenues(venueType), getVenuesWithCuisine()]).then(function([venueResp, cuisineResp]) {
...
});

NodeJS, MongoDB - Adding sequential ID and promise problems

I'm coming from MS SQL so to make things easier in my mind, I'm trying to create the equivalent of a sequential primary key. Using some online articles and API references I've constructed the following:
function getNextSequence(name) {
var ret = db.collection('counters').findOneAndUpdate(
{_id: name },
{ $inc: { seq: 1 } },
{returnNewDocument: true}
)
return ret.seq;
}
console.log(getNextSequence("sms_id"))
db.collection('SMS').insertOne({
"_id":getNextSequence("sms_id"),
record
}, (err, result) => {
if (err) {
return console.log('Unable to insert record', err);
}
console.log(JSON.stringify(result.ops, undefined, 2));
});
The problem is the getNextSequence() function is continuing before the findOneAndUpdate() method inside of it finishes. After some debugging, I've determined that it is a promise that is pending, so I tried making the following changes:
function getNextSequence(name) {
var ret = db.collection('counters').findOneAndUpdate(
{_id: name },
{ $inc: { seq: 1 } },
{returnNewDocument: true}
).then(() => {
return ret.seq
});
}
But it still continues on. How can I get it to wait for the promise to finish?
You want to construct a sequence of async executions, which is simple with Promises by returning them throughout your code:
function getNextSequence(name) {
return db.collection('counters').findOneAndUpdate(
{_id: name },
{ $inc: { seq: 1 } },
{returnNewDocument: true}
).then(ret => ret.seq);
}
And then using the function:
getNextSequence('sms_id').then((seq) => {
return db.collection('SMS').insertOne({
"_id": seq,
record
});
}).then((result) => {
console.log(JSON.stringify(result.ops, undefined, 2));
}).catch((err) => {
console.log('Unable to insert record', err);
});
Note that the error passed to the .catch callback can either be from getNextSequence or the insertOne method call on the SMS collection.
If you return another promise from within the callback of a Promise's .then-call, the next .then-call will wait for that promise to fulfill. See the below snippet for an example:
function waitAndLog (msg, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(msg)
resolve()
}, ms)
})
}
waitAndLog("there", 1000).then(() => {
return waitAndLog("is", 1000)
}).then(() => {
return waitAndLog("no", 1000)
}).then(() => {
return waitAndLog("spoon", 1000)
}).then(() => {
console.log("Sequence complete")
})

Resources