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"
Related
Specific example from kubernetes-client:
Application code:
console.log(`Trying to create a namespace: ${namespaceName}`)
try {
await client.api.v1.namespaces(namespaceName).get()
} catch (err) {
try {
await client.api.v1.namespaces.post(namespaceJson)
} catch (err) {
console.log('Error creating namespace: ' + err)
return new Promise(function (resolve, reject) {
reject(new Error(`Failure creating namespace: ${namespaceName}: ${err}`))
})
}
...
Tests:
jest.doMock('kubernetes-client', () => {
const aRejection = jest.fn(() => {
const err = new Error(404)
err.response = 'Not found'
err.status = 404
err.statusCode = 404
return Promise.reject(err)
})
const aPromise = jest.fn(() => {
return Promise.resolve({
body: {
data: {
username: 'dXNlcm5hbWUK' // 'username', base64-encoded
}
}
})
})
const namespaces = jest.fn().mockReturnValue({
get: aRejection,
post: aPromise
})
const mockClient = {
api: {
v1: {
namespaces
}
},
}
return {
Client: jest.fn(() => mockClient),
}
})
Note that client.api.v1.namespaces.post(namespaceJson) (namespaces is an object) and client.api.v1.namespaces(namespace).get() (namespaces is a function) are both valid.
If I mock namespaces as a function, the tests fail because client.api.v1.namespaces.post() is not a function.
Hopefully I'm just doing something wrong somewhere but I don't know what that is.
I am trying to write an async lambda function which is calling a function for sign up a user in cognito.
my problem is that in my lambda function, it is not waiting for the result and finish the execution. would you mind check what is my issue? I am new to rxjs. please help me.
mylambda function
exports.handler = async (event, context) => {
//poolData and params will fetch from event
let source = await signup(poolData, params);
console.log(source);
});
my signup function
function signup(poolData, body) {
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const { username, password, attributes } = body;
const attributesList = [];
if (Array.isArray(attributes)) {
attributesList.push(
...attributes.map(item => new AmazonCognitoIdentity.CognitoUserAttribute(item))
);
}
let source = Observable.create(observer => {
let output = (err, res) => {
if (err)
{
observer.error(err);
}
else
{
const cognitoUser = res.user;
const data = {
username: cognitoUser.getUsername(),
};
observer.next(data);
}
observer.complete();
}
userPool.signUp(username, password, attributesList, null, output);
});
let respond;
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
source.subscribe(subscriber);
}
module.exports = signup;
This behavior is totally normal.
So first thing first, an observable is not a promise which means you are not able to await a response with the await keyword, also I don't see anything to be returned from the signup function, which will probably lead to undefined to be logged anyways.
So how to fix that, one way to fix this issue is to use toPromise() which will turn your observable into a promise which then can be awaited wherever needed.
The other way (which is the rxjs way) will be to return from the signup function the observable and inside your handler function to subscribe for the response.
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
exports.handler = (event, context) => {
//poolData and params will fetch from event
signup(poolData, params).subscribe(subscriber);
})
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.
I want to refactor code for chain of promises by async, await. I have sequelize ORM for DB management and the code is written in AWS Lambda function having multiple middleware. In such cases I have to traverse code for multiple entries using sequelize transactions. It is easy to manage using promise.all() but need to change it to async await syntax for cleaner code.
Here are my demo code.
/* get all invoice where user_id === current logged in user, and where status != "paid" */
db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: "paid"
}
}
}).then(invoices => {
if (!invoices || invoices === null) {
return false;
}
function addScheduledTransactionAttempts(invoice, tryPayOnDate, t) {
return new Promise((resolve, reject) => {
/* check If any ScheduledTransactionAttempts exists for this invoice.id */
db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id
}
})
.then(function(attempts) {
if (attempts) {
attempts
.destroy({}, {
transaction: t
})
.then(deletedAttempts => {
console.log("Attempts Record Deleted: ", deletedAttempts);
})
.catch(error => {
reject(error);
t.rollback();
});
}
return db.ScheduledTransactionAttempts.create({
invoice_id: invoice.id,
payment_source_id: PaymentMethodId,
try_pay_on_date: tryPayOnDate,
stripe_customer_id: currentLogInStripeCustomerId
}, {
transaction: t
})
.then(function(attempt) {
resolve(attempt.id);
})
.catch(error => {
reject(error);
t.rollback();
});
})
.catch(error => {
reject(error);
t.rollback();
});
});
}
//Run transaction to addScheduledTransactionAttempts
return db.sequelize.transaction().then(function(t) {
let promiseArr = [];
var i = 0;
invoices.forEach(function(invoice) {
var schedulePaymentDate = moment(paymentDate);
if (invoice) {
let tryPayOnDate = schedulePaymentDate
.add(i, "month")
.format("YYYY-MM-DD");
promiseArr.push(
addScheduledTransactionAttempts(invoice, tryPayOnDate, t) //calling above function
);
i++;
}
});
//now execute promise all
Promise.all(promiseArr)
.then(function(result) {
t.commit();
return true;
})
.catch(function(err) {
t.rollback();
return false;
});
});
});
In the above code I want to change
Promise.all(promiseArr)
which is calling
addScheduledTransactionAttempts
function to do DB queries to simple async function await process to make it easy simpler understandable without having multiple .then or .then inside then promises.
Any help regarding would be appreciated,
Thanks.
It's quite simple. await is valid when invoking methods that return a Promise. All of your SDK methods already return a promise, so refactoring should be quite straight forward. Here's something to get you off ground:
const processInvoices = async currentLoggedInUser {
const invoices = await db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: 'paid',
},
},
});
if (yourOwnLogicForInvoicesObject) {
for (const invoice of invoices) {
const potentiallyFoundInvoice = await db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id,
},
});
if (potentiallyFoundInvoice) {
await addScheduledTransactionAttempts(potentiallyFoundInvoice)
}
}
}
}
const addScheduledTransactionAttempts = async invoice => {
console.log('Do something with your invoice', invoice)
}
Long story short: refactor the code inside your functions into smaller functions and make these new functions async, just like I did with addScheduledTransactionAttempts and processInvoices
More on async/await
I am having difficulties getting and moving a response from the following functions in a chain.
The values exist further up but however I try to pass them down, the output always ends up empty.
I'm new to async/await in nodejs, but assumed they didn't need to be resolved like or with a promise.
let output = {
keywords: [],
steps: [],
twitter: []
}
async function requestGoogle (output) {
const GoogleResponse = await (
new Promise((resolve, reject) => {
const loc = utilities.checkOrigin()
googleMaps.directions({
origin: loc.origin,
destination: loc.destination
}, (error, response) => {
if (error) resolve(error)
resolve({
response
})
})
})
)
return GoogleResponse
}
async function requestTwitter (output) {
try {
const loc = utilities.checkOrigin()
const twitterParams = {
count: details.twitter.count,
geocode: loc.lat + loc.long + details.radius,
lang: details.lang,
result_type: 'recent'
}
await output.keywords.map(keyword => {
TwitterClient.get('search/tweets', {q: 'query'}, (error, tweets, response) => {
if (error) console.log(error)
tweets.statuses.map(tweet => {
output['twitter'].push({
text: tweet.text
})
})
})
})
return output
} catch (error) {
console.error('++ requestTwitter flow', error)
}
}
flow.commuteCheck = () => {
return requestGoogle(output)
.then(requestTwitter(output))
.then(console.log(output))
}
The response is unmodified and empty:
{ keywords: [], steps: [], twitter: [] }
full file on Github
.then(requestTwitter(output)) calls requestTwitter(output) and passes its return value (a promise) into then. But then expects a callback, not a promise. Similarly with .then(console.log(output)).
You probably meant to wrap those in functions:
flow.commuteCheck = () => {
return requestGoogle(output)
.then(() => requestTwitter(output))
// ---^^^^^^
.then(() => console.log(output))
// ---^^^^^^
}
FWIW, if requestGoogle and requestTwitter don't have to run in series, you can overlap them so the requests run concurrently:
flow.commuteCheck = () => {
return Promise.all([
requestGoogle(output),
requestTwitter(output)
]).then(() => console.log(output));
}
It's also probably worth noting that in both cases, the promise resolves with no useful value for the caller. You might have it resolve with output. To provent crosstalk between requests, you could also make output not be a module global that gets reused, but instead an object you create for each request:
flow.commuteCheck = () => {
const output = output = {
keywords: [],
steps: [],
twitter: []
};
return Promise.all([
requestGoogle(output),
requestTwitter(output)
]).then(() => {
console.log(output);
return output;
});
}