I am creating a google function. However, when I try to deploy to Google Cloud Platform, I am getting this error
ERROR: (gcloud.beta.functions.deploy) OperationError: code=3, message=Function load error: Code in file index.js can't be loaded.
Did you list all required modules in the package.json dependencies?
Detailed stack trace: Error: Cannot find module 'request'
How do I upload/install the 'request' library in google cloud platform?
Code Snippet
'use strict';
const https = require('https');
const host = 'https://www.example.com';
const clientId = 'qpopMIGtVdeIdVk3oEtr2LGbn8vTeTWz';
const clientSecret = 'eUnsWQ8y3AuiFHJu';
const grant_type = 'client_credentials';
const resource = 'b.microsoft.com/4fa4b4a7-d34f-49af-8781-c8b39f0cf770';
const request = require("request");
exports.oauthtoken = (req, res) => {
// Call the Apigee API
callGetOAuthToken().then((output) => {
// Return the results from the APigee to DialogFlow
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': output, 'displayText': output }));
}).catch((error) => {
// If there is an error let the user know
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': error, 'displayText': error }));
});
};
function callGetOAuthToken () {
return new Promise((resolve, reject) => {
let path = '/customers/v1/accesstoken';
var authHeader = Buffer.from(clientId + ':' + clientSecret).toString('base64');
var post_options = {
url: host + path,
method: 'POST',
headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + authHeader,
'grant_type':grant_type
}
};
// Make the HTTP request to get the weather
request(post_options, function(err, res, body) {
let output = JSON.parse(body);
console.log(output);
resolve(output);
});
});
}
-Alan-
Read through the Google Cloud documentation regarding dependencies:
https://cloud.google.com/functions/docs/writing/dependencies
List the 'request' module as a dependency in your package.json file if using the gcloud CLI.
Or, run 'npm install --save request' in the folder containing your cloud function and upload your pre-installed dependencies as part of your ZIP file.
Related
I am using React + NodeJS & Axios but have been trying to send a post request but experiencing difficulties.
The request seems to be posting successfully, but all actions at the nodejs server is returning in the "undefined" data value, even if the data is passed successfully shown in the console.
REACT
const fireAction = (data1, data2) => {
const data = JSON.stringify({data1, data2})
const url = `http://localhost:5000/data/corr/fire`;
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'AUTHCODE',
}
}
axios.post(url, data, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
fireAction("Oklahoma", "Small apartment")
NODE
app.post('/data/corr/fire', async (req, res) => {
try {
const data = req.body.data1;
console.log(data)
} catch(e) {
res.send({success: "none", error: e.message})
}
});
Result of node: "undefined"
I have added the following body parser:
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
I am not sure why this error is happening. I see there is similar questions to mine: however none of them are applicable as I'm using both express and body parser which is already suggested.
You're POSTing JSON with a content-type meant for forms. There's no need to manually set content-type if you're sending JSON, but if you want to manually override it, you can use 'Content-Type': 'application/json', and access the response in your route with req.body. If it does need to be formencoded, you'll need to build the form:
const params = new URLSearchParams();
params.append('data1', data1);
params.append('data2', data2);
axios.post(url, params, config);
I have 2 functions in the same google cloud functions project (myfunction1 and myfunction2.
exports.myfunction1 = async (req, res) => {
await axios({
method: 'post',
url: 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2',
timeout: 15000,
headers: {
'Content-Type': 'application/json',
},
data: myjson
}).then(response => {
console.log(JSON.stringify(response.data));
}).catch(err => {
console.error("catch error");
console.error(err);
})
}
It is works fine, but only if I configure invokers permission for allUsers. If I remove this permission, e receive 403 code error. Not sounds good keep this permisson activate, because the function is exposed. I tried solve with this link and this link, but, no sucess.
Edit1:
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const targetAudience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2'
const url = '??????????';
async function request() {
console.info('request ${url} with target audience ${targetAudience}');
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({url});
console.info(res.data);
}
I'm trying using this code, but, who is const url?
You must perform service to service authentication. You can find a great tutorial in the Cloud Run page (ok you use Cloud Functions but the underlying infrastructure is the same and the doc is better).
You also have to be aware about the Functions identity and how to change them (or to grant the current service account the correct permission)
let audience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2';
let token_request_url = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=' + audience;
var token_response = await axios.get(token_request_url, { headers: {'Metadata-Flavor': 'Google'} });
let token_auth = token_response.data;
axios({
method: 'post',
url: audience,
timeout: 15000,
headers: {
'Authorization': "Bearer " + token_auth
},
data: myJSON
}).catch(err => {
console.error(err);
});
I have an Angular Universal app. I am trying to access an external API, but doing it directly through the HttpClient generates a Cors error. In development, I successfully used a proxy to make the call. I am trying to implement a proxy in production by creating a route on my express server that will swap in the appropriate external API route. I am having trouble seeing anything online that can help with this particular situation. I seem to have set up the route ok. I'm getting a 200 ok error but no data is being sent. Can anyone help?
server.ts
app.route('/api/book').get((req, res) => {
https.get('https://api2.isbndb.com/book/' + req, (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
res.send(res.json(data)
);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
});
You can simply enable cors from your server side like this.
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
For better understanding or to configure cors() url through.
can see here.
As by default it will call the options method to check the permission for the user to access that end points.
or you can use below example from client side api call,
let data = { name: 'Peter Parker', age: 34 };
const results = await fetch(
'http://localhost:3000/api/v1/results',
{
method: "post",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(data)
}
)
.then(res => res.json())
.then(res => {
return res;
})
.catch(error => {
error.response = {
status: 0,
statusText:
"Cannot connect. Please make sure you are connected to internet."
};
throw error;
});
I have set up a test end point here:
https://9wi46s5jzc.execute-api.us-east-1.amazonaws.com/test
For some baseline testing, I go to apitester.com and run two tests:
Firstly, a post request to: https://admin:password#9wi46s5jzc.execute-api.us-east-1.amazonaws.com/test (correct credentials) gives me output of:
{"isBase64Encoded":false,"statusCode":401,"headers":{"x-powered-by":"Express","content-type":"text/html; charset=utf-8","content-length":"0","etag":"W/\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\""},"body":""}
Secondly, a post request to: https://admin:BADPASSWORD#9wi46s5jzc.execute-api.us-east-1.amazonaws.com/test (incorrect credentials) gives me output of:
{"message":"Unauthorized"}
So they're the baseline tests for what should happen.
When I run the following code:
const request = require('request');
const url = 'https://admin:password#9wi46s5jzc.execute-api.us-east-1.amazonaws.com/test';
request.post(url, function(err, res, body) {
console.log("body", body);
});
I get:
body {"message":"Unauthorized"}
Why is this happening?
According to the docs:
https://github.com/request/request
this is the way to do basic authentication.
So I'm expecting correct authorization but I'm not getting it. What am I doing wrong?
You should try it using this :
const proxyUrl = 'https://admin:password#9wi46s5jzc.execute-api.us-east-1.amazonaws.com/test';
const proxyRequest = request.defaults({ 'proxy': proxyUrl});
const options = {
url: '...',
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer " + token // if this is how you handle auth and you already have the token
}
};
proxyRequest .get(options, function (error, response, body) {
if (error) {
next(error); // if you're using Express
}
console.log("body", body);
});
I have a node app that serves a react app as well as makes requests to Google Cloud Storage. The App works perfectly locally, but after I've deployed it to Heroku I get the following error whenever I make requests to any of my endpoints:
2017-05-26T21:53:34.426234+00:00 app[web.1]: app.post /upload_url Endpoint
2017-05-26T21:53:34.484393+00:00 app[web.1]: (node:34) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): RangeError: Invalid status code: 0
The first line is a console log to check if the endpoint is reached, the second line is the error. Here's the code for the /upload_url endpoint:
app.post('/upload_url', (req, res) => {
console.log("app.post /upload_url Endpoint");
var originalFileName = req.body["originalFileName"];
var buf = crypto.randomBytes(16);
var uniqueFilename = buf.toString('hex') + '_' + originalFileName;
var file = bucket.file(uniqueFilename);
file.createResumableUpload(function (err, uri) {
if (!err) {
var json = JSON.stringify({
uri: uri,
uniqueFilename: uniqueFilename,
originalFileName: originalFileName
});
res.writeHead(200)
res.end(json);
} else {
res.writeHead(err.code);
res.end();
}
});
});
This endpoint is called by the react front end with the following fetch call:
function upload(file) {
var data = new FormData();
data.append('file', file);
return fetch(`upload_url`,{
method: 'POST',
headers: new Headers({
"Content-Type": "application/json",
}),
body: JSON.stringify({
originalFileName: file.name
})
});
}
}
Again, this works fine in development but not after deploying to Heroku. I've tried Heroku's suggestion of adding concurrency to the app (detailed here) without any luck. Any thoughts or suggestions on how to solve this problem would be very much appreciated.
EDIT:
bucket is a google cloud bucket and is defined like this:
const gcs = require('#google-cloud/storage')({
projectId: 'my-project',
keyFilename: process.env.GCS_KEYFILE
});
var bucket = gcs.bucket('my-bucket');
ANSWER:
While this didn't solve my issue entirely, by handling response error codes more appropriately I was able to determine that my actual problem is related to google cloud authentication. Here's my updated upload_url endpoint:
file.createResumableUpload(function (err, uri) {
if (!err) {
var json = JSON.stringify({
uri: uri,
uniqueFilename: uniqueFilename,
originalFileName: originalFileName
});
res.writeHead(200)
res.end(json);
} else {
if (err.code >= 100 && err.code < 600){
console.error(err)
res.writeHead(err.code);
res.end();
}else{
console.error(err)
res.writeHead(500);
res.end();
}
}
});
});
Refer to this answer https://stackoverflow.com/a/38258590/4348875, make sure err.code is a valid HTTP status code.