How to publish to iot stream from lambda - aws-lambda

I keep having a timeout in my lambda function when I try to call the iotData publish function. Code below. It always times out without error. This function works from the sam local command line. I imagine this is a permissions error with lambda. The strange thing is I've given permissions for IoT, Kinesis and SNS already to this lambda function but nothing is working.
'use strict';
console.log('Loading function');
require('dotenv').config();
const {Pool} = require('pg');
const pool = new Pool();
const AWS = require('aws-sdk');
console.log("finished loading");
/** * Provide an event that contains the following keys:
* * * - resource: API Gateway resource for event
* * - path: path of the HTTPS request to the microservices API call
* * - httpMethod: HTTP method of the HTTPS request from microservices API call
* * - headers: HTTP headers for the HTTPS request from microservices API call
* * - queryStringParameters: query parameters of the HTTPS request from microservices API call
* * - pathParameters: path parameters of the HTTPS request from microservices API call
* * - stageVariables: API Gateway stage variables, if applicable
* * - body: body of the HTTPS request from the microservices API call
* */
exports.handler = function(event, context, callback) {
console.log("starting");
let _response = "";
context.callbackWaitsForEmptyEventLoop = false;
if(event.httpMethod==="POST" && event.resource==="/pings"){
var body = JSON.parse(event.body);
console.log("here2");
pool.query("SELECT name from pings where test = decode($1,'hex');",[body.bid], (err,res)=>{
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response, null);
}
console.log("here3");
var iotdata = new AWS.IotData({endpoint:'XXXXXXX.iot.us-east-1.amazonaws.com'});
const publishParams = {
topic: body.topic,
payload: Buffer.from(JSON.stringify({
message: "Welcome to "+res.rows[0].name+" house"
}), 'utf8'),
qos: 0
}
console.log("here4");
iotdata.publish(publishParams, function(err, data) {
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response,null);
}
_response = buildOutput(200, {message: "success"});
callback(null, _response);
});
});
} else {
_response = buildOutput(500, {
message:"path not found"
});
callback(_response,null);
}
};
/* Utility function to build HTTP response for the microservices output */
function buildOutput(statusCode, data) {
let _response = {
statusCode: statusCode,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(data)
};
return _response;
}
policy
{
"Sid": "",
"Effect": "Allow",
"Action": [
"iot:*"
],
"Resource": "*"
},
UPDATE:
I attempted to give the lambda function admin access temporarily and that did not even work.

This is how you can post on MQTT topic directly from lambda function.
Use the code written in Node.js 10.x
var AWS = require('aws-sdk');
const IOT_ENDPOINT = "yourIoTentPoint.iot.region.amazonaws.com"
var iotdata = new AWS.IotData({endpoint:IOT_ENDPOINT});
exports.handler = function(event, context, callback) {
var params = {
topic: 'my/topic',
payload: 'This is my menssage from Lambda function.',
qos: 1
};
iotdata.publish(params, function(err, data){
if(err){
callback(err, null);
}
else{
callback(null, {"published_message": params.payload, "topic": params.topic});
}
});
};
Also, you can check the message sent by subscribing on the topic my/topic through IoT Core>Test on AWS Console.

Related

AWS API gateway websocket receives messages inconsistently

