DirectLine response is not included in function response, execution completes too quickly - azure

I am trying to send a message and get a response using " directLine.activity$" -- although my function execution completes before the directLine.activity$ detects a "received activity".
Below is my entire code. I'm just wondering, how can I wait for the WebSocket to receive at least 1 "received activity" before processing the response.
When I run this code it just quickly executes and says "Executed 'Functions.twilioagogo' (Succeeded, Id=xxxxxx-c1a9-4092-b740-xxxxxx, Duration=272ms)"
const express = require('express');
const { MessagingResponse } = require('twilio').twiml;
const app = express();
//DirectLine
global.XMLHttpRequest = require('xhr2');
global.WebSocket = require('ws');
const { DirectLine } = require('botframework-directlinejs');
const { twiml } = require('twilio');
module.exports = async function (context, req) {
//global variables
var myPhone
var myActivity
var responseCount = 0
const twiml = new MessagingResponse();
var directLine = new DirectLine({
secret: 'xxxxxxxxxxxxxxxxxx',
});
async function myfunction() {
console.log('Inside of myfunction');
console.log("start")
const name = (req.query.name || (req.body && req.body.name));
// const responseMessage = name
// ? "Hello, " + name + ". This HTTP triggered function executed successfully."
// : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";
//new
// twiml.message('The Robots are coming! Head for the hills!');
//get from phone #
var myInfo = req.body.toString().split("&")
for (i = 0; i < myInfo.length; i++) {
if (myInfo[i].toString().includes("From=")) {
myPhone = myInfo[i].toString()
}
}
}
async function myfunction2() {
console.log('Inside of myfunction2');
console.log("start2")
directLine.postActivity({
from: { id: '123'}, // required (from.name is optional)
type: 'message',
text: 'a message for you, Rudy'
}).subscribe(
id => {console.log("Posted activity, assigned ID ", id)
directLine.activity$
.subscribe(
activity => {
if (activity.from.id === "tbot1000v5") {
console.log("received activity ", activity.text)
myActivity = activity.text
console.log("myactivity here " + myActivity)
responseCount++
console.log("ResponseCount is " + responseCount)
twiml.message(myActivity);
console.log("here")
} //end if
}
)
},
error => console.log("Error posting activity", error)
)
} //end myfunction2
async function myfunction3() {
console.log('Inside of myfunction3');
console.log("start3")
}
function start() {
return myfunction();
}
function start2() {
return myfunction2();
}
function start3() {
return myfunction3();
}
// Call start
(async() => {
console.log('before start');
await start();
await start2();
await start3();
console.log("send response begin")
context.res = {
// status: 200, /* Defaults to 200 */
// body: 'The Robots are coming! Head for the hills!'
headers: { 'Content-Type': 'application/xml' },
body: twiml.toString()
};
})();
}

Related

nodejs/express, Controller send the response before to get the result of function setInterval

