I have an Azure App Service as a backend that is written using Node currently with verbose logging enabled. Within the application logs, I seem to always get duplicate log messages for various requests.
2018-02-28T16:26:33 PID[22384] Verbose Received request: GET https://appname.azurewebsites.net/tables/TableOne?$filter=(email%20eq%20'stevejohns%40gmail.com')
2018-02-28T16:26:33 PID[22384] Verbose Received request: GET https://appname.azurewebsites.net/tables/TableOne?$filter=(email%20eq%20'stevejohns%40gmail.com')
I have confirmed this issue isn't in my mobile application as the result is the same if I send a request from Postman. This leads me to believe I somehow have a second instance of something running in the service. I've looked through Kudu as much as possible but haven't come across any indication that would cause this logging issue.
Please see below for my current app.js and TableOne.js files.
Any tips are much appreciated!
app.js
var express = require('express'), azureMobileApps = require('azure-mobile-apps');
var app = express();
var mobile = azureMobileApps({
homePage: true
});
mobile.tables.import('./tables');
mobile.api.import('./api');
mobile.tables.initialize()
.then(function () {
app.use(mobile);
app.listen(process.env.PORT || 3000);
});
TableOne.js
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
table.access = 'authenticated';
table.name = 'TableOne';
table.maxTop = 1000;
table.softDelete = false;
table.dynamicSchema = false;
table.columns = {
firstname: 'string',
lastname: 'string',
email: 'string',
profilepictureurl: 'string',
phonenumber: 'string'
};
table.insert(function (context) {
return context.execute();
});
table.read(function (context) {
return context.execute();
});
table.update(function (context) {
return context.execute();
});
table.delete(function (context) {
return context.execute();
});
module.exports = table;
Related
I'm a huge sneaker head and getting my feet wet in node and express and I'm trying to create an app using Twilio to text me when a certain shoe decreases in price and the shoe is triggered by replying "Track (SKU)" to add it to the database. I have it completely written and when I input "node index.js", it connects to my localhost just fine, but when I input http://localhost:3000/sms, I get Cannot GET /sms. I've been trying to look for a fix by trying to find some videos or if someone has done a similar project to model off of, but no luck. Is anyone familiar with creating Twilio apps with express servers able to help me out? My current index.js is below.
const SneaksAPI = require('sneaks-api');
const sneaks = new SneaksAPI();
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: false }));
const MessagingResponse = require('twilio').twiml.MessagingResponse;
const Subscribers = [
{
"number": 2025550121,
"styleID": "FY2903",
"lastResellPrice": 475
},
{
"number": 1234567890,
"styleID": "DD0587-600",
"lastResellPrice": 285
}
];
app.post('/sms', function (req, res) {
const twiml = new MessagingResponse();
const SMS = twiml.message();
const recievedSMS = req.body.Body.toLowerCase().trim();
const firstWord = recievedSMS.split(" ")[0];
if (firstWord == 'track'){
const styleID = recievedSMS.split(" ")[1] || null;
if(styleID){
const sneaker = sneaksApiFunctionWrapper(styleID);
if(!sneaker){
SMS.body("Sneaker could not be found");
} else {
const sub = {
"number": req.body.From,
"styleID": sneaker.styleID,
"lastResellPrice": sneaker.price
};
Subscribers.push(sub);
SMS.media(sneaker.image);
SMS.body(`Current lowest price for ${sneaker.name} is $${String(sneaker.price)} at ${sneaker.site}: ${sneaker.url}\nYou will be notified when the price drops. Reply STOP to opt-out of alerts.`);
}
}
} else {
SMS.body('To start tracking a sneaker, text \"track\" followed by the sneaker ID.');
}
res.writeHead(200, {'Content-Type': 'text/xml'});
res.end(twiml.toString());
});
function sneaksApiFunctionWrapper(styleID) {
return new Promise((resolve, reject) => {
sneaks.getProductPrices(styleID, function(err, product){
const lowestResellSite = Object.keys(product.lowestResellPrice).reduce((a, b) => product.lowestResellPrice[a] > product.lowestResellPrice[b] ? a : b);
const sneaker = {
name: product.shoeName,
image: product.thumbnail,
site: lowestResellSite,
price: product.lowestResellPrice[lowestResellSite],
url: product.resellLinks[lowestResellSite],
styleID: product.styleID
};
resolve(sneaker);
}, (errorResponse) => {
reject(errorResponse);
});
});
}
app.listen(3000, () => {
console.log('Express server listening on port 3000');
});```
You are using a POST request via app.post(), which is different from a GET request. You'll need to submit data via a form or apps like Postman to properly access the POST request.
When should I use GET or POST method? What's the difference between them? This gives a solid response.
I am trying to get SharePoint list items from Node.js app but I am still getting following error:
No XML to parse!
at new XmlDocument (C:\Users\xx\Desktop\AppTest2\node_modules\xmldoc\lib\xmldoc.js:247:11)
at C:\Users\xx\Desktop\AppTest2\node_modules\node-sp-auth\lib\src\utils\AdfsHelper.js:28:25
at processTicksAndRejections (node:internal/process/task_queues:96:5)
As you may see, I tried it with 'node-sp-auth' and 'sp-request' but result is the same.
Could it be some security or network related issue or do I miss something?
I tried the same http request via power automate and it was working.
Thanks for your help in advance.
var express = require('express');
var app = express();
const port = process.env.PORT || 3000;
//sharepoint auth libs
var spauth = require('node-sp-auth');
var request = require('request-promise');
var $REST = require("gd-sprest");
var url = "https://inoffice.sharepoint.com/sites/";
const credentialOptions =
{
username: "xx",
password: "oxxVMQOz",
online: true
};
const credentialOptions2 =
{
username: "yy",
password: "xxx",
online: true
};
var sprequest = require('sp-request');
let spr = sprequest.create(credentialOptions);
spr.get('https://inoffice.sharepoint.com/sites/DEV/_api/web/lists/GetByTitle(\'Users\')')
.then(response => {
console.log('List Id: ' + response.body.d.Id);
})
.catch(err =>{
console.log(err);
});
/*
spauth.getAuth(
url,
{
username: "WW_DEV_AAPeople_Mgmt#emea.adecco.net",
password: "oq&a#Dl59XVMQOz",
online: true
}
).then(options => {
// Log
console.log("Connected to SPO");
// Code Continues in 'Generate the Request'
});
*/
I have a TEAMS node.js bot running locally (with ngrok). I receive messages from TEAMS client and echo works
context.sendActivity(`You said '${context.activity.text}'`);
I need to send a proactive message, but i get an error creating conversation pbject.
My code:
...
await BotConnector.MicrosoftAppCredentials.trustServiceUrl(sServiceUrl);
var credentials = new BotConnector.MicrosoftAppCredentials({
appId: "XXXXXXXXXXXX",
appPassword: "YYYYYYYYYYYYY"
});
var connectorClient = new BotConnector.ConnectorClient(credentials, { baseUri: sServiceUrl });
const parameters = {
members: [{ id: sUserId }],
isGroup: false,
channelData:
{
tenant: {
id: sTenantId
}
}
};
// Here I get the error: "TypeError: source.on is not a function"
var conversationResource = await connectorClient.conversations.createConversation(parameters);
await connectorClient.conversations.sendToConversation(conversationResource.id, {
type: "message",
from: { id: credentials.appId },
recipient: { id: sUserId },
text: 'This a message from Bot Connector Client (NodeJS)'
});
String values are correct and I have a valid connectorClient.
Thanks in advance,
Diego
I think you are close but just need to make a couple modifications. It's hard to tell because I can't see all of your code.
Here, I add the Teams middleware to the adapter in index.js and am making the call that triggers the proactive message from within a waterfall step in mainDialog.js. The location of the code shouldn't matter as long as you can pass context in.
Also, with regards to your message construction, sending the text alone is sufficient. The recipient is already specified in the paremeters with the rest of the activity properties assigned via the connector. There is no need to reconstruct the activity (unless you really need to).
Lastly, be sure to trust the serviceUrl. I do this via the onTurn method in mainBot.js.
Hope of help!
index.js
const teams = require('botbuilder-teams');
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
})
.use(new teams.TeamsMiddleware())
mainDialog.js
const { ConnectorClient, MicrosoftAppCredentials } = require('botframework-connector');
async teamsProactiveMessage ( stepContext ) {
const credentials = new MicrosoftAppCredentials(process.env.MicrosoftAppId, process.env.MicrosoftAppPassword);
const connector = new ConnectorClient(credentials, { baseUri: stepContext.context.activity.serviceUrl });
const roster = await connector.conversations.getConversationMembers(stepContext.context.activity.conversation.id);
const parameters = {
members: [
{
id: roster[0].id
}
],
channelData: {
tenant: {
id: roster[0].tenantId
}
}
};
const conversationResource = await connector.conversations.createConversation(parameters);
const message = MessageFactory.text('This is a proactive message');
await connector.conversations.sendToConversation(conversationResource.id, message);
return stepContext.next();
}
mainBot.js
this.onTurn(async (context, next) => {
if (context.activity.channelId === 'msteams') {
MicrosoftAppCredentials.trustServiceUrl(context.activity.serviceUrl);
}
await next();
});
I created my bot with Yeoman, so I had a running example which can respond to an incoming message.
https://learn.microsoft.com/es-es/azure/bot-service/javascript/bot-builder-javascript-quickstart?view=azure-bot-service-4.0
With that, I can respond to an incoming message and it works
My complete code:
index.js: // Created automatically, not modified
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const dotenv = require('dotenv');
const path = require('path');
const restify = require('restify');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter } = require('botbuilder');
// This bot's main dialog.
const { MyBot } = require('./bot');
// Import required bot configuration.
const ENV_FILE = path.join(__dirname, '.env');
dotenv.config({ path: ENV_FILE });
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`);
console.log(`\nTo test your bot, see: https://aka.ms/debug-with-emulator`);
});
// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about how bots work.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
console.error(`\n [onTurnError]: ${ error }`);
// Send a message to the user
await context.sendActivity(`Oops. Something went wrong!`);
};
// Create the main dialog.
const myBot = new MyBot();
// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// Route to main dialog.
await myBot.run(context);
});
});
My bot file. bot.js // created automatically, modified to add proactive messages
const { ConnectorClient, MicrosoftAppCredentials, BotConnector } = require('botframework-connector');
const { ActivityHandler } = require('botbuilder');
class MyBot extends ActivityHandler {
constructor() {
super();
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
this.onMessage(async (context, next) => {
//await context.sendActivity(`You said '${context.activity.text}'`); // It works
var activity = context.activity;
await MicrosoftAppCredentials.trustServiceUrl(activity.serviceUrl);
var connectorClient = new ConnectorClient(credentials, { baseUri: activity.serviceUrl });
const parameters = {
members: [{ id: activity.from.id }],
isGroup: false,
channelData:
{
tenant: {
id: activity.conversation.tenantId
}
}
};
var conversationResource = await connectorClient.conversations.createConversation(parameters);
});
}
module.exports.MyBot = MyBot;
In 'createConversation' is where I get the error:
[onTurnError]: TypeError: source.on is not a function
This code will be in a method, to call it when required, I put it in 'onMessage' just to simplify
I think my code is like yours... But I'm afraid I am missing something or doing sth wrong...
Thanks for your help,
Diego
I have a stack trace of the error, and it seems to be related to 'delayed stream' node module. I add as a response since it is too long for a comment:
(node:28248) UnhandledPromiseRejectionWarning: TypeError: source.on is not a function
at Function.DelayedStream.create ([Bot Project Path]\node_modules\delayed-stream\lib\delayed_stream.js:33:10)
at FormData.CombinedStream.append ([Bot Project Path]\node_modules\combined-stream\lib\combined_stream.js:45:37)
at FormData.append ([Bot Project Path]\node_modules\form-data\lib\form_data.js:74:3)
at MicrosoftAppCredentials.refreshToken ([Bot Project Path]\node_modules\botframework-connector\lib\auth\microsoftAppCredentials.js:127:20)
at MicrosoftAppCredentials.getToken ([Bot Project Path]\node_modules\botframework-connector\lib\auth\microsoftAppCredentials.js:91:32)
at MicrosoftAppCredentials.signRequest ([Bot Project Path]\node_modules\botframework-connector\lib\auth\microsoftAppCredentials.js:71:38)
at SigningPolicy.signRequest ([Bot Project Path]\node_modules\#azure\ms-rest-js\dist\msRest.node.js:2980:44)
at SigningPolicy.sendRequest ([Bot Project Path]\node_modules\#azure\ms-rest-js\dist\msRest.node.js:2984:21)
at ConnectorClient.ServiceClient.sendRequest ([Bot Project Path]\node_modules\#azure\ms-rest-js\dist\msRest.node.js:3230:29)
at ConnectorClient.ServiceClient.sendOperationRequest ([Bot Project Path]\node_modules\#azure\ms-rest-js\dist\msRest.node.js:3352:27)
I found my problem. I must set credentials/conector/trust in the correct order:
credentials = new MicrosoftAppCredentials(process.env.MicrosoftAppId, process.env.MicrosoftAppPassword);
connectorClient = new ConnectorClient(credentials, { baseUri: activity.serviceUrl });
MicrosoftAppCredentials.trustServiceUrl(activity.serviceUrl);
I have a simple Express/ gRPC project that's supposed to print a hard coded JSON object to the browser & console. The problem is that the object isn't returned to the client in time to receive it from the gRPC server.
My project is actually a modification of this Codelabs project. I've only implemented the listBooks method though, and have changed Go to Express. I was able to successfully get a response from the server in the Codelabs project.
At first I thought about avoiding callbacks, and trying promises (promises, promisfy...) instead. But I haven't had success. Also, from this answer, I now know that Node gRPC isn't implemented to have sync:
"Node gRPC does not have synchronous calls." - murgatroid99
With that said, the output is showing that the data isn't being received. What do I need to do to have the client wait for the data to be available to be received?
products.proto
syntax = "proto3";
package products;
service ProductService {
rpc ListProduct (Empty) returns (ProductList) {}
}
message Empty {}
message Product {
int32 id = 1;
string name = 2;
string price = 3;
}
message ProductList {
repeated Product products = 1;
}
server.js
var PROTO_PATH = __dirname + '/products.proto';
var grpc = require('grpc');
var protoLoader = require('#grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var productsProto = grpc.loadPackageDefinition(packageDefinition).products;
var products = [{
id: 123,
name: 'apple',
price: '$2'
}];
function listProduct(call, callback){
console.log(products);
callback(null, products);
}
function main(){
var server = new grpc.Server();
server.addService(productsProto.ProductService.service,
{ListProduct: listProduct}
);
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
console.log("server started");
server.start();
}
main();
client.js
var PROTO_PATH = __dirname + '/products.proto';
var grpc = require('grpc');
var protoLoader = require('#grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var products = grpc.loadPackageDefinition(packageDefinition).products;
var client = new products.ProductService('localhost:50051', grpc.credentials.createInsecure());
function printResponse(error, response){
if(error){
console.log('Error: ', error);
}
else{
console.log(response);
}
}
function listProducts() {
client.listProduct({}, function(error, products){
printResponse(error, products);
});
}
exports.listProducts = listProducts;
client-server.js
const express = require('express');
const app = express();
var client = require('./client');
app.get('/', (req, res) => {
console.log('Hello World!');
client.listProducts();
res.send('Hello World!');
});
app.listen(3000, () =>
console.log('Example app listening on port 3000!'),
);
Actual Result
The gRPC server obviously already has the object, but I print it to console just for fun. The client prints its callback result to the console, but is only printing an empty object.
server.js Output
[ { id: 123, name: 'apple', price: '$2' } ]
client.js Output
Hello World!
{ products: [] }
To Reproduce
Open 2 terminals, cd to project
Run node server.js in one terminal
Run node client-server.js in the other terminal
Open a browser to localhost:3000
The problem here has nothing to do with synchronous vs asynchronous actions.
The object you are trying to send from your server request handler does not match the response message type you declared in your products.proto file. The response message type ProductList is a message with a single field products that is a repeated list of ProductObjects. So to match that the object you send should be an object with a products field that contains an array of objects structured like ProductObject messages. The code you have is almost there. You have the array, so your server handler code should look like this:
function listProduct(call, callback){
console.log(products);
callback(null, {products: products});
}
The output you already have hints towards this. The object your client receives is {products: []}, which is the structure of the objects your server should be sending.
How to send bot response based on user session in NodeJS.
I have created a sample NodeJS application using botframework and connecting to IBM watson to process the user query to get the appropriate response and sending back the response with 10secs delay.
I have generate the ngrok URL and configured it bot framework for web channel. When i test this in mutipl browsers simulataneously , i am getting the same response for all , not based on the user session and user request.
It's giving the latest request processed response to everyuser. I have copied the sample nodejs code below.
Can someone please look into my code and suggest me on how to send the response based on user session/conversationID?
NodeJS sample code :
'use strict';
var restify = require('restify');
var builder = require('botbuilder');
var Conversation = require('watson-developer-cloud/conversation/v1');
require('dotenv').config({silent: true});
var server = restify.createServer();
var contexts = { CandidateID : "89798" , Location : "USA" }
var workspace= 'XXXXXXXXXXXXXXXX';
let name,id,message,addr,watson;
var savedAddress;
server.listen(process.env.port || process.env.PORT || 3000, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create the service wrapper
var conversation = new Conversation({
username: 'XXXXXXXXXXXXXXXX',
password: 'XXXXXXXXXXXXXXXX',
url: 'https://gateway.watsonplatform.net/conversation/api',
version_date: 'XXXXXXXXXXXXXXXX'
});
// setup bot credentials
var connector = new builder.ChatConnector({
appId: 'XXXXXXXXXXXXXXXX',
appPassword: 'XXXXXXXXXXXXXXXX'
});
var bot = new builder.UniversalBot(connector,);
server.post('/api/messages', connector.listen());
// root dialog
bot.dialog('/', function (session, args) {
name = session.message.user.name;
id = session.message.user.id;
message = session.message.text;
savedAddress = session.message.address;
var payload = {
workspace_id: workspace,
context: contexts,
input: {
text: session.message.text
}
};
var conversationContext = {
workspaceId: workspace,
watsonContext: contexts
};
if (!conversationContext) {
conversationContext = {};
}
payload.context = conversationContext.watsonContext;
conversation.message(payload, function(err, response) {
if (err) {
console.log(err);
session.send(err);
} else {
console.log(JSON.stringify(response, null, 2));
conversationContext.watsonContext = response.context;
watson = response.output.text;
}
});
console.log(message);
setTimeout(() => {
startProactiveDialog(savedAddress);
}, 10000);
});
// handle the proactive initiated dialog
bot.dialog('/survey', function (session, args, next) {
if (session.message.text === "done") {
session.send("Great, back to the original conversation");
session.endDialog();
} else {
session.send(id + ' ' + watson);
}
});
// initiate a dialog proactively
function startProactiveDialog(address) {
bot.beginDialog(address, "*:/survey");
}
You need to make sure it passes back and forth all of the system context for that user's session. Our botkit middleware essentially does this for you.
https://github.com/watson-developer-cloud/botkit-middleware
It looks like that you are leveraging the sample at https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/core-proactiveMessages/startNewDialog Sending a dialog-based proactive message to specific user.
The sample is just for reference, it only saves the latest conversion at it only declare one variable savedAddress, each time the new user comes in, the new seession address will assign to savedAddress and cover the old value.
You can try to define an object to save user addresses for quick test:
var savedAddress = {};
bot.dialog('/', function (session, args) {
savedAddress[session.message.address.id] = session.message.address;
var message = 'Hey there, I\'m going to interrupt our conversation and start a survey in a few seconds.';
session.send(message);
message = 'You can also make me send a message by accessing: ';
message += 'http://localhost:' + server.address().port + '/api/CustomWebApi';
session.send(message);
setTimeout(() => {
startProactiveDialog(savedAddress[session.message.address.id]);
}, 5000);
});
For production stage, you can leverage Redis or some other cache storage to store this address list.
Hope it helps.