Firebase Cloud Function with Swagger: Error: could not handle the request - google-cloud-functions

I have this code in a function called docs:
const functions = require("firebase-functions");
const express = require("express");
const swaggerUi = require("swagger-ui-express");
const swaggerJsdoc = require("swagger-jsdoc");
const options = {
definition: {
openapi: "3.0.0",
info: {
title: "API",
version: "1.0.0",
},
},
apis: ["../**/*.function.js"],
};
const openapiSpecification = swaggerJsdoc(options);
console.log("🚀 ", openapiSpecification);
const app = express();
app.use(
"/",
swaggerUi.serve,
swaggerUi.setup(openapiSpecification, {
swaggerOptions: {
supportedSubmitMethods: [], //to disable the "Try it out" button
},
})
);
module.exports = functions.https.onRequest(app);
But every time I hit the URL, this error gets returned:
Error: could not handle the request
URL:
https://REGION-PROJECT.cloudfunctions.net/docs
The deps above are all installed.
Any idea what is causing this issue?
Or better yet, how can I serve an endpoint for Swagger docs?
Please keep in mind that all other functions don't use express; most are callable. Just this one function uses it, so not sure if the mixing not supported.
Here's the folder structure:
package.json
index.js
app
auth
login.function.js
users
createUser.function.js
Inside index.js, the functions get loaded and added to the exports dynamically. This setup works fine and deploys well, so no issues there.

Related

Error when deploying DialogFlow CX webhook on Google Cloud Functions: "Error: listen EADDRINUSE: address already in use :::8080"

I desperately try to implement a simple Webhook for my DialogFlow CX agent. Never done this before so I just copy paste the index.js and package.json code I found on the following page to my Google Cloud Function: DialogFlow CX calculate values
But it seems this is not working. When trying to deploy the Cloud Function I get the error "Error: listen EADDRINUSE: address already in use :::8080".
Same happens if I take this sample code: Dialogflow CX webhook for fulfilment to reply user using nodejs
What am I doing wrong? I am editing the code and trying to deploy it directly in the Google Cloude web console and not via a command prompt tool.
HERE SOME MORE DETAILS:
Setup of Google Cloud Function: I set up a new Google Cloud Function via Google Cloud Console by clicking Create Function. I set Region to us-east1, Trigger type to HTTP and Allow unauthenticated invocations. Then I save, update the index.js and package.json as described below and click Deploy. The result is that deployment could not be done because of Error: listen EADDRINUSE: address already in use :::8080.
Here the code I put into to index.js:
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
var port = process.env.PORT || 8080;
app.use(
bodyParser.urlencoded({
extended: true
})
);
app.use(bodyParser.json());
app.post('/BMI', (req, res) => processWebhook4(req, res));
var processWebhook4 = function(request, response ){
const params = request.body.sessionInfo.parameters;
var heightnumber = params["height.number"];
var weightnumber = params["weight.number"];
var heightunit = params["height.unit-height"]
var weightunit = params["weight.unit-weight"]
var computedBMI;
if (heightunit == "cm" && weightunit == "kg") { //using metric units
computedBMI = ((weightnumber/heightnumber/heightnumber )) * 10000;
} else if (heightunit == "in" && weightunit == "lb") { //using standard metrics
computedBMI = ((weightnumber/heightnumber/heightnumber )) * 703;
}
const replyBMI = {
'fulfillmentResponse': {
'messages': [
{
'text': {
'text': [
'This is a response from webhook! BMI is ' + computedBMI
]
}
}
]
}
}
response.send(replyBMI);
}
app.listen(port, function() {
console.log('Our app is running on http://localhost:' + port);
});
And here the code I put into package.json:
{
"name": "cx-test-functions",
"version": "0.0.1",
"author": "Google Inc.",
"main": "index.js",
"engines": {
"node": "8.9.4"
},
"scripts": {
"start": "node index.js"
},
"dependencies": {
"body-parser": "^1.18.2",
"express": "^4.16.2"
}
}
The code in the StackOverflow posts you’ve shared is working on other platforms such as Heroku.
The error you encountered “Error: listen EADDRINUSE: address already in use :::8080” is because of the code function listening to port 8080. Note that you will need to check and edit the sample code you’ve provided, and see if the libraries used are supported (for example: express js) in Google Cloud Functions and if the libraries are compatible in order to use it in Google Cloud Functions.
Here’s a working code for Cloud Functions from this StackOverflow Post:
exports.helloWorld = (req, res) => {
const params = req.body.sessionInfo.parameters;
var heightnumber = params.height.number;
var weightnumber = params.weight.number;
var heightunit = params.height.heightUnit;
var weightunit = params.weight.weightUnit;
var computedBMI;
if (heightunit == "cm" && weightunit == "kg") { //using metric units
computedBMI = ((weightnumber/heightnumber/heightnumber )) * 10000;
} else if (heightunit == "in" && weightunit == "lb") { //using standard metrics
computedBMI = ((weightnumber/heightnumber/heightnumber )) * 703;
}
const replyBMI = {
'fulfillmentResponse': {
'messages': [
{
'text': {
'text': [
'This is a response from webhook! BMI is ' + computedBMI
]
}
}
]
}
}
res.status(200).send(replyBMI);
};
Here’s the result:
Moreover, here’s a sample code you can also use for deploying in Cloud Function:
index.js
exports.helloWorld = (req, res) => {
let fulfillmentResponse = {
"fulfillmentResponse": {
"messages": [{
"text": {
"text": [
"This is a sample response"
]
}
}]
}
};
res.status(200).send(fulfillmentResponse);
};
package.json
{
"name": "sample-http",
"version": "0.0.1"
}
Once you deployed the sample code, you can do the following to be able to use the webhook:
Go to Manage > Webhooks > Create
Add Display Name and Webhook URL(Trigger URL in Cloud Functions)
Click Save
Go to Build > Flow > Start Page
Select any Route and add the webhook
Test in Simulator
Result should be like this:

Cannot get image from ipfs in vuejs

I want to get an image from IPFS into my Vue/Nuxt project. I already import ipfs by 'npm i ipfs'. But when i run "cont node = Ipfs.create()". it show error
but this error doesn't always happen, many times it works and I can get the image normally. Has anyone ever encountered this situation and have a solution?
async downloadImg () {
const node = await Ipfs.create()
const { agentVersion, id } = await node.id()
this.agentVersion = agentVersion
this.id = id
const cid = '/ipfs/QmY2dod6X7GFmqnQ6qCBiaeNxJWa3CYQaxEjGUfL5CqMAj'
// load the raw data from js-ipfs (>=0.40.0)
const bufs = []
const a = node.cat(cid)
for await (const buf of node.cat(cid)) {
bufs.push(buf)
}
const data = Buffer.concat(bufs)
const blob = new Blob([data], { type: 'image/jpg' })
this.imageSrc = window.URL.createObjectURL(blob)
},
If am I true it can depends on the web protocol which you use, if you use https its work and in other protocol not !
The web crypto API is only available on pages accessed via https. If you're seeing that message, you are probably accessing the page via plain http.
Follow the github link at the top of the stack trace for more explanation and solutions.

how can I properly invoke a google cloud function inside another function [duplicate]

I am using a Cloud Function to call another Cloud Function on the free spark tier.
Is there a special way to call another Cloud Function? Or do you just use a standard http request?
I have tried calling the other function directly like so:
exports.purchaseTicket = functions.https.onRequest((req, res) => {
fetch('https://us-central1-functions-****.cloudfunctions.net/validate')
.then(response => response.json())
.then(json => res.status(201).json(json))
})
But I get the error
FetchError: request to
https://us-central1-functions-****.cloudfunctions.net/validate
failed, reason: getaddrinfo ENOTFOUND
us-central1-functions-*****.cloudfunctions.net
us-central1-functions-*****.cloudfunctions.net:443
Which sounds like firebase is blocking the connection, despite it being a google owned, and therefore it shouldn't be locked
the Spark plan only allows outbound network requests to Google owned
services.
How can I make use a Cloud Function to call another Cloud Function?
You don't need to go through the trouble of invoking some shared functionality via a whole new HTTPS call. You can simply abstract away the common bits of code into a regular javascript function that gets called by either one. For example, you could modify the template helloWorld function like this:
var functions = require('firebase-functions');
exports.helloWorld = functions.https.onRequest((request, response) => {
common(response)
})
exports.helloWorld2 = functions.https.onRequest((request, response) => {
common(response)
})
function common(response) {
response.send("Hello from a regular old function!");
}
These two functions will do exactly the same thing, but with different endpoints.
To answer the question, you can do an https request to call another cloud function:
export const callCloudFunction = async (functionName: string, data: {} = {}) => {
let url = `https://us-central1-${config.firebase.projectId}.cloudfunctions.net/${functionName}`
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ data }),
})
}
(Note we are using the npm package 'node-fetch' as our fetch implementation.)
And then simply call it:
callCloudFunction('search', { query: 'yo' })
There are legitimate reasons to do this. We used this to ping our search cloud function every minute and keep it running. This greatly lowers response latency for a few dollars a year.
It's possible to invoke another Google Cloud Function over HTTP by including an authorization token. It requires a primary HTTP request to calculate the token, which you then use when you call the actual Google Cloud Function that you want to run.
https://cloud.google.com/functions/docs/securing/authenticating#function-to-function
const {get} = require('axios');
// TODO(developer): set these values
const REGION = 'us-central1';
const PROJECT_ID = 'my-project-id';
const RECEIVING_FUNCTION = 'myFunction';
// Constants for setting up metadata server request
// See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
const functionURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
const metadataServerURL =
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenUrl = metadataServerURL + functionURL;
exports.callingFunction = async (req, res) => {
// Fetch the token
const tokenResponse = await get(tokenUrl, {
headers: {
'Metadata-Flavor': 'Google',
},
});
const token = tokenResponse.data;
// Provide the token in the request to the receiving function
try {
const functionResponse = await get(functionURL, {
headers: {Authorization: `bearer ${token}`},
});
res.status(200).send(functionResponse.data);
} catch (err) {
console.error(err);
res.status(500).send('An error occurred! See logs for more details.');
}
};
October 2021 Update: You should not need to do this from a local development environment, thank you Aman James for clarifying this
Despite of the question tag and other answers concern the javascript I want to share the python example as it reflects the title and also authentification aspect mentioned in the question.
Google Cloud Function provide REST API interface what incluse call method that can be used in another Cloud Function.
Although the documentation mention using Google-provided client libraries there is still non one for Cloud Function on Python.
And instead you need to use general Google API Client Libraries. [This is the python one].3
Probably, the main difficulties while using this approach is an understanding of authentification process.
Generally you need provide two things to build a client service:
credentials ans scopes.
The simpliest way to get credentials is relay on Application Default Credentials (ADC) library. The rigth documentation about that are:
https://cloud.google.com/docs/authentication/production
https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md
The place where to get scopes is the each REST API function documentation page.
Like, OAuth scope: https://www.googleapis.com/auth/cloud-platform
The complete code example of calling 'hello-world' clound fucntion is below.
Before run:
Create default Cloud Function on GCP in your project.
Keep and notice the default service account to use
Keep the default body.
Notice the project_id, function name, location where you deploy function.
If you will call function outside Cloud Function environment (locally for instance) setup the environment variable GOOGLE_APPLICATION_CREDENTIALS according the doc mentioned above
If you will call actualy from another Cloud Function you don't need to configure credentials at all.
from googleapiclient.discovery import build
from googleapiclient.discovery_cache.base import Cache
import google.auth
import pprint as pp
def get_cloud_function_api_service():
class MemoryCache(Cache):
_CACHE = {}
def get(self, url):
return MemoryCache._CACHE.get(url)
def set(self, url, content):
MemoryCache._CACHE[url] = content
scopes = ['https://www.googleapis.com/auth/cloud-platform']
# If the environment variable GOOGLE_APPLICATION_CREDENTIALS is set,
# ADC uses the service account file that the variable points to.
#
# If the environment variable GOOGLE_APPLICATION_CREDENTIALS isn't set,
# ADC uses the default service account that Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run,
# and Cloud Functions provide
#
# see more on https://cloud.google.com/docs/authentication/production
credentials, project_id = google.auth.default(scopes)
service = build('cloudfunctions', 'v1', credentials=credentials, cache=MemoryCache())
return service
google_api_service = get_cloud_function_api_service()
name = 'projects/{project_id}/locations/us-central1/functions/function-1'
body = {
'data': '{ "message": "It is awesome, you are develop on Stack Overflow language!"}' # json passed as a string
}
result_call = google_api_service.projects().locations().functions().call(name=name, body=body).execute()
pp.pprint(result_call)
# expected out out is:
# {'executionId': '3h4c8cb1kwe2', 'result': 'It is awesome, you are develop on Stack Overflow language!'}
These suggestions don't seem to work anymore.
To get this to work for me, I made calls from the client side using httpsCallable and imported the requests into postman. There were some other links to https://firebase.google.com/docs/functions/callable-reference there were helpful. But determining where the information was available took a bit of figuring out.
I wrote everything down here as it takes a bit of explaining and some examples.
https://www.tiftonpartners.com/post/call-google-cloud-function-from-another-cloud-function
Here's an inline version for the 'url' might expire.
This 'should' work, it's not tested but based off of what I wrote and tested for my own application.
module.exports = function(name,context) {
const {protocol,headers} = context.rawRequest;
const host = headers['x-forwardedfor-host'] || headers.host;
// there will be two different paths for
// production and development
const url = `${protocol}://${host}/${name}`;
const method = 'post';
const auth = headers.authorization;
return (...rest) => {
const data = JSON.stringify({data:rest});
const config = {
method, url, data,
headers: {
'Content-Type': 'application/json',
'Authorization': auth,
'Connection': 'keep-alive',
'Pragma': 'no-cache,
'Cache-control': 'no-cache',
}
};
try {
const {data:{result}} = await axios(config);
return result;
} catch(e) {
throw e;
}
}
}
This is how you would call this function.
const crud = httpsCallable('crud',context);
return await crud('read',...data);
context you get from the google cloud entry point and is the most important piece, it contains the JWT token needed to make the subsequent call to your cloud function (in my example its crud)
To define the other httpsCallable endpoint you would write an export statement as follows
exports.crud = functions.https.onCall(async (data, context) => {})
It should work just like magic.
Hopefully this helps.
I found a combination of two of the methods works best
const anprURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
const metadataServerURL =
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenUrl = metadataServerURL + anprURL;
// Fetch the token
const tokenResponse = await fetch(tokenUrl, {
method: "GET"
headers: {
'Metadata-Flavor': 'Google',
},
});
const token = await tokenResponse.text();
const functionResponse = await fetch(anprURL, {
method: 'POST',
headers: {
"Authorization": `bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({"imageUrl": url}),
});
// Convert the response to text
const responseText = await functionResponse.text();
// Convert from text to json
const reponseJson = JSON.parse(responseText);
Extending the Shea Hunter Belsky's answer I would love to inform you that the call to the metatdata server of google to fetch the authorization token would not work from local machine
Since fetch is not readily available in Node.JS and my project was already using the axios library, I did it like this:
const url = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${FUNCTION_NAME}`;
const headers = {
'Content-Type': 'application/json',
};
const response = await axios.post(url, { data: YOUR_DATA }, { headers });

How can I have multiple API endpoints for one Google Cloud Function?

I have a Google Cloud Function which contains multiple modules to be invoked on different paths.
I am using the serverless framework to deploy my functions, but it has the limitation of only one path per function.
I want to use multiple paths in one function just like we can in the AWS serverless framework.
Suppose a user cloud function will have two paths /user/add as well as /user/remove; both the paths should invoke the same function.
Something like this:
serverless.yml
functions:
user:
handler: handle
events:
- http: user/add
- http: user/remove
How can I have multiple API endpoints for one GCF?
Yes, indeed there is no actual REST service backing up Google Cloud Functions. It uses out of the box HTTP triggers.
To hustle the way around, I'm using my request payload to determine which action to perform. In the body, I'm adding a key named "path".
For example, consider the Function USER.
To add a user:
{
"path":"add",
"body":{
"first":"Jhon",
"last":"Doe"
}
}
To remove a user:
{
"path":"remove",
"body":{
"first":"Jhon",
"last":"Doe"
}
}
If your operations are purely CRUD, you can use request.method which offers verbs like GET, POST, PUT, DELETE to determine operations.
You could use Firebase Hosting to rewriting URLs.
In your firebase.json file:
"hosting": {
"rewrites": [
{
"source": "/api/v1/your/path/here",
"function": "your_path_here"
}
]
}
Keep in mind this is a workaround and it has a major drawback: you will pay for double hit. Consider this if your app has to scale.
You can write your functions in different runtimes. The Node.js runtime uses the Express framework. So you can use its router to build different routes within a single function.
Add the dependency
npm install express#4.17.1
The following example is using typescript. Follow these guidelines to initiate a typescript project.
// index.ts
import { HttpFunction } from '#google-cloud/functions-framework';
import * as express from 'express';
import foo from './foo';
const app = express();
const router = express.Router();
app.use('/foo', foo)
const index: HttpFunction = (req, res) => {
res.send('Hello from the index route...');
};
router.get('', index)
app.use('/*', router)
export const api = app
// foo.ts
import { HttpFunction } from '#google-cloud/functions-framework';
import * as express from 'express';
const router = express.Router();
const foo: HttpFunction = (req, res) => {
res.send('Hello from the foo route...');
};
router.get('', foo)
export default router;
to deploy run:
gcloud functions deploy YOUR_NAME \
--runtime nodejs16 \
--trigger-http \
--entry-point api \
--allow-unauthenticated
Currently, in google allows only one event definition per function is supported. For more
Express can be installed with npm i express, then imported and used more or less as normal to handle routing:
const express = require("express");
const app = express();
// enable CORS if desired
app.use((req, res, next) => {
res.set("Access-Control-Allow-Origin", "*");
next();
});
app.get("/", (req, res) => {
res.send("hello world");
});
exports.example = app; // `example` is whatever your GCF entrypoint is
If Express isn't an option for some reason or the use case is very simple, a custom router may suffice.
If parameters or wildcards are involved, consider using route-parser. A deleted answer suggested this app as an example.
The Express request object has a few useful parameters you can take advantage of:
req.method which gives the HTTP verb
req.path which gives the path without the query string
req.query object of the parsed key-value query string
req.body the parsed JSON body
Here's a simple proof-of-concept to illustrate:
const routes = {
GET: {
"/": (req, res) => {
const name = (req.query.name || "world");
res.send(`<!DOCTYPE html>
<html lang="en"><body><h1>
hello ${name.replace(/[\W\s]/g, "")}
</h1></body></html>
`);
},
},
POST: {
"/user/add": (req, res) => { // TODO stub
res.json({
message: "user added",
user: req.body.user
});
},
"/user/remove": (req, res) => { // TODO stub
res.json({message: "user removed"});
},
},
};
exports.example = (req, res) => {
if (routes[req.method] && routes[req.method][req.path]) {
return routes[req.method][req.path](req, res);
}
res.status(404).send({
error: `${req.method}: '${req.path}' not found`
});
};
Usage:
$ curl https://us-east1-foo-1234.cloudfunctions.net/example?name=bob
<!DOCTYPE html>
<html lang="en"><body><h1>
hello bob
</h1></body></html>
$ curl -X POST -H "Content-Type: application/json" --data '{"user": "bob"}' \
> https://us-east1-foo-1234.cloudfunctions.net/example/user/add
{"message":"user added","user":"bob"}
If you run into trouble with CORS and/or preflight issues, see Google Cloud Functions enable CORS?

feathers.js -> Authentication token missing

I have a feathters.js application and now I want to secure the create and update hooks. I use a socket.io client and currently am going for JWT. I have added what I think I needed to add but am getting Error: Authentication token missing and Error Authenticating. The later I understand for that is from my code. I have a backend / frontend situation
So this is what I've implemented so far.
File: backend\backend.js (called in backend\index.js for the configuration of the app)
'use strict';
const path = require('path');
const serveStatic = require('feathers').static;
const favicon = require('serve-favicon');
const compress = require('compression');
const cors = require('cors');
const feathers = require('feathers');
const configuration = require('feathers-configuration');
const authentication = require('feathers-authentication');
const hooks = require('feathers-hooks');
const rest = require('feathers-rest');
const bodyParser = require('body-parser');
const socketio = require('feathers-socketio');
const middleware = require('./middleware/index');
const services = require('./services/index');
const appFeathers = feathers();
appFeathers.configure(configuration(path.join(__dirname, '..')));
appFeathers.use(compress())
.options('*', cors())
.use(cors())
.use(favicon(path.join(appFeathers.get('public'), 'favicon.ico')))
.use('/', serveStatic(appFeathers.get('public')))
.use(bodyParser.json())
.use(bodyParser.urlencoded({extended: true}))
.configure(hooks())
.configure(rest())
.configure(socketio())
.configure(services)
.configure(middleware)
.configure(authentication());
module.exports = appFeathers;
File: backend\config\default.json
{
"host": "localhost",
"port": 3001,
"mysql_connection": "mysql://CONNECTION_STRING",
"public": "../public/",
"auth": {
"idField": "id",
"token": {
"secret": "SECRET_KEY"
},
"local": {}
}
}
In a working component of the frontend:
<template>
<div class="vttIndex">
idnex.vue
todo: eagle.js slideshow
todo: first info
<ul>
<li v-for="message in listMessages">
{{ message }}
</li>
</ul>
</div>
</template>
<script>
import feathers from 'feathers/client';
import socketio from 'feathers-socketio/client';
import hooks from 'feathers-hooks';
import io from 'socket.io-client';
import authentication from 'feathers-authentication/client';
import * as process from "../nuxt.config";
const vttSocket = io(process.env.backendUrl);
const vttFeathers = feathers()
.configure(socketio(vttSocket))
.configure(hooks())
.configure(authentication());
const serviceMessage = vttFeathers.service('messages');
vttFeathers.authenticate({
type: 'token',
'token ': 'SECRET_KEY'
}).then(function(result){
console.log('Authenticated!', result);
}).catch(function(error){
console.error('Error authenticating!', error);
});
export default {
layout: 'default',
data: function() {
return {
listMessages: []
}
},
mounted: function() {
serviceMessage.find().then(page => {
this.listMessages = page.data;
});
serviceMessage.on('created', (serviceMessage) => {
this.listMessages.push(serviceMessage);
});
}
}
</script>
As token, I have the secret key of the backend json file. As you see, now I only try to log console messages. It is doing something for my error message is coming from there.
Question
Where am I missing what to have this functioning?
Goal
Just in case it's needed. My goal is for all 'public' data to be select with a token in my client and then an admin section maybe with 0auth. So the general 'SELECT' stuff is secured through a token instead of no authentication at all.
Solution
Okay I solved it, sort of. I first needed to create a user. Then I needed to do a local login with the user. That returns a token. If I use that, then there is no problem at all.
To use a token, you must first make sure it is generated. I was using the secret key as token what isn't correct. When you first athenticate with the 'local' type (default email and password) it will create a token and that is what you could then use with the method 'token'