the problem is :
In Controller, the response is send before the result of condition into function setInterval
Steps :
Controller run function setInterval
Then send the response to frontend with res.status.json
The function setInterval run
condition of setInterval work and finish
setInterval don't return the resultto controller
the controller have already sent a res...
import checkHealth from "./checkHealth.js";
async function checkHealthInterval(labelData) {
console.log("labelData==>", labelData);
let resultInterval = { health: "", message: "" };
let i = 1;
let interval;
interval = await setInterval(async () => {
let healthCheckers = await checkHealth();
let healthCheckerStatus;
if (labelData== "example1") {
healthCheckerStatus = healthCheckers.statusInstanceGlobal;
console.log(
"healthCheckerStatus value from tenant1 : ",
healthCheckerStatus
);
} else {
healthCheckerStatus = healthCheckers.statusWam1Wam2;
console.log(
"healthCheckerStatus value from tenant2 : ",
healthCheckerStatus
);
}
i++;
if (healthCheckerStatus !== true && i > 36) {
clearInterval(interval);
console.log("VMs are UNHEALTHY in the end of interval or before the end");
this.isProcessing = false;
throw new ApiError(
500,
"VMs are UNHEALTHY in the end of interval or before the end"
);
} else if (healthCheckerStatus == true) {
clearInterval(interval);
console.log(
`VMs ${labelData} are HEALTHY and successfully started!`
);
resultInterval.health = "healthy";
resultInterval.message = "VMs are successfully started!";
console.log("resultInterval into interval===>", resultInterval);
return resultInterval
}
}, 10000);
console.log("Script interval is running to check health");
return interval
}
export { checkHealthInterval };
getStartVms = async (req, res, next) => {
console.log("You asked to start VMs: starting VMs");
try {
this.isProcessing = true;
const responseStart = await this.instanceService.startInstances();
// use interval to checkHealth of Vms with import function checkHealth()
const resultInterval = await checkHealthInterval(responseStart?.labelData);
res.status(200).json({
health: "healthy",
message: "VMs are successfully started!",
});
this.isProcessing = false;
io.emit("processChanged", this.isProcessing);
} catch (err) {
this.isProcessing = false;
io.emit("processChanged", this.isProcessing);
next(err);
}
};

RefreshToken not present in tokenSET

I am trying to connect with the api hubstaff to which I have set up my authentication using auth0 and express as my backend. To know about the info about the logged in user I need to send the token object via the API.
By some research I have gotten to this point:
const {
Issuer,
TokenSet
} = require('openid-client');
const fs = require('fs');
const jose = require('jose');
// constants
const ISSUER_EXPIRE_DURATION = 7 * 24 * 60 * 60; // 1 week
const ACCESS_TOKEN_EXPIRATION_FUZZ = 30; // 30 seconds
const ISSUER_DISCOVERY_URL = 'https://account.hubstaff.com';
// API URl with trailing slash
const API_BASE_URL = 'https://api.hubstaff.com/';
let state = {
api_base_url: API_BASE_URL,
issuer_url: ISSUER_DISCOVERY_URL,
issuer: {}, // The issuer discovered configuration
issuer_expires_at: 0,
token: {},
};
let client;
function loadState() {
return fs.readFileSync('./configState.json', 'utf8');
}
function saveState() {
fs.writeFileSync('./configState.json', JSON.stringify(state, null, 2), 'utf8');
console.log('State saved');
}
function unixTimeNow() {
return Date.now() / 1000;
}
async function checkToken() {
//console.log('state.token.access_token', state.token.access_token);
if (!state.token.access_token || state.token.expires_at < (unixTimeNow() + ACCESS_TOKEN_EXPIRATION_FUZZ)) {
// console.log('Refresh token');
state.token = await client ? .refresh(state.token);
// console.log('Token refreshed');
saveState();
}
}
async function initialize() {
console.log('API Hubstaff API');
let data = loadState();
data = JSON.parse(data);
if (data.issuer) {
state.issuer = new Issuer(data.issuer);
state.issuer_expires_at = data.issuer_expires_at;
}
if (data.token) {
state.token = new TokenSet(data.token);
}
if (data.issuer_url) {
state.issuer_url = data.issuer_url;
}
if (data.api_base_url) {
state.api_base_url = data.api_base_url;
}
if (!state.issuer_expires_at || state.issuer_expires_at < unixTimeNow()) {
console.log('Discovering');
state.issuer = await Issuer.discover(state.issuer_url);
state.issuer_expires_at = unixTimeNow() + ISSUER_EXPIRE_DURATION;
console.log(state.issuer);
}
client = new state.issuer.Client({
// For personal access token we can use PAT/PAT.
// This is only needed because the library requires a client_id where as the API endpoint does not require it
client_id: 'Z',
client_secret: 'J',
});
saveState();
console.log('API Hubstaff initialized');
}
async function request(url, options) {
await checkToken();
let fullUrl = state.api_base_url + url;
return client ? .requestResource(fullUrl, state.token, options);
}
function tokenDetails() {
let ret = {};
if (state.token.access_token) {
ret.access_token = jose.JWT.decode(state.token.access_token);
}
if (state.token.refresh_token) {
ret.refresh_token = jose.JWT.decode(state.token.refresh_token);
}
return ret;
}
module.exports = {
initialize,
checkToken,
request,
tokenDetails
};
// COntroller
const { response } = require('express')
const api = require('../util/hubstaffConnect.util');
const testConnected = require('../util/testhubstaff.util');
const usersGet = async (req, res = response) => {
await api.initialize();
const response = await api.request('v2/organizations',{
method: 'GET',
json: true,
});
console.log('response', response);
if(response != null){
const body = JSON.parse(response);
res.json({
organizations: body.organizations || []
});
}
};
Although when I go to the address localhost:8080/oauth/api/organizations I ran into an error:
I do realise this is regarding missing tokens which won't let me get the user's information.