I have a websocket in api gateway connected to a lambda that looks like this:
const AWS = require('aws-sdk');
const amqp = require('amqplib');
const api = new AWS.ApiGatewayManagementApi({
endpoint: 'MY_ENDPOINT',
});
async function sendMsgToApp(response, connectionId) {
console.log('=========== posting reply');
const params = {
ConnectionId: connectionId,
Data: Buffer.from(response),
};
return api.postToConnection(params).promise();
}
let rmqServerUrl =
'MY_RMQ_SERVER_URL';
let rmqServerConn = null;
exports.handler = async event => {
console.log('websocket event:', event);
const { routeKey: route, connectionId } = event.requestContext;
switch (route) {
case '$connect':
console.log('user connected');
const creds = event.queryStringParameters.x;
console.log('============ x.length:', creds.length);
const decodedCreds = Buffer.from(creds, 'base64').toString('utf-8');
try {
const conn = await amqp.connect(
`amqps://${decodedCreds}#${rmqServerUrl}`
);
const channel = await conn.createChannel();
console.log('============ created channel successfully:');
rmqServerConn = conn;
const [userId] = decodedCreds.split(':');
const { queue } = await channel.assertQueue(userId, {
durable: true,
autoDelete: false,
});
console.log('============ userId:', userId, 'queue:', queue);
channel.consume(queue, msg => {
console.log('========== msg:', msg);
const { content } = msg;
const msgString = content.toString('utf-8');
console.log('========== msgString:', msgString);
sendMsgToApp(msgString, connectionId)
.then(res => {
console.log(
'================= sent queued message to the app, will ack, outcome:',
res
);
try {
channel.ack(msg);
} catch (e) {
console.log(
'================= error acking message:',
e
);
}
})
.catch(e => {
console.log(
'================= error sending queued message to the app, will not ack, error:',
e
);
});
});
} catch (e) {
console.log(
'=========== error initializing amqp connection',
e
);
if (rmqServerConn) {
await rmqServerConn.close();
}
const response = {
statusCode: 401,
body: JSON.stringify('failed auth!'),
};
return response;
}
break;
case '$disconnect':
console.log('user disconnected');
if (rmqServerConn) {
await rmqServerConn.close();
}
break;
case 'message':
console.log('message route');
await sendMsgToApp('test', connectionId);
break;
default:
console.log('unknown route', route);
break;
}
const response = {
statusCode: 200,
body: JSON.stringify('Hello from websocket Lambda!'),
};
return response;
};
The amqp connection is for a rabbitmq server that's provisioned by amazonmq. The problem I have is that messages published to the queue either do not show up at all in the .consume callback, or they only show up after the websocket is disconnected and reconnected. Essentially they're missing until a point much later after which they show up unexpectedly. That's within the websocket. Even when they do show up, they don't get sent to the client (app in this case) that's connected to the websocket. What could be the problem here?
The problem here is that I had the wrong idea about how API Gateway's websockets work. API gateway maintains the websocket connection but not the lambda itself. I put my .consume subscription logic inside the lambda, which doesn't work because the lambda runs and terminates instead of being kept alive. A better method would be to make the queue an event source for the lambda. However this also didn't work for me because it requires you to know your queues when setting up the lambda. My queues are dynamically created so that violated the requirement. I ended up standing up a rmq server on a vps.

How to create a shared drive with Google Drive API (node.js)

I'm trying to make a script that makes a shared drive from the google drive api but right now I'm getting an error inside the createSharedDrive(auth) function where it says
drive.drives.create
as it's saying the 'create' function is undefined. Not sure how to fix it. The google drive api documentation doesn't show the entire script to create a shared drive.
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const { uuid } = require('uuidv4');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
//authorize(JSON.parse(content), listFiles);
authorize(JSON.parse(content), createSharedDrive);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
resource: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}
The create method does not contains any "resource" property. It contains a requestId property and a requestBody property; Therefore, your request should look like this:
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
requestBody: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}

calling ApiGatewayManagementApi.postToConnection() gives me 500 internal server error?

