Get my Action’s server URL in (JavaScript) fulfilment code - node.js

I am using actionssdk and I build my Action fulfilments using Javascript and node.js + Express.
I am looking for a way to get the url (protocol + host name + port) of the server where the fulfilment is hosted.
Is there a simple way to do this? E.g. in the MAIN intent? Is there some conv-property I can use? Can I get hold of a req-parameter in the MAIN-intent, from which I can deduct hostname etc?
const express = require('express');
const expressApp = express();
const { actionssdk, ... } = require('actions-on-google');
const app = actionssdk({
ordersv3: true,
clientId: ...
});
expressApp.post('/fulfilment', app);
app.intent('actions.intent.MAIN', (conv) => {
let myUrl: string = ... // ???????
...
});
(background: obviously I know myself to where I deployed my fulfilment code. But I have a reusable template for fulfilment code in which I want to refer to the host url, and I do not want to type that in manually each time I develop a new Action).

You can get access to the request object in a middleware via Framework Metadata which is by default of type BuiltinFrameworkMetadata which contains objects used by Express
For example, you can use it like this, which will be ran before each request:
app.middleware((conv, framework) => {
console.log(framework.express.request.headers.host)
})

Related

Why am I getting: AskSdk.Request verification failed Error, Missing Certificate for the skill request?

I got a self hosted skill in Cloud Foundry, that uses Node js, Typescript and Express.
The code looks like this:
const app: express.Application = express()
// Skill entry point
const skill = application.getSkillBuilder.createSkillForWebService()
const adapter = new ExpressAdapter(skill, config.global.verifySignature, config.global.verifyTimestamp)
const PORT = app.listen(process.env.PORT || config.global.port)
app.post("/", adapter.getRequestHandlers())
const listener = app.listen(PORT, function () {
const { address, port } = listener.address() as AddressInfo
logger.debug("%s listening to %s%s", app.name, address, port)
})
So when I deploy into a lower environment and a new instance of the Express adapter is created, I pass false for verify signature and verify timestamp.
And it works when I test it on an Alexa device and on the Alexa developers console.
And when I deploy it into production and a new instance of the Express adapter is created, I pass true for the verify signature and verify timestamp.
And I get this error: AskSdk.Request verification failed Error, Missing Certificate for the skill request
Am I missing something in my code??
Do I need to upload the certificate in the Developers console??
Thanks in advance for your help

How to use Bolt events with the newer Slack API manifests?

I'm building a Slack App using Bolt and I've got the basics working using Socket Mode. The docs say that socket mode apps are not allowed in the public directory, which I do want my App in when it's ready. I've now turned off socket mode and got ngrok working as described here. Slack was able to validate the url anyway.
But what's not working is a slash command. The manifest editor says the url is required for a slash command, but how does that line up with bolt? Are there better docs for non-socket-mode somewhere? It seems like every example of using bolt says "let's use socket mode, it's easy".
Manifest portion:
slash_commands:
- command: /sb
url: https://[my url].ngrok.io/slack/command
Sample code:
const { App } = require('#slack/bolt');
const express = require('express');
const app = express();
const boltApp = new App({
signingSecret: config.slackApp.signingSecret,
token: config.slackApp.token,
endpoints = '/'
});
app.use('/slack/events', boltApp.receiver.router);
Bolt
Slack App Manifests
I got this working with a combination of the following:
setting every url in the manifest (slash_commands, event_subscriptions, interactivity) to https://foo.ngrok.io/slack/
attaching Bolt to an existing Express App, attempting to follow this PR to use app and/or router config prop on ExpressReceiver, but strangely what worked was putting the express app into the router
setting up Bolt like below
Example Code:
const expressApp = express();
...
const boltReceiver = new ExpressReceiver({
router: expressApp,
signingSecret: SLACK_SIGNING_SECRET,
endpoints: '/slack'
});
const boltApp = new App({
token: SLACK_BOT_TOKEN,
receiver: boltReceiver,
appToken: SLACK_APP_TOKEN,
socketMode: false,
});

Can Open Telemetry Instrument Two Express Services in the Same Node Program?