clearInterval of a external function not working - Node.JS

I have a setInterval function that's been called in another function, and I need to stop it when the proccess is done. I tried to set this setInterval function as a variable and call clearInterval, but the interval keeps running
const createInterval = (visibilityTimeout, startDateTime, message) => {
setInterval(() => {
const currentDateTime = moment().valueOf();
const timeDifference = (visibilityTimeout * 1000) - (currentDateTime - startDateTime);
if (timeDifference >= 600000) {
return;
}
if (timeDifference < 494983) {
const params = {
QueueUrl: 'http://localhost:4566/000000000000/test-queue2',
ReceiptHandle: message.ReceiptHandle,
VisibilityTimeout: visibilityTimeout,
};
sqs.changeMessageVisibility(params, (err, data) => {
if (err) logger.error(err, err.stack);
else logger.info(data);
});
// eslint-disable-next-line no-param-reassign
visibilityTimeout += 300;
}
}, 5000);
};
module.exports = async (message) => {
const startDateTime = moment().valueOf();
const {
noteId,
} = JSON.parse(message.Body);
logger.info(`Processing message [noteId=${noteId}]`);
try {
const note = await TestSessionNote.findById(noteId);
const testSession = await TestSession.findById(note.test_session_id);
logger.info(`Downloading video [key=${testSession.video_key}]`);
const isProcessing = true;
const interval = createInterval(500, startDateTime, message, isProcessing);
await sleep(20000);
clearInterval(interval);
logger.info(`Finished processing message [noteId=${noteId}]`);
} catch (ex) {
await TestSessionNote.update(noteId, { status: 'transcribe_error' });
logger.error(`Error processing message [noteId=${noteId}]`, ex);
}
};
I know that if i create a var test = setInterval(() => {console.log('blabla')}, 500) and call clearInterval(test) it works, but i don't know how can i do this calling a function
I think that you have to return from createInterval function the intervalId and after that it should work.
Can you check what value has your intervalId right now, with your current implementation?
https://developer.mozilla.org/en-US/docs/Web/API/setInterval
"The returned intervalID is a numeric, non-zero value which identifies the timer created by the call to setInterval(); this value can be passed to clearInterval() to cancel the interval."

How to include custom API request in ALEXA intent by using async

