I am playing around with dialogflow action. I have one intent that asks for three questions and then I do REST call to my low-code platform with the responses from the users.
Everything looks fine and I get the result back nice and tidy to my own platform.
But I want to inform the user that I have accepted the call and created the object. Therefore in my REST response I have a QuoteName and a QuoteNr that I would like to send to the agent to display to the user as the end of conversation.
Before I do this I put this information into the console.log() and I can see that it is printed in the log.
Log from dialogflow
But my dialogflows response is "XXXXX can't respond right now, please try again later." even if I got the REST call and everything working as supposed???
What is going on here?
Here is my inline editor code:
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const axios = require('axios');
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function Auth() {
console.log("Getting the token");
axios.post('https://b4c5e131-eff9-44e0-a8dc-3764f17a1220.app.flowfactory.se/Auth/LoginAPI?username=api#test.se&password=APassword', {
params: {
}
})
.then((response) => {
let upload = indoor(response.data.AccessToken, agent);
})
.catch(function(error) {
console.log(error);
});
}
function indoor(token, agent){
console.log("Making the call againts indoor");
const config = {
headers: { Authorization: 'Bearer ' + token, }
};
const bodyParameters = {
Customer: agent.parameters.customer,
Value: agent.parameters.quotevalue,
Description: agent.parameters.description
};
axios
.post(
'https://b4c5e131-eff9-44e0-a8dc-3764f17a1220.app.flowfactory.se/externalapi/V1/QuickQuote/CreateQuickQuote',
bodyParameters,
config
).then((response) => {
console.log(response.data.QuoteName + " - " + response.data.QuoteNr);
let msgToUser = theResponseBack(response.data.QuoteName, response.data.QuoteNr, agent);
agent.add("Skapade ett utkast av en offert åt dig i system.");
})
.catch(function(error) {
console.log(error);
});
}
function theResponseBack(qName,qNr, agent) {
console.log("Sending message back to user");
agent.add("Skapade offert " + qName + " - " + qNr);
}
let intentMap = new Map();
intentMap.set('QuickQuote', Auth);
agent.handleRequest(intentMap);
});
Related
I am trying to implement a form based authentication. Authentication is based for the Users is based on Roles ADMIN and USER. When I run my custom login url .loginProcessingUrl("/admin/login") my authentication fails meaning
I get a HttpStatus OK
Anyone can sign in even if you are not register
but as soon as I comment out the .loginProcessingUrl("/admin/login") to use spring customs login page, it works.
I have looked at various examples but nothing to push me in the right direction. I don't know if it is because of I am not saving session ID in the User entity class (NOTE: I am not saving session ID yet cause I am trying to understand just the basic of form based authentication) or something is wrong with my JS.
NOTE: On start of this app, I am injecting dummy users with one having a Role ADMIN and the other two USERS
Here is my SecurityFilterChain in SecurityConfig Class
#Bean
public SecurityFilterChain filterChain1(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.cors() //
.and() //
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((auth) -> auth
.antMatchers("/admin/**", "/secured/**")
.hasRole(ADMIN.name())
)
.formLogin() //
.loginProcessingUrl("/admin/login")
.and()
// logout TODO
// .logout()
// .logoutUrl("/admin/logout")
// .and()
.build();
}
Admin Login Controller api (not it has a global request mapping #RequestMapping("/admin"))
#PostMapping(path = "/login")
public ResponseEntity<?> login(#Valid #RequestBody User user) {
System.out.println("Status " + userDTOService.confirmUser(user));
if (!userDTOService.confirmUser(user)) {
return new ResponseEntity<>(!userDTOService.confirmUser(user), BAD_REQUEST);
}
return new ResponseEntity<>(userDTOService.confirmUser(user), FOUND);
}
service class which confirms if the user exists
public Boolean confirmUser(User user) {
/*
* Check if username exist in the database
* then check if the password provided equals password in database
* Then check if user is an admin
* */
System.out.println(user);
String userName = user.getUserName();
String password = user.getPassword();
Optional<User> findUser = userRepository.findUserByUserName(userName);
return findUser
.stream()
.anyMatch(param ->
param.getPassword().equals(password)
&& param.getRole().equals(ADMIN)
);
}
vanilla js sign in
const signIn = () => {
formElement.addEventListener("submit", (event) => {
event.preventDefault();
const formD = new FormData(event.target);
fetch(LOGIN, {
method: "POST",
body: formD
}).then(async (response) => {
if (response.ok) {
// window.location.href = "../static/new.html";
console.log("Success");
return response.json();
}
const body = await response.json();
throw new Error(body.message);
})
.catch((error) => {
console.log(error);
});
})
}
Also for some weird reason, I get an this syntax error SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON coming from login the error console.log(error);
I figured the answer.
Using .formLogin() and .loginProcessingUrl() is wrong when using REST APIs. This is because these methods are used for Springs inbuilt frontend ThymeLeaf.
Using FormData is wrong because it is for uploading files so I instead created an object. Documentation for better explanation on FormData here.
Since I am using basic authentication, I'll need to pass user credentials in the header.
Client side Logic
const signIn = (LOGIN) => {
form.addEventListener("submit", (event) => {
event.preventDefault();
const formD = {
"username": name.value,
"password": password.value
}
let authorizationData = 'Basic ' + btoa(name.value + ':' + password.value);
fetch(LOGIN, {
method: "POST",
headers: {
'Content-Type': 'application/json',
"Authorization": authorizationData
},
body: JSON.stringify(formD)
}).then(async (response) => {
if (response.ok) {
window.localStorage.setItem("Authorization", authorizationData)
window.location.href = "../static/new.html";
return response.json();
}
const body = await response.json();
throw new Error(body.message);
})
.then ((data) => {
console.log("Data " + data);
})
.catch((err) => {
console.log("Error " + err.message);
});
})
}
Security filter
.cors() //
.and() //
.csrf(csrf -> csrf.ignoringAntMatchers("/h2-console/**").disable())
.authorizeHttpRequests((auth) -> auth
.antMatchers("/unsecured/**", "/admin/login", "/h2-console/**") //
.permitAll() //
.anyRequest().authenticated()
)
.sessionManagement(sessions -> sessions.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(headers -> headers.frameOptions().sameOrigin())
.httpBasic()
.and()
.build();
I am working on the Zoom API now. I want to send my token from ZOOM API to front-end as a response. However, "token from request" is always printed first and undefined! Then the token from Zoon API" will be followed with the token. How can I make it happen? Thx!
const Newrequest = require("request");
class ZoomController {
async getInvLink({ request, response }) {
const instructor_id = params.id;
try {
let tokenRes;
const code = request.all().code;
console.log("the code from frontend is ", code);
const client_id = _something_
const client_secret = _something_
var options = {
method: "POST",
url: "https://api.zoom.us/oauth/token",
qs: {
grant_type: "authorization_code",
code: code,
redirect_uri: _something_
},
headers: {
Authorization:
"Basic " +
Buffer.from(client_id + ":" + client_secret).toString("base64")
}
};
await Newrequest(options, function(error, res, body) {
if (error) throw new Error(error);
tokenRes = JSON.parse(body);
console.log("token from Zoon API",tokenRes);
});
console.log("token from request",tokenRes);
return response.status(200).send(tokenRes);
} catch (error) {
console.log(error);
return response.status(401).send();
}
I have no idea what this api is, but I'm going to make an educated guess that Newrequest doesn't return a promise. So awaiting it isn't actually what you want to do.
What you can do, however, is use some simple code to turn it into a promise:
const tokenRes = await new Promise((resolve, reject) => {
Newrequest(options, function(error, res, body) {
if (error) reject(error);
tokenRes = JSON.parse(body);
console.log("token from Zoon API",tokenRes);
resolve(tokenRes);
});
})
You would have to listen to an endpoint at something and you will receive the code over there. This is the code you can send to exchange for an access token.
Please consult this link : https://www.npmjs.com/package/request#promises--asyncawait
You can convert a regular function that takes a callback to return a promise instead with util.promisify()
Example :
Newrequest(options, function(error, res, body) {
if (error) throw new Error(error);
tokenRes = JSON.parse(body);
console.log("token from Zoon API",tokenRes);
});
// to
const util = require('util');
const req = util.promisify(Newrequest)
const data = await req(options)
// ...
It's a sample code. Please adapt with your needs
Useful course : https://masteringjs.io/tutorials/node/promisify
Request library is deprecated
It would be interesting to use another library.
Alternatives :
Got
Axios
Node-fetch
superagent
So I created a function that will handle Paypal Instant Payment Notification(IPN). All goes well in receiving the notification and I also got "VERIFIED" message when requesting back to Paypal for verification. The problem is that cloud function is not or stops executing inside verification function callback.
exports.ipnHandler = functions.https.onRequest((req, res) => {
console.log("IPN Notification Event Received");
if (req.method !== "POST") {
console.error("Request method not allowed.");
res.status(405).send("Method Not Allowed");
} else {
// Return empty 200 response to acknowledge IPN post success.
console.log("IPN Notification Event received successfully.");
// JSON object of the IPN message consisting of transaction details.
let ipnTransactionMessage = req.body;
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = querystring.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`Verifying IPN: ${verificationBody}`);
let options = {
method: "POST",
url: getPaypalURI(),
body: verificationBody,
};
requestPromise(options)
.then(body => {
// This will log "VERIFIED"
console.log(body); // Cloud function stops here
if (body === "VERIFIED") {
console.log("Manage user subscription");
const transactionType = ipnTransactionMessage.txn_type;
const invoice = ipnTransactionMessage.invoice;
const docRef = firestore.collection("users");
console.log("Transaction type: " + transactionType);
console.log("Invoice " + invoice);
if (transactionType === "subscr_payment") {
console.log("About to subscribe user: " + invoice);
return docRef.where("invoice", "==", invoice).get();
}
}
return console.log("Request completed");
})
.then(snapshots => {
return console.log("Return snapshots " + snapshots);
})
.catch(error => {
console.log(error);
})
res.status(200).end();
}
It starts misbehaving when I added this line return docRef.where("invoice", "==", invoice).get(); Supposedly it will return a snapshot data in the callback below.
.then(snapshots => {
return console.log("Return snapshots " + snapshots);
})
I have solved the problem. It seems that function reached timeout which is 60 seconds in default. I just changed timeout to 5 minutes.
I have a function running on the creation of a document.
When I send this information to an external API Firebase returns on 'ok' message before the API call is complete.
const functions = require('firebase-functions');
const request = require('request');
const admin = require('firebase-admin');
const rp = require('request-promise');
const port = '****';
const ip = '***.***.***.***';
admin.initializeApp(functions.config().firebase);
exports.sendUser = functions.firestore
.document('user/{userId}')
.onCreate((snap, context) => {
const data = snap.data();
const options = {
method: 'POST',
uri: 'http://' + ip + ':' + port + '/user',
body: data,
json: true,
};
rp(options)
.then(function (parsedBody) {
console.log('TEN ', parsedBody);
return parsedBody;
})
.catch(function (err) {
console.log('ERR ', err);
return err;
});
});
As you can see from my function it is not doing anything special apart from sending the data to an external source.
The API look like the following:-
app.post('/user', function (req, res) {
fs.exists(path, function(exists) {
if (exists === true) {
console.log('Currently Printing Different User Info');
fs.unlinkSync(path);
res.status(404).json({errorCode: 404, errorMessage: 'Currently Printing Different User.'});
return;
} else {
fs.writeFile(path, '', () => { console.log('File Created'); });
fs.unlinkSync(path);
res.status(200).json({statusCode: 200, statusMessage: 'Here we go'});
return;
}
});
})
How can I get Firebase to recognise the returned 404 as a failed call, and also wait until the call is complete before returning ok or failed.
The API is behaving correctly with Postman but not when data is posted via Firebase.
Has anyone encountered this before, or can anybody see what I am doing wrong?
The data is being parse over to the serve but only once Firebase has returned with 'ok' even if I purposely trigger a fail.
I need this in place to be able to use the Firebase Cloud Function retry function.
Images can be seen # https://imgur.com/a/1qYxrci
The Cloud Function returns the result before the call is complete because you don't return the Promise returned by the request-promise call.
Changing your code as follows should do the trick (at least for this problem):
exports.sendUser = functions.firestore
.document('user/{userId}')
.onCreate((snap, context) => {
const data = snap.data();
const options = {
method: 'POST',
uri: 'http://' + ip + ':' + port + '/user',
body: data,
json: true,
};
return rp(options) // <-- See the change here
.then(function (parsedBody) {
console.log('TEN ', parsedBody);
return parsedBody;
})
.catch(function (err) {
console.log('ERR ', err);
return err;
});
});
I would suggest you watch the official Video Series (https://firebase.google.com/docs/functions/video-series/) which explain very well this point about returning Promises for background functions (in particular the ones titled "Learn JavaScript Promises").
I'm making a bot that searches restaurants based on location. Can anyone help me why this doesnt show up in FB messenger?:
restaurants(result.getMemory('location').raw)
.then(res=>{
message.addReply(res);
message.reply();
});
}
The call to the restaurants function returns the results from a YELP API call (an array of restaurants) but when I add it as a reply to message, nothing happens in FB messenger.
Here is the full code for message.js:
const recastai = require('recastai');
const restaurants = require('./restaurants');
// This function is the core of the bot behaviour
const replyMessage = (message) => {
// Instantiate Recast.AI SDK, just for request service
const request = new recastai.request(process.env.REQUEST_TOKEN,
process.env.LANGUAGE);
// Get text from message received
const text = message.content;
console.log('I receive: ', text);
// Get senderId to catch unique conversation_token
const senderId = message.senderId;
// Call Recast.AI SDK, through /converse route
request.converseText(text, { conversationToken: senderId })
.then(result => {
//Recast takes text analyses that, returns a result object, generates replies adds messages to reply stack and then sends the replies
//Call Yelp API with when the intent is Location. When Yelp returns result we add it to the result.replies array.
//Then we add everything in result.replies to the messaging queue that sends the responses to FB
if (result.action) {
console.log('The conversation action is: ', result.action.slug);
}
// If there is not any message return by Recast.AI for this current conversation
if (!result.replies.length) {
message.addReply({ type: 'text', content: 'I don\'t have the reply to this yet :)' });
} else {
// Add each reply received from API to replies stack
result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }));
}
// Send all replies
message.reply()
//send initial reply generated by Recast first
.then(() => {
//call restaurant function that returns a list of results from API
//if the action is location and done
if(result.action && result.action.slug === 'location' && result.action.done){
restaurants(result.getMemory('location').raw)
.then(res=>{
console.log(res);
message.addReply(res);
message.reply();
});
}
})
.catch(err => {
console.error('Error while sending message to channel', err);
});
})
.catch(err => {
console.error('Error while sending message to Recast.AI', err);
});
};
module.exports = replyMessage;
And here is my restaurants.js code that is imported into the message.js file for the bot behavior:
const rp = require('request-promise');
// Load configuration
require('./config');
const restaurants = (location) => {
return Promise.all([
yelpCall(location)
]).then(result => {
//result contains the return value from Yelp call
return result;
});
};
const yelpCall = (location) => {
const auth = {
method: 'POST',
url: 'https://api.yelp.com/oauth2/token?grant_type=client_credentials&client_id='+ process.env.YELP_APP_ID +'&client_secret='+process.env.APP_SECRET
};
return rp(auth)
.then(result => {
const tokens = JSON.parse(result);
return tokens;
})
.then(result=>{
const options = {
url: 'https://api.yelp.com/v3/businesses/search?location=' + location + "&term=thai",
headers: {Authorization: "Bearer " + result.access_token}
};
return rp(options).then(findings =>{
return findings;
});
});
};
module.exports = restaurants;
A few thoughts :
message.reply is thenable, therefore return message.reply() in two places.
request.converseText() is thenable, therefore return request.converseText(...).
restaurants is thenable, therefore return restaurants(...).
in message.js, message.addReply() is passed object of the form {type:..., content:...} in two places but finally just res. Is that correct?
in restaurants.js, Promise.all() appears to be unnecessary. It will cause its result to be wrapped in an array. module.exports = location => yelpCall(location); seems more appropriate.