i am struggling a bit with my google assistant action. Right now i am using Dialogflow and Firebase for my webhook. In my code i would like to get data from an API, for example this one: API. I am coding with Node.js by the way. Since Node is asynchronous i do not know how to get the Data. When i try to make an Callback it doesnt work e.g.:
app.intent(GetData, (conv) => {
var test= "error";
apicaller.callApi(answer =>
{
test = answer.people[0].name
go()
})
function go ()
{
conv.ask(`No matter what people tell you, words and ideas change the world ${test}`)
}
For some reason this works when i test it in an other application. With Dialogflow it does not work
I have also tried to use asynch for the function app.intent and tried it with await but this did not work too.
Do you have any idea how i could fix this?
Thank you in advance and best regards
Luca
You need to return Promise like
function dialogflowHanlderWithRequest(agent) {
return new Promise((resolve, reject) => {
request.get(options, (error, response, body) => {
JSON.parse(body)
// processing code
agent.add(...)
resolve();
});
});
};
See following for details:
Dialogflow NodeJs Fulfillment V2 - webhook method call ends before completing callback
If this works in another application then I believe you're getting an error because you're attempting to access an external resource while using Firebases free Spark plan, which limits you to Google services only. You will need to upgrade to the pay as you go Blaze plan to perform Outbound networking tasks.
Due to asynchronicity, the function go() is gonna be called after the callback of callapi been executed.
Although you said that you have tried to use async, I suggest the following changes that are more likely to work in your scenario:
app.intent(GetData, async (conv) => {
var test= "error";
apicaller.callApi(async answer =>
{
test = answer.people[0].name
await go()
})
async function go ()
{
conv.ask(`No matter what people tell you, words and ideas change the world ${test}`)
}
First follow the procedure given in their Github repository
https://github.com/googleapis/nodejs-dialogflow
Here you can find a working module already given in the README file.
You have to make keyFilename object to store in SessionsClient object creation (go at the end of post to know how to genearate credentials file that is keyFileName). Below the working module.
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//////////////////////////////////////////////////////////////////
const dialogflow = require("dialogflow");
const uuid = require("uuid");
/**
* Send a query to the dialogflow agent, and return the query result.
* #param {string} projectId The project to be used
*/
async function runSample(projectId = "<your project Id>") {
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient({
keyFilename: "<The_path_for_your_credentials_file>"
});
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: "new page",
// The language used by the client (en-US)
languageCode: "en-US"
}
}
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
console.log("Detected intent");
console.log(responses);
const result = responses[0].queryResult;
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
}
runSample();
Here you can obtain the keyFileName file that is the credentials file using google cloud platform using
https://cloud.google.com/docs/authentication/getting-started
For complete tutorial (Hindi language) watch youtube video:
https://www.youtube.com/watch?v=aAtISTrb9n4&list=LL8ZkoggMm9re6KMO1IhXfkQ
Related
I am trying to implement this code in my Node/Express server.
https://www.npmjs.com/package/#google-cloud/language?activeTab=readme
async function quickstart() {
// Imports the Google Cloud client library
const language = require('#google-cloud/language');
// Instantiates a client
const client = new language.LanguageServiceClient();
// The text to analyze
const text = 'Hello, world!';
const document = {
content: text,
type: 'PLAIN_TEXT',
};
// Detects the sentiment of the text
const [result] = await client.analyzeSentiment({document: document});
const sentiment = result.documentSentiment;
console.log(`Text: ${text}`);
console.log(`Sentiment score: ${sentiment.score}`);
console.log(`Sentiment magnitude: ${sentiment.magnitude}`);
}
The above code I guess cannot be on the frontend react side. So I tried to use it in my app.js file reference it from a GET request, but the server won't compile because it says require doesn't exits.
app.get('/sentiment', async (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
console.log('sentiment was called'.green.inverse);
var results = quickstart(req.params);
res.send(results);
});
Not sure how to resolve require or if even implementing this quickstart correctly?
I built an app in Slack, that on interactions in Slack, will send an HTTP POST request to a URL. That URL is a Firebase Function that is triggered with an HTTP request.
The Firebase Function looks like this...
// process incoming shortcuts
exports.interactions = functions.https.onRequest(async (request, response) => {
response.send();
const payload = JSON.parse(request.body.payload);
functions.logger.log(payload);
if (payload.type === 'shortcut') {
functions.logger.log('Found a shortcut...');
const shortcuts = require('./shortcuts');
await shortcuts(payload);
} else if (payload.type === 'block_actions') {
functions.logger.log('Found a block action...');
const blockActions = require('./blockActions');
await blockActions(payload);
} else if (payload.type === 'view_submission') {
functions.logger.log('Found a view submission...');
const viewSubmissions = require('./viewSubmissions');
await viewSubmissions(payload);
}
functions.logger.log('Done with interactions.');
});
The problem is, is that Firebase is taking 5-10 seconds to respond, and Slack is expecting a response in 3 seconds.
So the app in Slack erroring out.
It turns out while I thought it would be useful to do a response.send() immediately when the function was called so that Slack had its instant response, I was then also inadvertently starting background activities in Firebase.
The line in the above Firebase docs that gave me the biggest clue was:
Background activity can often be detected in logs from individual invocations, by finding anything that is logged after the line saying that the invocation finished.
Which I found here... the function started, and completed, and then the code to open a modal began to be executed...
I then found in the Firebase docs
Terminate HTTP functions with res.redirect(), res.send(), or res.end().
So all I really had to do was move response.send() to the end of the function. Also I had to make sure that I had await statements before my async functions, so that async functions waited to be resolved before executing the final response.send()
// process incoming shortcuts
exports.interactions = functions.https.onRequest(async (request, response) => {
const payload = JSON.parse(request.body.payload);
functions.logger.log(payload);
if (payload.type === 'shortcut') {
functions.logger.log('Found a shortcut...');
const shortcuts = require('./shortcuts');
await shortcuts(payload);
} else if (payload.type === 'block_actions') {
functions.logger.log('Found a block action...');
const blockActions = require('./blockActions');
await blockActions(payload);
} else if (payload.type === 'view_submission') {
functions.logger.log('Found a view submission...');
const viewSubmissions = require('./viewSubmissions');
await viewSubmissions(payload);
}
functions.logger.log('Done with interactions.');
response.send();
});
The modal interaction response times in Slack are much quicker and usable now.
I'm creating an API in Azure Functions using TypeScript, with multiple endpoints connecting to the same Azure SQL Server. Each endpoint was set up using the Azure Functions extension for VS Code, with the HttpTrigger TypeScript template. Each endpoint will eventually make different calls to the database, collecting from, processing and storing data to different tables.
There don't seem to be any default bindings for Azure SQL (only Storage or Cosmos), and while tedious is used in some Microsoft documentation, it tends not to cover Azure Functions, which appears to be running asynchronously. What's more, other similar StackOverflow questions tend to be for standard JavaScript, and use module.exports = async function (context) syntax, rather than the const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> syntax used by the TypeScript HttpTrigger templates.
Here's what I've got so far in one of these endpoints, with sample code from the tedious documentation in the default Azure Functions HttpTrigger:
var Connection = require('tedious').Connection;
var config = {
server: process.env.AZURE_DB_SERVER,
options: {},
authentication: {
type: "default",
options: {
userName: process.env.AZURE_DB_USER,
password: process.env.AZURE_DB_PASSWORD,
}
}
};
import { AzureFunction, Context, HttpRequest } from "#azure/functions"
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
context.log('HTTP trigger function processed a request.');
const name = (req.query.name || (req.body && req.body.name));
if (name) {
var connection = new Connection(config);
connection.on('connect', function(err) {
if(err) {
console.log('Error: ', err)
}
context.log('Connected to database');
});
context.res = {
// status: 200, /* Defaults to 200 */
body: "Hello " + (req.query.name || req.body.name)
};
}
else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
};
export default httpTrigger;
This ends up with the following message:
Warning: Unexpected call to 'log' on the context object after function
execution has completed. Please check for asynchronous calls that are
not awaited or calls to 'done' made before function execution
completes. Function name: HttpTrigger1. Invocation Id:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Learn more:
https://go.microsoft.com/fwlink/?linkid=2097909
Again, the async documentation linked to covers just the standard JavaScript module.exports = async function (context) syntax, rather than the syntax used by these TypeScript httpTriggers.
I've also been reading that best practice might be to have a single connection, rather than connecting anew each time these endpoints are called - but again unsure if this should be done in a separate function that all of the endpoints call. Any help would be much appreciated!
I'm glad that using the 'mssql' node package works for you:
const sql = require('mssql');
require('dotenv').config();
module.exports = async function (context, req) {
try {
await sql.connect(process.env.AZURE_SQL_CONNECTIONSTRING);
const result = await sql.query`select Id, Username from Users`;
context.res.status(200).send(result);
} catch (error) {
context.log('error occurred ', error);
context.res.status(500).send(error);
}
};
Ref:https://stackoverflow.com/questions/62233383/understanding-js-callbacks-w-azure-functions-tedious-sql
Hope this helps.
I'm trying to do some integration tests for my api in express.
My API's structure is something like:
app -> routes -> controllers -> services
Because I already have unit tests, my idea is only test that all that components are connected in the correct way.
So my idea was created an stub with Sinon for the service, and only check the responses of the controller with supertest.
When I run a single test everything is ok. The problem is when I run more than one unit test for different controllers, the stub doesn't work in the second run.
I think it's because the app is already saved in cache as a module, so sinon can't stub the service.
Some examples of my code:
controller.js
const httpStatus = require('http-status');
const { service } = require('../services/croupier');
/**
* Execute lambda tasks for candidates
* #public
*/
exports.task = async (req, res, next) => {
try {
const result = await service({
body: req.body,
authorizer: req.authorizer
});
console.log('res', result);
res.status(httpStatus.OK).json(result);
} catch (error) {
next(error);
}
};
foo.integration.test.js
const request = require('supertest');
const httpStatus = require('http-status');
const sinon = require('sinon');
const mongoose = require('../../../database');
const deleteModule = module => delete require.cache[require.resolve(module)];
const requireUncached = module => {
deleteModule(module);
return require(module);
};
describe('Foo - Integration Test', async () => {
describe('POST /v1/foo', () => {
const fooService = require('../../services/foo');
const stub = sinon.stub(fooService, 'service');
let db;
before(async () => {
db = await mongoose.connect();
});
afterEach(async () => {
sinon.restore();
});
after(async () => {
await db.close();
});
it('the api should response successfully', async () => {
stub.returns({});
const payload = { task: 'task', payload: [{ pathParameters: {}, body: {} }] };
const app = requireUncached('../../../app');
await request(app)
.post('/api/foo')
.send(payload)
.expect(httpStatus.OK);
});
it('the api should response with an error', async () => {
stub.throwsException();
const payload = { task: 'task', payload: [{ pathParameters: {}, body: {} }] };
const app = requireUncached('../../../app');
await request(app)
.post('/api/foo')
.send(payload)
.expect(httpStatus.INTERNAL_SERVER_ERROR);
});
});
});
The other integration tests have the same structure. I've also tried using proxyquire but didn't work.
Also I tried deleting cache of de app.js with any success.
Any ideas?
Context: integration test.
I agree with your idea: "test that all that components are connected in the correct way". Then what you need is spy, not stub. When there is a case / condition, you need to setup preconfigured/dummy data (up mongodb with specific data), turn on HTTP server, call HTTP request with specific data (post / get with specific query), and check the HTTP response for correct status, etc. The spy needed to check/validate/verify whether your service get called with correct parameter and response with correct result. This test validate you have correctly configured route - controller to a service for specific HTTP request.
You must have question: How to test negative scenario? For example: 404, 500. Then you need to know which specific scenario do what, which result negative condition. For example: if request come with unknown ID query, then response will be 404. Or if express not connected to database, then response will be 500. You need to know the real scenario, and again provide the require setup to produce the negative response.
For problem: "When I run a single test everything is ok. The problem is when I run more than one unit test for different controllers, the stub doesn't work in the second run.". There are several possible solutions, the main point is: you must make sure that the conditions for specific scenario/case are correctly prepared.
You can do:
create sandbox, to make sure no other stub service run between test cases.
start up fresh http (and or db) server before and shut down the server after the test run for each services, (for example: start the app and use real http client - as alternative to supertest)
run on debug mode to find out why the second stub not run or not get called or not work,
change implementation from stub to spy, you have already had a unit test, you just need to check whether the service get called or not, and then check the overall response.
Hope this helps.
I'm trying to write some tests using Lab and Sinon for various HTTP requests that are called in a file of mine. I followed the Fake XMLHttpRequest example at http://sinonjs.org/ but when I run my tests it appears to not actually capture any requests.
Here is the (relevant) testing code:
context('when provided a valid payload', function () {
let xhr;
let requests;
before(function (done) {
xhr = sinon.useFakeXMLHttpRequest();
requests = [];
xhr.onCreate = function (req) { requests.push(req); };
done();
});
after(function (done) {
// clean up globals
xhr.restore();
done();
});
it('responds with the ticket id', (done) => {
create(internals.validOptions, sinon.spy());
console.log(requests); // Logs empty array []
done();
});
});
create is the function I imported from the other file, here:
internals.create = async function (request, reply) {
const engineeringTicket = request.payload.type === 'engineering';
const urgentTicket = request.payload.urgency === 'urgent';
if (validation.isValid(request.payload)) {
const attachmentPaths = formatUploads(request.payload.attachments);
const ticketData = await getTicket(request.payload, attachmentPaths);
if (engineeringTicket) {
const issueData = getIssue(request.payload);
const response = await jira.createIssue(issueData);
jira.addAttachment(response.id, attachmentPaths);
if (urgentTicket) {
const message = slack.getMessage(response);
slack.postToSlack(message);
}
}
zendesk.submitTicket(ticketData, function (error, statusCode, result) {
if (!error) {
reply(result).code(statusCode);
} else {
console.log(error);
}
});
} else {
reply({ errors: validation.errors }).code(400); // wrap in Boom
}
};
as you can see it calls jira.createIssue and zendesk.submitTicket, both of which use an HTTP request to post some payload to an API. However, after running the test, the requests variable is still empty and seems to have captured no requests. It is definitely not actually submitting the requests as no tickets/issues have been created, what do I need to fix to actually capture the requests?
Your problem is apparent from the tags: you are running the code in NodeJS, but the networking stubs in Sinon is for XMLHttpRequest, which is a browser specific API. It does not exist in Node, and as such, the setup will never work.
That means if this should have worked you would have needed to run the tests in a browser. The Karma test runner can help you with this if you need to automate it.
To make this work in Node you can either go for an approach where you try to stub out at a higher level - meaning stubbing the methods of zendesk and jira, or you can continue with the approach of stubbing network responses (which makes the tests a bit more brittle).
To continue stubbing out HTTP calls, you can do this in Node using Nock. Saving the requests like you did above is done like this:
var requests = [];
var scope = nock('http://www.google.com')
.get('/cat-poems')
.reply(function(uri, requestBody) {
requests.push( {uri, requestBody} );
});
To get some insights on how you can stub out at a higher level, I wrote this answer on using dependency injection and Sinon, while this article by Morgan Roderick gives an intro to link seams.