I have following problem which already took me several hours but I can´t fix it.
I want to create an Alexa skill, where the user is asked several questions. The user input afterwards sent to an API.
When I make the request separate everything works well. When it´s included in the intent it doesn´t work. It´s skipped even if I use async.
Here the code:
/* This code has been generated from your interaction model by skillinator.io
/* eslint-disable func-names */
/* eslint quote-props: ["error", "consistent"]*/
// There are three sections, Text Strings, Skill Code, and Helper Function(s).
// You can copy and paste the contents as the code for a new Lambda function, using the alexa-skill-kit-sdk-factskill template.
// This code includes helper functions for compatibility with versions of the SDK prior to 1.0.9, which includes the dialog directives.
// 1. Text strings =====================================================================================================
// Modify these strings and messages to change the behavior of your Lambda function
var request = require('request');
var size;
var heightSlot = 188;
var ageSlot = 35;
var weightSlot = 88;
var gender = "MALE"
var jsonBody = {
"gender" : gender,
"unit": "METRIC",
"height": heightSlot,
"weight": weightSlot,
"age": ageSlot
}
const options = {
method: 'PUT',
uri: "https://custom.apicall.com/api/finder/",
//headers: headers, // headers if your api requires
body: jsonBody,
json: true
};
let speechOutput;
let reprompt;
let welcomeOutput = "This is a placeholder welcome message. This skill includes 6 intents. Try one of your intent utterances to test the skill.";
let welcomeReprompt = "sample re-prompt text";
// 2. Skill Code =======================================================================================================
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
speechOutput = '';
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', welcomeOutput, welcomeReprompt);
},
'AMAZON.HelpIntent': function () {
speechOutput = 'Placeholder response for AMAZON.HelpIntent.';
reprompt = '';
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
speechOutput = 'Placeholder response for AMAZON.CancelIntent';
this.emit(':tell', speechOutput);
},
'AMAZON.StopIntent': function () {
speechOutput = 'Placeholder response for AMAZON.StopIntent.';
this.emit(':tell', speechOutput);
},
'SessionEndedRequest': function () {
speechOutput = '';
//this.emit(':saveState', true);//uncomment to save attributes to db on session end
this.emit(':tell', speechOutput);
},
'AMAZON.FallbackIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "This is a place holder response for the intent named AMAZON.FallbackIntent. This intent has no slots. Anything else?";
this.emit(":ask", speechOutput, speechOutput);
},
'AMAZON.NavigateHomeIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "This is a place holder response for the intent named AMAZON.NavigateHomeIntent. This intent has no slots. Anything else?";
this.emit(":ask", speechOutput, speechOutput);
},
'findOutMyCalorie': function () {
//delegate to Alexa to collect all the required slot values
let filledSlots = delegateSlotCollection.call(this);
speechOutput = '';
//any intent slot variables are listed here for convenience
let genderSlotRaw = this.event.request.intent.slots.gender.value;
console.log(genderSlotRaw);
let genderSlot = resolveCanonical(this.event.request.intent.slots.gender);
console.log(genderSlot);
let ageSlotRaw = this.event.request.intent.slots.age.value;
console.log(ageSlotRaw);
let ageSlot = resolveCanonical(this.event.request.intent.slots.age);
console.log(ageSlot);
let weightSlotRaw = this.event.request.intent.slots.weight.value;
console.log(weightSlotRaw);
let weightSlot = resolveCanonical(this.event.request.intent.slots.weight);
console.log(weightSlot);
let weight_unitSlotRaw = this.event.request.intent.slots.weight_unit.value;
console.log(weight_unitSlotRaw);
let weight_unitSlot = resolveCanonical(this.event.request.intent.slots.weight_unit);
console.log(weight_unitSlot);
let heightSlotRaw = this.event.request.intent.slots.height.value;
console.log(heightSlotRaw);
let heightSlot = resolveCanonical(this.event.request.intent.slots.height);
console.log(heightSlot);
let height_unitSlotRaw = this.event.request.intent.slots.height_unit.value;
console.log(height_unitSlotRaw);
let height_unitSlot = resolveCanonical(this.event.request.intent.slots.height_unit);
console.log(height_unitSlot);
myAsyncFn();
//Your custom intent handling goes here
speechOutput = "Deine Kalorienverbrauch ist " + size;
this.emit(':ask', speechOutput, speechOutput);
//Your custom intent handling goes here
//speechOutput = "This is a place holder response for the intent named findOutMySize, which includes dialogs. This intent has 6 slots, which are gender, age, weight, weight_unit, height, and height_unit. Anything else?";
//this.emit(':ask', speechOutput, speechOutput);
},
'Unhandled': function () {
speechOutput = "The skill didn't quite understand what you wanted. Do you want to try something else?";
this.emit(':ask', speechOutput, speechOutput);
}
};
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
alexa.appId = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
//alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
//alexa.dynamoDBTableName = 'DYNAMODB_TABLE_NAME'; //uncomment this line to save attributes to DB
alexa.execute();
};
// END of Intent Handlers {} ========================================================================================
// 3. Helper Function =================================================================================================
async function myAsyncFn() {
try {
await request(options, function(err, response) {
size = response.body.size;
console.log(response.body.size)
});
}
catch (error) {
console.log(error);
}
}
function resolveCanonical(slot){
//this function looks at the entity resolution part of request and returns the slot value if a synonyms is provided
let canonical;
try{
canonical = slot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
}catch(err){
console.log(err.message);
canonical = slot.value;
};
return canonical;
};
function delegateSlotCollection(){
console.log("in delegateSlotCollection");
console.log("current dialogState: "+this.event.request.dialogState);
if (this.event.request.dialogState === "STARTED") {
console.log("in Beginning");
let updatedIntent= null;
// updatedIntent=this.event.request.intent;
//optionally pre-fill slots: update the intent object with slot values for which
//you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property
//this.emit(":delegate", updatedIntent); //uncomment this is using ASK SDK 1.0.9 or newer
//this code is necessary if using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady', updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
// return a Dialog.Delegate directive with no updatedIntent property.
//this.emit(":delegate"); //uncomment this is using ASK SDK 1.0.9 or newer
//this code necessary is using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', null, null),
shouldEndSession: false
});
this.emit(':responseReady');
} else {
console.log("in completed");
console.log("returning: "+ JSON.stringify(this.event.request.intent));
// Dialog is now complete and all required slots should be filled,
// so call your normal intent handler.
return this.event.request.intent;
}
}
function randomPhrase(array) {
// the argument is an array [] of words or phrases
let i = 0;
i = Math.floor(Math.random() * array.length);
return(array[i]);
}
function isSlotValid(request, slotName){
let slot = request.intent.slots[slotName];
//console.log("request = "+JSON.stringify(request)); //uncomment if you want to see the request
let slotValue;
//if we have a slot, get the text and store it into speechOutput
if (slot && slot.value) {
//we have a value in the slot
slotValue = slot.value.toLowerCase();
return slotValue;
} else {
//we didn't get a value in the slot.
return false;
}
}
//These functions are here to allow dialog directives to work with SDK versions prior to 1.0.9
//will be removed once Lambda templates are updated with the latest SDK
function createSpeechObject(optionsParam) {
if (optionsParam && optionsParam.type === 'SSML') {
return {
type: optionsParam.type,
ssml: optionsParam['speech']
};
} else {
return {
type: optionsParam.type || 'PlainText',
text: optionsParam['speech'] || optionsParam
};
}
}
function buildSpeechletResponse(options) {
let alexaResponse = {
shouldEndSession: options.shouldEndSession
};
if (options.output) {
alexaResponse.outputSpeech = createSpeechObject(options.output);
}
if (options.reprompt) {
alexaResponse.reprompt = {
outputSpeech: createSpeechObject(options.reprompt)
};
}
if (options.directives) {
alexaResponse.directives = options.directives;
}
if (options.cardTitle && options.cardContent) {
alexaResponse.card = {
type: 'Simple',
title: options.cardTitle,
content: options.cardContent
};
if(options.cardImage && (options.cardImage.smallImageUrl || options.cardImage.largeImageUrl)) {
alexaResponse.card.type = 'Standard';
alexaResponse.card['image'] = {};
delete alexaResponse.card.content;
alexaResponse.card.text = options.cardContent;
if(options.cardImage.smallImageUrl) {
alexaResponse.card.image['smallImageUrl'] = options.cardImage.smallImageUrl;
}
if(options.cardImage.largeImageUrl) {
alexaResponse.card.image['largeImageUrl'] = options.cardImage.largeImageUrl;
}
}
} else if (options.cardType === 'LinkAccount') {
alexaResponse.card = {
type: 'LinkAccount'
};
} else if (options.cardType === 'AskForPermissionsConsent') {
alexaResponse.card = {
type: 'AskForPermissionsConsent',
permissions: options.permissions
};
}
let returnResult = {
version: '1.0',
response: alexaResponse
};
if (options.sessionAttributes) {
returnResult.sessionAttributes = options.sessionAttributes;
}
return returnResult;
}
function getDialogDirectives(dialogType, updatedIntent, slotName) {
let directive = {
type: dialogType
};
if (dialogType === 'Dialog.ElicitSlot') {
directive.slotToElicit = slotName;
} else if (dialogType === 'Dialog.ConfirmSlot') {
directive.slotToConfirm = slotName;
}
if (updatedIntent) {
directive.updatedIntent = updatedIntent;
}
return [directive];
}
Do you get an error message? My feeling is that the handler itself needs to be async too.
I solved it like this
const getSpeechOutput = async function (version) {
const gadata = await ga.getGA(gaQueryUsers, 'ga:users')
let speechOutput;
...
return speechOutput
}
const UsersIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'UsersIntent';
},
async handle(handlerInput) {
try {
const speechOutput = await getSpeechOutput("long");
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard(defaulttext.SKILL_NAME, speechOutput)
.getResponse();
} catch (error) {
console.error(error);
}
},
};
module.exports = {
UsersIntentHandler,
getSpeechOutput
}
That the function with the actual api call:
const getGA = async (gaQuery,requestResult) => {
try {
jwt.authorize()
const result = await google.analytics('v3').data.ga.get(gaQuery)
return result.data.totalsForAllResults[requestResult];
} catch (error) {
throw error
}2
}