so I am trying to develop an aws websocket function using lambda. But it seems that whenever I try to call "postToConnection" it just gives me 500 internal server error.
Cloud watch also doesn't logs the error that I am receiving.
And what I'm receiving on the terminal once I send the message is this:
"{"message": "Internal server error", "connectionId":"xxx", "requestId":"xxx"}"
(Which doesn't give me any information at all)
This is my whole code on the lambda function.
var AWS = require('aws-sdk');
AWS.config.update({ region: "us-west-2" });
var DDB = new AWS.DynamoDB({ apiVersion: "2012-10-08" });
require('aws-sdk/clients/apigatewaymanagementapi');
exports.handler = function (event, context, callback) {
var url_handler = event.requestContext.domainName + "/" + event.requestContext.stage;
// var params = event.requestContext;
// console.log(params);
var scanParams = {
TableName: "tbl-web-socket-connection",
ProjectionExpression: "id"
};
DDB.scan(scanParams, function (err, data) {
// callback(null, {
// statusCode: 200,
// body: "Data send to"
// });
if (err) {
callback(null, {
statusCode: 500,
body: JSON.stringify(err)
});
} else {
var apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: "2018-11-29",
endpoint: event.requestContext.domainName + "/" + event.requestContext.stage
});
var postParams = {
Data: JSON.parse(event.body).data
};
var count = 0;
data.Items.forEach(function (element) {
postParams.ConnectionId = element.id.S;
console.log(postParams);
apigwManagementApi.postToConnection(postParams, function (err, data) {
if (err) {
// API Gateway returns a status of 410 GONE when the connection is no
// longer available. If this happens, we simply delete the identifier
// from our DynamoDB table.
if (err.statusCode === 410) {
console.log("Found stale connection, deleting " + postParams.connectionId);
DDB.deleteItem({ TableName: process.env.TABLE_NAME,
Key: { connectionId: { S: postParams.connectionId } } });
} else {
console.log("Failed to post. Error: " + JSON.stringify(err));
}
} else {
count++;
}
});
});
callback(null, {
statusCode: 200,
body: "Data send to " + count + " connection" + (count === 1 ? "" : "s")
});
}
});
};
The aws-sdk is also updated, I declared it on a lambda layer and that's what I'm using.
Any idea what's causing this?
This is due to a timeout, the dynamodb loops through all of the records which is causes timeout.
It looks like the cloudwatch was really logging the error, but I was just too focused on the terminal error which gives me the 500, Internal Server Error.
To fix this, just go to the lambda function and increase the time limit.

CloudFormation Custom Resource not finishing deleting