Let's say I have a NodeJS program that has two separate instances of an express server running.
const express = require('express')
const app1 = express()
app1.listen(3000, () => { //... })
//...
const app2 = express()
app2.listen(3001, () => { //... })
I've been able to instrument a program like this via open telemetry, and have my spans sent/exported successfully to Zipkin. All I needed to do is/was add code like the following to the start of my program.
const { NodeTracerProvider } = require('#opentelemetry/node');
const { ZipkinExporter } = require('#opentelemetry/exporter-zipkin');
const provider = new NodeTracerProvider({
plugins: {
express: {
enabled: true,
},
http: {
requestHook: (span, request) => {
span.setAttribute("custom request hook attribute", "request");
},
},
}
});
const options = {
url: 'http://localhost:9411/api/v2/spans',
serviceName: 'service-main'
}
const zipkinExporter = new ZipkinExporter(options);
provider.addSpanProcessor(new SimpleSpanProcessor(zipkinExporter))
provider.register();
and make sure that the express and http plugins were/are installed
npm install #opentelemetry/plugin-http #opentelemetry/plugin-express
This all works great -- except for one thing. Open Telemetry sees both my express services running as the same service-main service.
When I instrumented these services directly with Zipkin -- I would add the Zipkin middleware to each running express server
app1.use(zipkinMiddleware({tracer: tracer1}));
app2.use(zipkinMiddleware({tracer: tracer2}));
Each tracer could be instantiated with its own service name, which allowed each service to have its individual name and show up as a different service in Zipkin.
(/main, /hello, and /goobye are all service via a different express service in the above URL)
Is this sort of thing (instrumenting two services in one program) possible with Open Telemetry? Or would I need to separate these two services out into separate programs in order to have each services have an individual name? This question is less about solving a particular problem, and more about understanding the semantics of Open Telemetry.
It is possible to create two separate tracer providers. Only one of them will be the global tracer provider, which the API will use if you call API methods. You can't use the plugins in this configuration, which means you will have to manually instrument your application. If this is a use-case which is important to you, I suggest you create an issue on the github repo.
yes, you can have multiple express running in the same node process (thats how clustering works in node as well)
but you will need to have them running on different ports.;
# const express = require('express')
const app1 = express()
app1.listen(3001, () => { //... })
//...
const app2 = express()
app2.listen(3002, () => { //... })

Using Firebase function as a proxy server

I built an app with Vuejs which is hosted on firebase, I recently added dynamic rendering with rendertron to improve SEO, I'm hosting the rendertron on Heroku. The rendertron client work well.
In order to send requests coming from bots like googlebot to rendertron and recieve a compiled HTML file, I used firebase function, it checks for the user agent, if it's a bot then it sends it to the rendertron link, if it's not, it fetches the app and resend result.
Here's the function code:
const functions = require('firebase-functions');
const express = require('express');
const fetch = require('node-fetch');
const url = require('url');
const app = express();
const appUrl = 'khbich.com';
const renderUrl = 'https://khbich-render.herokuapp.com/render';
function generateUrl(request){
return url.format({
protocol:request.protocol,
host:appUrl,
pathname:request.originalUrl
});
}
function detectBot(userAgent){
let bots = [
"googlebot",
"bingbot",
"facebookexternalhit",
"twitterbot",
"linkedinbot",
"facebot"
]
const agent = userAgent.toLowerCase()
for(let bot of bots){
if(agent.indexOf(bot)>-1){
console.log('bot-detected',bot,agent)
}
}
}
app.get('*', (req,res)=>{
let isBot = detectBot(req.headers['user-agent']);
if(isBot){
let botUrl= generateUrl(req);
fetch(`${renderUrl}/${botUrl}`)
.then(res => res.text())
.then(body=>{
res.set('Cache-Control','public','max-age=300','s-maxage=600')
res.set('Vary','User-Agent');
res.send(body.toString())
})
}
else{
fetch(`https://${appUrl}`)
.then(res=>res.text())
.then(body=>{
res.send(body.toString())
})
}
});
I used the function as an entry point for firebase hosting, so it's invoked whenever someone enters the app.
I checked on the firebase dashboard to see if it's working, and I noticed that it crashed for exceeding the number of requests per 100 second quota, I don't have much users when I checked, and the function invocations reached 370 calls in one minute.
I don't see why I had a large number of calls all at once, I'm thinking that maybe since I'm fetching the website if the user agent is not a bot, then the function is re-invoked causing an infinite loop of invocations, but I don't know if that's really why ?
If it's an infinite loop, how can I redirect users to their entered url without reinvoking the function ? will a redirect work ?

Creating an API to wrap an... API?

This isn’t a specific issue question, but I’m trying to wrap my head around the concept of REST APIs and calling an API within your own API.
For example, if I develop an App called “BeesBees”, where users can buy bees, and I have a database of logins and passwords and obviously the bees, and how much each cost, I understand that I can used my own apps BeesBees API to get the list of bees (and if I make it open for other people, they can also use my GET /bees point to get, well, the bees)
But if I want to allow customers to buy the bees, and I don’t want to build a whole platform for doing so, so I integrate Stripe - could I have a POST /:users/charges/:priceOfBee API call that, in turn, called the Stripe API function somewhere somehow? For example, pointing to the URL of a Node.js project that will make the charge using Stripe’s Node.js SDK.
Or, in a case like this, would it be better to just implement the Stripe SDK for my device’s platform and make the charge using the device?
I have no code base so there’s nothing to pick apart, but I couldn’t think of anywhere else to ask, sorry y’all
You can certainly wrap APIs with other APIs, it's really just a form of composition, delegate to other services where it makes sense to do so.
Stripe integration might well be a good example of where it could make sense to follow this pattern. You certainly don't want to roll your own payment service.
Here's a trivial example of a local Express server making an API request:
const express = require("express");
const port = 3000;
const app = express();
const request = require('request');
app.get("/api/example", (req, res) => {
// Make external REST request here...
let options = {
url: 'https://httpbin.org/get',
qs: { parameter1: 42 },
json: true
}
request(options, (err, response, body) => {
if (err) {
res.status(500).send(err.message);
} else {
res.status(200).send(body);
}
});
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('running at http://' + host + ':' + port)
});
console.log(`Serving at http://localhost:${port}`);

Resources