Polling a URL until certain value is set in JSON response : Mocha, Integration testing

I am working on automating an End to end scenario using Mocha.
I have a url endpoint which is to be polled until a certain value is obtained in the resulting response. Is there any way to do it ?
Example with request and callback approach:
const request = require('request');
describe('example', () => {
it('polling', function (done) {
this.timeout(5000);
let attemptsLeft = 10;
const expectedValue = '42';
const delayBetweenRequest = 100;
function check() {
request('http://www.google.com', (error, response, body) => {
if (body === expectedValue) return done();
attemptsLeft -= 1;
if (!attemptsLeft) return done(new Error('All attempts used'));
setTimeout(check, delayBetweenRequest);
});
}
check();
});
});
Example with got and async/await approach:
const utils = require('util');
const got = require('got');
const wait = utils.promisify(setTimeout);
describe('example', () => {
it('polling', async function (done) {
this.timeout(5000);
const expectedValue = '42';
const delayBetweenRequest = 100;
for (let attemptsLeft = 10; attemptsLeft; attemptsLeft -= 1) {
const resp = await got.get('http://www.google.com');
if (resp.body === expectedValue) return done();
await wait(delayBetweenRequest);
}
done(new Error('All attempts used'));
});
});
This is how I was able to do it with WebdriverIO and Mocha
describe("wait for value in content of page", () => {
it("should be able to wait to value in url", () => {
var max_seconds_to_wait = 10;
var seconds_counter = 0;
var should_continue = true;
while (should_continue) {
browser.url('http://your.url.com');
var response = JSON.parse(browser.getText("body"));
console.log(response)
if (response == 'something') {
should_continue = false;
}
browser.pause(1000);
seconds_counter++;
if (seconds_counter > max_seconds_to_wait) {
throw 'Waiting for json from url timeout error';
}
}
});
});

Resources