I would like to be able to get usage data from Twilio from within an AWS Lambda function. I am following the examples on twilio's REST API page but am not having any success. I am using version 3 of Twilio's Node Helper Library. Below is the code that I have in the handler:
'use strict';
exports.handler = async(request, context) => {
const accountSid = 'my account sid';
const authToken = 'my auth token';
const client = require('twilio')(accountSid, authToken);
client.usage.records.today.each(record => console.log(record.count));
};
The Lambda "feels" like it is at least trying to get the data from Twilio. It runs for ~10 seconds before ending without any errors. However I never get the 'here' message.
Thanks in advance, Scott
This is my Lambda code:
exports.handler = (event, context, callback) => {
// Your Account SID from www.twilio.com/console
const accountSid = process.env.TWILIO_ACCOUNT_SID;
// Your Auth Token from www.twilio.com/console
const authToken = process.env.TWILIO_AUTH_TOKEN;
// Import Twilio's Node Helper library
// Create an authenticated Twilio Client instance
const client = require('twilio')(accountSid, authToken);
client.usage.records.lastMonth.each(record => console.log('here'));
};
and this is what I see in the "Function code" section after I run the function (Status: Succeeded).
Related
I keep getting a 403 with the testcode below. Is it just me or is it overly complicated to call a function within the same project? I did some research here and here.
I've set the cloud function invoker on the default service account for both functions. And the allow internal traffic
So i have tried both codes below. The token is printed to the logs in the first function, so why do i still get a 403?
Script1:
const axios = require("axios");
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = async (req, res) => {
console.log(JSON.stringify(process.env));
const sample_api_url = `https://someRegionAndSomeProject.cloudfunctions.net/sample-api`;
const metadataServerURL =
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=";
const tokenUrl = metadataServerURL + sample_api_url;
// Fetch the token
const tokenResponse = await axios(tokenUrl, {
method: "GET",
headers: {
"Metadata-Flavor": "Google",
},
});
const token = tokenResponse.data;
console.log(token);
const functionResponse = await axios(sample_api_url, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = functionResponse.data;
console.log(data);
res.status(200).json({ token, data });
};
Script2:
const {GoogleAuth} = require('google-auth-library');
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = async (req, res) => {
const url = 'https://someRegionAndSomeProject.cloudfunctions.net/sample-api';
const targetAudience = url;
const auth = new GoogleAuth();
const client = await auth.getIdTokenClient(targetAudience);
const response = await client.request({url});
res.status(200).json({data: response.data})
};
There is 2 types of security on Cloud Functions
Identity based security: if you deploy your Cloud Functions without the allow-unauthenticated parameter, you have to send a request with an Authorization: bearer <token> header, with token is an identity token which has, at least, the cloud functions invoker role. You can also add the allUsers user with the cloud functions invoker role to make the function publicly reachable (no security header required)
Network based security: this time, only the request coming from your project VPCs or your VPC SC are allowed to access the Cloud Functions. If you try to reach the cloud functions from an unauthorized network, you get a 403 (your error).
You can combine the 2 security solutions if you want. In your code, you correctly add the security header. However, your request is rejected by the network check.
The solution is not so simple, not free, and I totally agree with you that this pattern should be simpler.
To achieve this, you must create a serverless VPC connector and attach it on your functions that perform the call. You also have to set the egress to ALL on that functions. That's all
The consequence are the following: The traffic originated from your function will be routed to your VPC thanks to the serverless VPC connector. The Cloud Functions URL being always a public URL, you have to set the egress to ALL, to route the traffic going to public URL through the serverless VPC connector.
Based on your post, I created a sample that worked for me. I created two GCP functions "func1" and "func2". I then determined how to call func2 from func1 where func2 is only exposed to be invoked by the service account identity which func1 runs as.
The final code for func1 is as follows:
const func2Url = 'https://us-central1-XXX.cloudfunctions.net/func2';
const targetAudience = func2Url;
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
async function request() {
console.info(`request ${func2Url} with target audience ${targetAudience}`);
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({url: func2Url});
console.info(res.data);
return res;
}
exports.func1 = async (req, res) => {
let message = `Hello from func1`;
try {
let response = await request();
res.status(200).send(`${message} + ${response.data}`);
}
catch(e) {
console.log(e);
res.status(500).send('failed');
}
};
The primary recipe used here is as described in the Google Docs here. I also found this medium article helpful.
I am using the Stripe/NodeJS library to generate a Stripe customer id when a user is created:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
exports.handler = async (event, context) => {
const newUser = JSON.parse(event.body);
const customer = await stripe.customers.create({
email: newUser.emailAddress,
});
return customer.id;
};
I thought that the code is pretty straightforward and almost a duplicate to the library's README example, however, the lambda execution hits the stripe.customers.create and never completes, the lambda eventually times out (the lambda is NodeJS 12.x).
Thoughts on what I am missing in order to get this to work?
I am attempting to authenticate a user to access various scopes in the user Gsuite. I can run the code locally but I cannot seem to get it accepted as a cloud function.
I have tried deploying with firebase and with gcloud. I have checked my eslint settings.
This code is coming from https://github.com/googleapis/google-api-nodejs-client/blob/master/README.md#oauth2-client
'use strict';
const fs = require('fs');
const path = require('path');
const http = require('http');
const url = require('url');
const opn = require('open');
const destroyer = require('server-destroy');
const {google} = require('googleapis');
/**
* To use OAuth2 authentication, we need access to a a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. To get these credentials for your application, visit https://console.cloud.google.com/apis/credentials.
*/
const keyPath = path.join(__dirname, 'credentials.json');
let keys = {redirect_uris: ['']};
if (fs.existsSync(keyPath)) {
keys = require(keyPath).web;
}
/**
* Create a new OAuth2 client with the configured keys.
*/
const oauth2Client = new google.auth.OAuth2(
keys.client_id,
keys.client_secret,
`http://localhost:3000/oauth2callback`
);
/**
* This is one of the many ways you can configure googleapis to use authentication credentials. In this method, we're setting a global reference for all APIs. Any other API you use here, like google.drive('v3'), will now use this auth client. You can also override the auth client at the service and method call levels.
*/
google.options({auth: oauth2Client});
const scopes = ['https://www.googleapis.com/auth/documents'];
/**
* Open an http server to accept the oauth callback. In this simple example, the only request to our webserver is to /callback?code=<code>
*/
async function authenticate(){
// grab the url that will be used for authorization
const authorizeUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: scopes
});
const server = http.createServer(async (req, res) => {
try {
if (req.url.indexOf('/oauth2callback') > -1) {
const qs = new url.URL(req.url, 'http://localhost:3000').searchParams;
res.end('Authentication successful! Please return to the console.');
server.destroy();
const {tokens} = await oauth2Client.getToken(qs.get('code'));
oauth2Client.credentials = tokens; // eslint-disable-line require-atomic-updates
resolve(oauth2Client);
}
} catch (e) {
reject(e);
}
})
.listen(3000, () => {
// open the browser to the authorize url to start the workflow
opn(authorizeUrl, {wait: false}).then(cp => cp.unref())
.catch(
error => {
console.log(error);
});
});
destroyer(server)
.then(client => runSample(client)).catch(
error => {
console.log(error);
});
};
module.exports.authenticate=authenticate;
async function runSample(client) {
// retrieve user profile
console.log(client);
const docs = google.docs({
version: 'v1',
auth: client
});
const createResponse = await docs.documents.create({
requestBody: {
title: 'Your new document!',
},
});
}
I expect it to load as a cloud function to firebase or gcloud.
However:
Firebase returns "Deploy complete" but it never shows in the functions.
gcloud returns "SyntaxError: Unexpected token function" with the word function indicated in "async function authenticate(){"
I'm new to node.js and may be missing something really obvious to others.
You will never get User Credentials (Client ID/Client Secret) to work
in Cloud Functions (meaning authenticate and create credentials).
OAuth requires a web browser and a human. Neither one exists in Cloud
Functions. Use a Service Account instead. – John Hanley
I am using the dialogflow enterprise edition bot,the issue is about receiving responses from rest api(node.js sdk).Recently we have shifted from dialogflow standard edition to Enterprise edition. We have referred and used the following code in this link
https://www.npmjs.com/package/dialogflow
We have created our own cloud function using rest Api which takes the user answer as the request and send the fulfillment text(questions in that particular intent) as the response to the user.When multiple request calls happen the the cloud function is not showing proper response which means
When user(Android/IOS) "A" started answering the bot then the cloud function triggering gets started and it send the questions as the response to the user but when the multiple users started answering the bot, due to multiple calls for the cloud function, the questions which are displayed to the one user are not going to be displayed to the other user say "B". Please help us in handling the multiple calls for node.js sdk
https://i.stack.imgur.com/BVAci.png
This is the view of an intent in dialogflow.
https://i.stack.imgur.com/aRAr6.png ,
https://i.stack.imgur.com/oMp0d.png
https://i.stack.imgur.com/T7Jo7.png
https://i.stack.imgur.com/7U3Rb.png
https://i.stack.imgur.com/EwWSo.png
Above five screenshots represent the first time when the user triggers
the cloud function, and the 1st question is displayed. When we answer the
first question and submit the answer, next question is not displayed.(I am
getting the empty response).
https://i.stack.imgur.com/rLI2I.png ,
https://i.stack.imgur.com/T5wZL.png
These screenshots represent the empty response after first question is answered.
const functions = require('firebase-functions');
const dialogflow = require('dialogflow');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
var db = admin.firestore();
const {WebhookClient} = require('dialogflow-fulfillment');
exports.fulfillmenttext = functions.https.onRequest((req,res) =>{
runSample();
async function runSample() {
const projectId = 'bodha-192606';
const sessionId = uuid.v4();
const answer = req.body.Text;
console.log("Text said by the user",answer);
const languageCode = 'en-US';
const credentials = {
client_email: 'xxxx ',
Private_key:'xxxx ',
};
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient({
projectId,
credentials,
});
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: answer,
languageCode,
},
},
};
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
let action = result.action;
console.log("action is"+action);
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
res.status(200).send({"question":result.fulfillmentText});
} else {
console.log(` No intent matched.`);
res.status(400).send("No Intent matched");
}
}
});
It looks like you are using the same session ID for all of your clients. I'm not certain, but I can certainly understand that if Dialogflow gets two requests for the same session at the same time, that it can mix up the replies.
You should be generating a new session for each of your clients - possibly by putting something into an HTTP session (aha!) cookie when they first connect and continuing to use that during the session.
I'm beginning writing code with Cloud Functions with Firebase.
Of the functions below, testCreateUserAccount succeeds.
testLogin fails with a Type Error at runtime, stating "signInWithEmailAndPassword is not a function"
From what I have seen in the documentation, createUser is under the same class as signInWithEmailAndPassword, so its not clear to me why attempting to call signInWithEmailAndPassword would fail. Any ideas? Thanks!
"use strict";
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.testCreateUserAccount = functions.https.onRequest ((req, res) => {
var email = "joe#example.com";
var password = "joejoe";
admin.auth().createUser({
email: email,
password: password,
disabled: false
});
} );
exports.testLogin = functions.https.onRequest ((req, res) => {
var email = "joe#example.com";
var password = "joejoe";
admin.auth().signInWithEmailAndPassword(email, password);
} );
You used admin.auth().signInWithEmailAndPassword(email, password) on server side, you must use it on client side.
Now you could use the identitytoolkit's rest endpoint:
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]
Here you have the documentation:
https://firebase.google.com/docs/reference/rest/auth/
You can use Identity toolkit REST API like this then response with idToken. IdToken can be use to verify an account using admin.auth().verifyIdToken(idToken) in Cloud Function.
https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=YOUR_PROJECT_API_KEY_THAT_ALLOWS_IDENTITY_TOOLKIT_SERVICE&email=test#gmail.com&password=samplepass&returnSecureToken=true