Dialogflow webhook fulfillment parameter is not working in post API - node.js

I make a code in webhook were i want invoke POST API and i want to invoke that api for that i have to pass some parameter but whenever i am trying to pass parameter coming from dialogflow its gives error. My code is like that
//Self Hosted Express Server
const bodyParser = require('body-parser')
var request = require('request-promise-native');
const { dialogflow } = require('actions-on-google');
const assistant = dialogflow({
clientId: "305xxxxxx7-rv9kocdq2xxxxouuq8f9ul2eg.apps.googleusercontent.com"
});
module.exports = (app) => {
const logger = console;
assistant.intent('Sales',(conv, params) => {
var pcode = params.myproduct;
// console.log(pcode)
const token = '3369708919812376';
const serviceID = '502';
const P_STATE_CD = 'ALL';
const P_FO_CD = 'ALL';
const P_DISTT_CD = 'ALL';
const P_DATE = '16/12/2019';
const P_PRD_GROUP = pcode;
const P_PERSONAL_NO = '106296';
var data = {"token" : token,"serviceID" : serviceID,"P_STATE_CD" : P_STATE_CD,"P_FO_CD" : P_FO_CD,"P_DISTT_CD" : P_DISTT_CD,"P_DATE" : P_DATE,"P_PRD_GROUP" : P_PRD_GROUP ,"P_PERSONAL_NO" : P_PERSONAL_NO };
var sdata = JSON.stringify(data);
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: JSON.parse(sdata) ,
json: true
}
return request(options)
.then( body => {
var unit = body
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
unit.value2 = unit.saleInfo[0].QYTD
unit.value3 = unit.saleInfo[0].O_UOM
unit.value4 = null
unit.value5 = null
delete unit.saleInfo
var unit2 = JSON.stringify(unit)
console.log(unit2)
conv.ask(unit2);
})
.catch( err => {
console.error( err );
conv.ask('Something went wrong. What should I do now?');
});
})
And the error like this
TypeError: Cannot read property '0' of undefined
at request.then.body (/home/dbalounge/GoogleDF/service.js:40:44)
at process._tickCallback (internal/process/next_tick.js:68:7)
Please help me out this. Thank You in Advance

Apparently body is coming as a string, probably because the server is not setting the correct Content-Type to the response, and request is ignoring json: true option. So you should use JSON.parse on it, and then access the saleInfo
return request(options)
.then( body => {
var unit = JSON.parse(body)
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
/* ... */
});
Aside from that body: JSON.parse(sdata) in your call is not needed, you're stringifying data to sdata to parse it back again, just use data directly:
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: data,
json: true
}

Related

Azure Machine Learning REST Endpoint - Failed to Fetch

I created an Azure Machine Learning model with a REST Endpoint as a way to consume it. When I run the service using Postman everything seems to work fine.
However, when I try to create an HTML website (Codepen) with a javascript to call the REST Endpoint I only get an Error: Failed to Fetch message.
I also tried with Azure Static Web Apps and I am unsuccessful as well.
I was however able to verify (in the Console) that my input to the Rest Endpoint via Codepen is the same as Postman.
Is there anything I am missing out here?
Here is a sample of my javascript:
<script>
const form = document.querySelector('#agriculture-form');
form.addEventListener('submit', (event) => {
event.preventDefault();
const areaHarvest = parseFloat(document.querySelector('#area-harvest').value);
const farmGatePrice = parseFloat(document.querySelector('#farm-gate-price').value);
const volumeOfImport = parseFloat(document.querySelector('#volume-of-import').value);
const lowTemp = parseFloat(document.querySelector('#low-temp').value);
const averageTemp = parseFloat(document.querySelector('#average-temp').value);
const highTemp = parseFloat(document.querySelector('#high-temp').value);
const precipitationMm = parseFloat(document.querySelector('#precipitation-mm').value);
const precipitationDays = parseFloat(document.querySelector('#precipitation-days').value);
const tropicalCyclones = parseFloat(document.querySelector('#tropical-cyclones').value);
const volumeProductionGuess = 0;
const data = {
"Area_Harvested": areaHarvest,
"FarmGatePricePHPPSA": farmGatePrice,
"Volume_of_Import": volumeOfImport,
"temp_low": lowTemp,
"temp_ave": averageTemp,
"temp_high": highTemp,
"precipitation_mm": precipitationMm,
"precipitation_days": precipitationDays,
"tropical_cyclone": tropicalCyclones,
"Volume_of_Production": volumeProductionGuess
};
const formattedData = [data];
console.log('formatted data:', formattedData);
const testData = JSON.stringify(formattedData);
console.log('test data:', testData);
document.getElementById("demo").innerHTML = testData;
fetch('http://ziggyapimanagementservice.azure-api.net/score', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': 'cd529cc993494fdfb1530eaf04ae63dc'
},
body: testData
})
.then(response => response.json())
.then(data => {
console.log(data);
const result = data.result[0]; // Get the result array from the response
const volumeForecastElement = document.querySelector('#volume-forecast');
volumeForecastElement.textContent = result.join(', '); // Update the text content of the <b> element with the result array joined by commas
document.getElementById("result").innerHTML = result;
})
.catch(error => {
document.getElementById("error").innerHTML = error.message;
console.error(error.message)
});
});
And here is what I get in Postman:

Cannot GET /[object%20Object] when calling axios.get()

When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...
You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.
The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.

Dialogflow webhook fulfillment parameter not accessible

I want to get input of a parameter in my webhook fulfillment.
Here is the my code:
const bodyParser = require('body-parser')
var request = require('request-promise-native');
const { dialogflow } = require('actions-on-google');
const assistant = dialogflow({
clientId: "30xxxxx08407-rv9kxxxxxxxxuuq8f9ul2eg.apps.googleusercontent.com"
});
module.exports = (app) => {
const logger = console;
assistant.intent('Sales', conv => {
const pcode = agent.parameters['PCODE'];
console.log(pcode)
const token = '3369708919812376';
const serviceID = '502';
const P_STATE_CD = 'ALL';
const P_FO_CD = 'ALL';
const P_DISTT_CD = 'ALL';
const P_DATE = '16/12/2019';
const P_PRD_GROUP = 'UREA';
const P_PERSONAL_NO = '106296';
var data = {"token" : token,"serviceID" : serviceID,"P_STATE_CD" : P_STATE_CD,"P_FO_CD" : P_FO_CD,"P_DISTT_CD" : P_DISTT_CD,"P_DATE" : P_DATE,"P_PRD_GROUP" : P_PRD_GROUP,"P_PERSONAL_NO" : P_PERSONAL_NO };
var sdata = JSON.stringify(data);
const options = {
method: 'POST',
uri: 'http://Webservice/resources/webservice/service' ,
body: JSON.parse(sdata) ,
json: true
}
return request(options)
.then( body => {
var unit = body
console.log(body)
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
unit.value2 = unit.saleInfo[0].QYTD
unit.value3 = unit.saleInfo[0].O_UOM
unit.value4 = null
unit.value5 = null
delete unit.saleInfo
var unit2 = JSON.stringify(unit)
console.log(unit2)
conv.ask(unit2);
})
.catch( err => {
console.error( err );
conv.ask('Something went wrong. What should I do now?');
});
})
I tried with const pcode = agent.parameters.PCODE but it is not working. Giving me error:
ReferenceError: agent is not defined
at assistant.intent.conv (/home/dbalounge/GoogleDF/service.js:15:16)
at Function. (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:151:27)
at Generator.next ()
at /home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:22:71
at new Promise ()
at __awaiter (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:18:12)
at Function.handler (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:85:16)
at Object. (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/assistant.js:55:32)
at Generator.next ()
at /home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/assistant.js:22:71
agent is not defined anywhere in your code, that's why you're getting:
ReferenceError: agent is not defined
In any case if you use assistant.parameters won't work either. Dialogflow intent parameters can be accessed through the second argument of .intent callback.
assistant.intent('Sales', (conv, params) => {
const pcode = params.PCODE;
/* ... */
})
For more info you can check the docs

Returning result of request to another module

I would like to return some of the body from a request made in request.js to the module that calls it in app.js
I have tried using module.exports to save information in the body. I know that the request is asynchronous so I have to wait for it to come back but I am not sure how to do this.
My app.js file:
const yargs = require('yargs');
const fs = require('fs');
const argv = yargs.
options({
capcity: {
description: 'capital city',
alias: 'c',
demand: true
}
})
.help()
.argv;
module.exports.capcity = argv.capcity;
const requestData = require('./requestData.js');
console.log(requestData.country);
My requestData.js file:
const request = require('request');
const app = require('./app.js');
var country;
request({
url: `https://restcountries.eu/rest/v2/capital/${app.capcity}`,
json: true
}
, (error, response, body) => {
console.log(app.capcity);
if (error) {
console.log('error:', error);
} else if (body.status == 404) {
console.log(JSON.stringify(body, undefined, 2));
console.log("invalid city entered");
} else {
country = {
name: body[0].name,
code: body[0].currencies[0].code,
symbol: body[0].currencies[0].symbol
}
country = JSON.stringify(country);
console.log(country);
}
});
module.exports.country = country;
It returns that country is undefined.
You've got some race conditions going on here, You many want to some research on callbacks, or better yet switch to using promises. Also you may want to export functions instead of variables, your code will be more reusable that way.
Heres an example using promises. note I'm using request-promise-native as suggested in the request documentation. you would need to install that dependency also to use this.
In your app.js
const yargs = require('yargs');
const fs = require('fs');
const argv = yargs.
options({
capcity: {
description: 'capital city',
alias: 'c',
demand: true
}
})
.help()
.argv;
const capcity = argv.capcity;
const requestDataFile = require('./requestData.js');
requestDataFile.requestData(capcity).then(country=>{
console.log(country)
})
In your requestData file
const request = require('request-promise-native');
const app = require('./app.js');
module.exports.requestData = (capcity)=>request({
url: `https://restcountries.eu/rest/v2/capital/${capcity}`,
json: true
}).then((body)=>{
return country = {
name: body[0].name,
code: body[0].currencies[0].code,
symbol: body[0].currencies[0].symbol
}
})

Request body is empty when posting form-data

I'm using a simple post request to my backend for a form data and for some reason the body is alwayes empty.
I'm trying to isolate this so i changed the content type to application json and changed the data to json and only this way i can send data.
Client side:
submitForm(event) {
event.preventDefault();
console.log("gggg");
const data = new FormData(event.target);
axios.post("http://localhost:4000/user-form-post",data).then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
Server side:
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
app.use(logger('dev'));
app.post('/user-form-post', (req,res) =>{
console.log("dfdf");
console.log(req.body); // alwayes print empty dict {}
res.end();
})
This is not working because it expects jsons(expected behavior):
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
Same behavior with Postman.
You will need to parse your form data from express side. For this you will have to use multer or multiparty. Try something like this. refer the documentation as well
const multiparty = require('multiparty');
app.post('/user-form-post', (req,res) =>{
let form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
Object.keys(fields).forEach(function(name) {
console.log('got field named ' + name);
});
});
})
when it comes to my issue,
i have this front end
const form = new FormData();
form.email = this.email;
form.password = this.password;
console.log("onSubmit -> form", form);
axios.post("http://localhost:3000/register", form )
onSubmit -> form FormData {email: "admin#gmail.com", password: "123"}
but the req.body in backend is empty, and i figured it out that the form in axios.post still need 1 more bracket {} even it's a object. like this
axios.post("http://localhost:3000/register", { form })
After that backend got body like this
req.body = { form: { email: 'admin#gmail.com', password: '123' } }
A problem with request body when you post data is data type .
I have recently a problem with Postman .
You should post data with type x-www-form-urlencoded or raw->JSON to fix the problem.
Goodluck.
You are using:
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
Please, also use given below line code but first install multer and write the code in top of your application:
var multer = require('multer');
var upload = multer();
app.use(express.json());
Faced the same issue , spent 2 days . Here are the solutions i found :
my request payload had JSON.stringify() , it will make body as {} empty object . when i removed JSON.stringify() and sent request it worked .
Content type should be multipart-form :boundary -----
Now if i externally set it to multipart-form , boundary thing was missing.
for few people it worked when you set content-type as false / undefined , boundary thing got added up,but not for me .
Even though i followed all steps and sending FormData as payload, payload was request payload object in network tab and was not FormData object , my request failed with 500 .
i tried the below code , its react + typescript (make necessary changes to avoid syntax errors)
import QueryString from 'qs';
import { ApiParams } from './xyzfile';
import { ApiHandlerRawType } from './types/xyzfile';
const setDefaultOptions = ({
method = '',
url = '',
params = {},
data = {},
signal = null,
headers = new Headers(),
...options
} = {}) => ({
method,
url,
params,
signal,
headers,
data,
...options
});
const setData = ({ method, data, ...options }: ApiHandlerRawType) => {
const option = options;
if (method !== 'GET' && option.isStreamData) {
option.body = data;
}
return {
method,
...option
};
};
const addRequestHeaders = ({ headers = new Headers(), ...options }) => {
const { existingHeaders }: ApiHandlerRawType = options;
if (existingHeaders) {
Object.entries(existingHeaders).forEach(([key, value]) => {
if (key !== 'Content-Type') headers.set(key, value);
});
}
return {
headers,
...options
};
};
export const ApiHandlerRaw = ({
url,
...originalOptions
}: ApiHandlerRawType): Promise<Response> => {
const options = setData(
addRequestHeaders(setDefaultOptions(originalOptions))
);
return fetch(url || '', options)
.then(response => {
if (!response.ok) throw new Error(response.statusText);
return Promise.resolve(response);
})
.catch(err => Promise.reject(err));
};
export const FileUploadApiHandler = async ({
headers,
...options
}: ApiHandlerRawType): Promise<Response | Blob> => {
const response = await ApiHandlerRaw({
headers,
isStreamData: true,
...options
});
return response;
};
export const fileApiService = ({
url,
method,
qsObject,
headers,
reqObjectAsStreamData
}: ApiParams): Promise<Response> => {
const qs = QueryString.stringify(qsObject, { addQueryPrefix: true });
const urlPath = `${url}${qs}`;
const data = reqObjectAsStreamData;
const existingHeaders = headers;
return FileUploadApiHandler({
url: urlPath,
method,
data,
existingHeaders
}) as Promise<Response>;
};
send the required variables from fileApiService . existingHeaders would be your app headers , eg : token / ids ... etc . data in fileApiService is the body .
I have also faced the same issue in the published code.
But I have fixed this issue by using the below code highlighted in the attached image :-
enter image description here
There is no use of "Content-Type" to fix this issue.
Hope you fix your issue by using the above code snippets.

Resources