I have setup my custom resource to return immediately on deletes
const aws = require('aws-sdk')
const util = require('util')
exports.handler = (event, context) => {
console.log('Event>>>')
console.log(JSON.stringify(event))
aws.config.update({ region: event.ResourceProperties.Region })
if (event.RequestType === 'Delete') return ApiMethodCustom.sendResponse(event, context, 'SUCCESS') // HERE!
ApiMethodCustom.setupIntegration(event, context)
}
static async sendResponse(event, context, responseStatus, responseData = {}) {
var responseBody = JSON.stringify({
Status: responseStatus,
Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
PhysicalResourceId: context.logStreamName,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
Data: responseData
});
console.log("RESPONSE BODY:\n", responseBody);
var https = require("https");
var url = require("url");
var parsedUrl = url.parse(event.ResponseURL);
var options = {
hostname: parsedUrl.hostname,
port: 443,
path: parsedUrl.path,
method: "PUT",
headers: {
"content-type": "",
"content-length": responseBody.length
}
};
console.log("SENDING RESPONSE...\n");
var request = https.request(options, function (response) {
console.log("STATUS: " + response.statusCode);
console.log("HEADERS: " + JSON.stringify(response.headers));
// Tell AWS Lambda that the function execution is done
context.done();
});
request.on("error", function (error) {
console.log("sendResponse Error:" + error);
// Tell AWS Lambda that the function execution is done
context.done();
});
// write data to request body
request.write(responseBody);
request.end();
}
But it appears that CloudFormation is stuck in DELETE_IN_PROGRESS. Why is that?
In my logs, it seems like Lambda finished execution correctly:
2018-09-09T01:52:06.913Z f48808d0-b3d2-11e8-9e84-5b218cad3090
{
"RequestType": "Delete",
"ServiceToken": "arn:aws:lambda:ap-southeast-1:621567429603:function:income2-base-ApiVpcIntegration",
"ResponseURL": "https://cloudformation-custom-resource-response-apsoutheast1.s3-ap-southeast-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aap-southeast-1%3A621567429603%3Astack/test/5a34d100-b370-11e8-b89d-503a138dba36%7CApiTestIntegration%7C979b1814-d94c-4a49-b9f7-2fa352ab88f5?AWSAccessKeyId=AKIAIKQZQ3QDXOJPHOPA&Expires=1536465125&Signature=O2O0entoTXHCYp5jbJehghtE9Ck%3D",
"StackId": "arn:aws:cloudformation:ap-southeast-1:621567429603:stack/test/5a34d100-b370-11e8-b89d-503a138dba36",
"RequestId": "979b1814-d94c-4a49-b9f7-2fa352ab88f5",
"LogicalResourceId": "ApiTestIntegration",
"PhysicalResourceId": "2018/09/08/[$LATEST]b8a3df0fca884fe3b8abdde3ab525ac0",
"ResourceType": "Custom::ApiVpcIntegration",
"ResourceProperties": {
"ServiceToken": "arn:aws:lambda:ap-southeast-1:621567429603:function:income2-base-ApiVpcIntegration",
"ConnectionId": "24lbti",
"ResourceId": "x1gjyy",
"RestApiId": "aaj0q4dbml",
"Uri": "http://dropletapi-dev.2359media.net:3001/authentication",
"HttpMethod": "GET"
}
}
2018-09-09T01:52:06.914Z f48808d0-b3d2-11e8-9e84-5b218cad3090 RESPONSE BODY:
{
"Status": "SUCCESS",
"Reason": "See the details in CloudWatch Log Stream: 2018/09/09/[$LATEST]29276598cb9c49c1b1da3672c8707c78",
"PhysicalResourceId": "2018/09/09/[$LATEST]29276598cb9c49c1b1da3672c8707c78",
"StackId": "arn:aws:cloudformation:ap-southeast-1:621567429603:stack/test/5a34d100-b370-11e8-b89d-503a138dba36",
"RequestId": "979b1814-d94c-4a49-b9f7-2fa352ab88f5",
"LogicalResourceId": "ApiTestIntegration",
"Data": {}
}
I had a similar issue today while using the cfn-response package, which your code appears to be based on. The cfn-response package is based on a callback but your code also seems to partially use async/await (option with Runtime: node.js8.10).
In your case I suspect that you never saw the "STATUS: " or "HEADERS: " messages even if the response body was dumped to logs (synchronously). That mirrors my experience when using callback-based cfn-response mixed with async/await.
In other words, in all circumstances you will need to ensure that you send a response to Cloudformation (PUT to the event S3 ResponseURL) before your Lambda terminates or the template could hang for up to an hour before giving up and rolling back (probably with a Cloudformation error along the lines of "Failed to stabilise the resource...". Rollback (deletion) in turn can also take an hour because the delete also does not response appropriately. A bit more information here.
I ended up implementing custom resources much like this example on GitHub by https://github.com/rosberglinhares (MIT license) with a couple of differences; I didn't set-up a separate lambda to handle the sendResponse functionality and I made the custom resources server-less (using aws cloudformation package and aws cloudformation deploy commands).
Your ApiMethodCustom is not defined so it's hard for me to guide you on that implementation and so I am including my node.js8.10 code using async/await for reference.
First the Custom resource in the Cloudformation template:
---
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CustomResource Example Stack
Resources:
CustomResource:
Type: 'AWS::Serverless::Function'
Properties:
Runtime: nodejs8.10
Handler: index.handler
MemorySize: 128
Timeout: 15
Role: !GetAtt CustomResourceRole.Arn
CodeUri: ./CustomResource/
CustomResourceUser:
Type: 'Custom::CustomResourceUser'
Properties:
ServiceToken: !GetAtt CustomResource.Arn
...
Note that CodeUri is relative to the template path. You will need to define the IAM role and policies for CustomResourceRole.
Now for the CustomResource/index.js Lambda (you will also need to run "npm install --save axios" in the CustomResource directory):
'use strict';
const AWS = require('aws-sdk');
const axios = require('axios');
exports.handler = async (event, context) => {
try {
switch (event.RequestType) {
case 'Create':
await ApiMethodCustom.create(...);
break;
case 'Update':
await ApiMethodCustom.update(...);
break;
case 'Delete':
await ApiMethodCustom.delete(...);
break;
}
console.info('Success for request type ${event.RequestType}');
await sendResponse(event, context, 'SUCCESS', { } );
} catch (error) {
console.error('Error for request type ${event.RequestType}: ', error);
await sendResponse(event, context, 'FAILED', { } );
}
}
async function sendResponse (event, context, responseStatus, responseData, physicalResourceId) {
var reason = responseStatus == 'FAILED' ? ('See the details in CloudWatch Log Stream: ' + context.logStreamName) : undefined;
var responseBody = JSON.stringify({
StackId: event.StackId,
RequestId: event.RequestId,
Status: responseStatus,
Reason: reason,
PhysicalResourceId: physicalResourceId || context.logStreamName,
LogicalResourceId: event.LogicalResourceId,
Data: responseData
});
var responseOptions = {
headers: {
'Content-Type': '',
'Content-Length': responseBody.length
}
};
console.info('Response body:\n', responseBody);
try {
await axios.put(event.ResponseURL, responseBody, responseOptions);
console.info('CloudFormationSendResponse Success');
} catch (error) {
console.error('CloudFormationSendResponse Error:');
if (error.response) {
console.error(error.response.data);
console.error(error.response.status);
console.error(error.response.headers);
} else if (error.request) {
console.error(error.request);
} else {
console.error('Error', error.message);
}
console.error(error.config);
throw new Error('Could not send CloudFormation response');
}
}
For more information on using callback vs. async with AWS Lambda's have a look here.
Finally, note the use of Axios. It's promise-based and therefore supports await instead of callbacks.

node.js(Javascript) google-api-nodejs-client - list messages

My question was voted down, so i am rewriting it hopefully this is more succinct
I am stuck at writing a javascript function to list messages in inbox.
Using - official "google-api-nodejs-client", node.js, electron, (and javascript)
Goal: list messages in gmail inbox
For that to work i need to authorize first then ask for the messages
Authorize
- I copied the code from google node.js quickstart
- this works in electron (well actually node.js as it is a command line script).
Ask for Messages
- Google has an example, i copied it adjusted some parts but doesn't work. I think the example i am working from is not designed for the node.js "google-api-nodejs-client".
- Maybe it needs a different authorize
This is the listmessages function from the google example, i can't seem to figure out how to make this work with the authorize from list labels. This is what i have tried
changing gapi to google
changing userId to 'me'
changing givig it a query
Does not use a client library.
Does not use a client library.
/**
* Retrieve Messages in user's mailbox matching query.
*
* #param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* #param {String} query String used to filter the Messages listed.
* #param {Function} callback Function to call when the request is complete.
*/
function listMessages(userId, query, callback) {
var getPageOfMessages = function(request, result) {
request.execute(function(resp) {
result = result.concat(resp.messages);
var nextPageToken = resp.nextPageToken;
if (nextPageToken) {
request = gapi.client.gmail.users.messages.list({
'userId': userId,
'pageToken': nextPageToken,
'q': query
});
getPageOfMessages(request, result);
} else {
callback(result);
}
});
};
var initialRequest = gapi.client.gmail.users.messages.list({
'userId': userId,
'q': query
});
getPageOfMessages(initialRequest, []);
}
This is the aurhorize function that works to list labels.
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), listLabels);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
/**
* Lists the labels in the user's account.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listLabels(auth) {
var gmail = google.gmail('v1');
gmail.users.labels.list({
auth: auth,
userId: 'me',
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var labels = response.labels;
if (labels.length == 0) {
console.log('No labels found.');
} else {
console.log('Labels:');
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
console.log('- %s', label.name);
}
}
});
}
The flow of your program should look like this:
Then you can use the api to list messages. (just to get you started):
export function listMessages(oauth2Client, userId, query, callback) {
const gmail = google.gmail('v1');
gmail.users.messages.list({
auth: oauth2Client,
userId: 'me',
}, (err, response) => {
console.log(response);
});
}
Notice the oauth2Client parameter. This is the object you get from your function authorize. Let me know if you have any questions.

Resources