Send email from server via a G Suite email address - node.js

I am attempting to send an email from a node.js server using nodemailer
I currently have a domain name registered with Google Domains and with this I have a G Suite instance setup to provide me with an email server.
I have an email setup: noreply#domainname.com
What im trying to do is send an email from email above from my server. I don't want to use plain user and password verification as this is very insecure for me.
Does anyone have a tutorial or document they could link me to that would help me achieve this?

Here is a Quickstart from the official Gmail API documentation.
Complete the steps described in the rest of this page, and in about
five minutes you'll have a simple Node.js command-line application
that makes requests to the Gmail API.
Snippet from the quickstart:
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);
}
}
});
}
To understand the concept and the implementation details, you can try Sending Email. To give you an overview, here is the way of sending email and high end workflow.
There are two ways to send email using the Gmail API:
You can send it directly using the messages.send method.
You can send it from a draft, using the drafts.send method.
Emails are sent as base64url encoded strings within the raw property
of a message resource. The high-level workflow to send an email
is to:
Create the email content in some convenient way and encode it as a base64url string.
Create a new message resource and set its raw property to the base64url string you just created.
Call messages.send, or, if sending a draft, drafts.send to send the message.
You can also refer to this tutorial from Nodemailer community site.
These docs apply to the unmaintained versions of Nodemailer v2 and
older. For an upgraded and up to date Nodemailer v3+ documentation,
see nodemailer.com homepage.

Instead of building whole logic for creating tokens you can simply use Passportjs google auth and then finally use the googleapis package to send emails.

Related

use of r1.question when using google sheets api

I am using the documentation found at https://developers.google.com/sheets/api/quickstart/nodejs in order to use the google sheets api in my node.js application. For reference, the code I'm using from the page is as below:
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
// 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 Sheets API.
authorize(JSON.parse(content), listMajors);
});
/**
* 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 getNewToken(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 getNewToken(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 while trying to retrieve 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);
});
});
}
/**
* Prints the names and majors of students in a sample spreadsheet:
* #see https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
* #param {google.auth.OAuth2} auth The authenticated Google OAuth client.
*/
function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
console.log('Name, Major:');
// Print columns A and E, which correspond to indices 0 and 4.
rows.map((row) => {
console.log(`${row[0]}, ${row[4]}`);
});
} else {
console.log('No data found.');
}
});
}
At the line where it says rl.question('Enter the code from that page here: ', (code) => { I get the feeling that I'm supposed to input something, but I never see any obvious prompt to enter any kind of code, and thus I do not enter anything and my code goes to return console.error('Error while trying to retrieve access token', err); and I apparently never get an access token. How exactly do I enter this code, and where do I get this code from?
EDIT: Detailed flow for replicating bug.
I have the following requires at the top of my code:
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
Below is the route which I first access in the browser in order to initiate the process.
app.get("/test", function (req,res) {
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
// 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 Sheets API.
authorize(JSON.parse(content), listMajors);
});
/**
* 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;
var client_secret = credentials.web.client_secret;
var client_id = credentials.web.client_id;
var redirect_uris = credentials.web.redirect_uris;
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 getNewToken(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 getNewToken(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 while trying to retrieve 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);
});
});
}
/**
* Prints the names and majors of students in a sample spreadsheet:
* #see https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
* #param {google.auth.OAuth2} auth The authenticated Google OAuth client.
*/
function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
console.log('Name, Major:');
// Print columns A and E, which correspond to indices 0 and 4.
rows.map((row) => {
console.log(`${row[0]}, ${row[4]}`);
});
} else {
console.log('No data found.');
}
});
}
})
When I access this route, the browser tries to load to something, but never gets there. In the debug console I get a prompt saying "Authorize this app by visiting this url: ..." and I go to the given url in the browser. I am already signed into google, so I am given a list of accounts to choose from where the browser says "Choose an account to continue to quickstart." I choose my google account, and then I get the "this app isn't verified screen", so I click "Advanced" towards the bottom of the screen, after which a "Go to Quickstart (unsafe)" link appears. I click this link, and then I seemingly go back to the previous oauth screen where I was given a list of accounts, except a prompt now appears asking to Grant Quickstart permission to view your google spreadsheets. I click "Allow", and then I am redirected to a page which looks similar to the previous Oath screen, except it's asking me to confirm my choices, make sure you trust quickstart etc., so I click Allow once again and I am finally redirected to the page I specified that I would go to by my redirect url. However, in my working directory I can never see token.json, and I never see the "Enter the code from that page here: " in the terminal (I am using VSCode and the terminal is set as windows powershell). When I try to enter the code I get from the url parameters (from the redirect page at the end) I get the error I mentioned before.

TypeError: Cannot read property 'redirect_uris' of undefined Youtube Data API authentication NodeJS

I have a web application through which I'd like to be able create playlists, add videos to a playlist, delete a video etc, to my youtube channel. I have created a service account and downloaded the service account credentials key file and set up my OAuth 2.0 Client IDs in the Google Developer Console.
To authenticate my app, I followed the instructions in the README.md for the google-api-nodejs-client here https://github.com/googleapis/google-api-nodejs-client - under service account credentials
Here is my controller file...
I should note that the project uses ES Modules and thus "type": "module" is set in package.json. This is why you'l notice for example that I am importing __dirname as a utility since ES Modules do not support the regular __dirname.
import googleapi from "googleapis";
const { google } = googleapi;
import Auth from "#google-cloud/local-auth";
const { authenticate } = Auth;
import path from "path";
import __dirname from "../utils/dirname.js";
async function initialize() {
try {
const auth = await authenticate({
keyfilePath: path.join(__dirname, "../service_account_credentials.json"),
scopes: ["https://www.googleapis.com/auth/youtube"],
});
console.log("Auth details");
console.log(auth);
google.options({ auth });
} catch (e) {
console.log(e);
}
}
initialize();
const oauth2Client = new google.auth.OAuth2(
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
"http://localhost:5000/oauth2callback"
);
// initialize the Youtube API library
const youtube = google.youtube({ version: "v3", auth: oauth2Client });
class YoutubeController {
static async createPlaylist(req, res) {
const { name } = req.body;
const playlist = await youtube.playlists.insert({
part: "snippet,status",
resource: {
snippet: {
title: name,
description: `${name} videos.`,
},
status: {
privacyStatus: "private",
},
},
});
res.json(playlist);
}
}
The initialize function, is the one that throws the error and I can't quite figure it out. I think because of that, when I make a POST request to the route that calls the method createPlaylist inside the class, I get back No access, refresh token or API key is set.
I've been going through the docs trying to understand how everything flows but I'm a little stuck.
A similar question was asked here - TypeError: Cannot read property 'redirect_uris' of undefined but there are no answers and the suggested workflow does not work for my case so I'd really appreciate your help on this.
service accounts
The YouTube API does not support service account authentication you need to use OAuth2.
OAuth2 Authorization
You might want to consider following the YouTube API quick start for nodejs.
The issue is that you are using service account authentication with the YouTube API which it does not support.
var fs = require('fs');
var readline = require('readline');
var {google} = require('googleapis');
var OAuth2 = google.auth.OAuth2;
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/youtube-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/youtube.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'youtube-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 YouTube API.
authorize(JSON.parse(content), getChannel);
});
/**
* 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 oauth2Client = new 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), (err) => {
if (err) throw err;
console.log('Token stored to ' + TOKEN_PATH);
});
}
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function getChannel(auth) {
var service = google.youtube('v3');
service.channels.list({
auth: auth,
part: 'snippet,contentDetails,statistics',
forUsername: 'GoogleDevelopers'
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var channels = response.data.items;
if (channels.length == 0) {
console.log('No channel found.');
} else {
console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
'it has %s views.',
channels[0].id,
channels[0].snippet.title,
channels[0].statistics.viewCount);
}
});
}
Code shamelessly ripped from YouTube API quick start for nodejs.
backend access
Due to the fact that YouTube API does not support service accounts. Accessing the data from a backend service can be tricky but it is not imposible.
Run your application locally once.
Aurhtoirze it to access your account data.
In your code find the credentials that are stored they should contain a refresh token.
Save this refresh token as part of your application.
set up your code to read this refresh token when loading.
Unfortunately i am not a node.js developer so i cant help you with the code required to do that. The library should be storing things into a credentials object if you can find that and how thats loaded then you should be able to do what i have suggested.
I would start by digging around into what ever storeToken(token); is doing.

can't get refresh token for google api. Node js

I've got an Apps Script script that I am running remotely through Google's API. When I go to the link it gives me for the code to retrieve the access token, it says that the file requested does not exist. However, the code is in the url anyway, this gives me the access token, but not the refresh token.
I have tried adding 'access_type: offline' and 'approval_prompt: force', but those didn't change anything. Here's the code:
var { google } = require('googleapis');
var fs = require('fs');
var async = require('async');
const readline = require('readline');
// If modifying these scopes, delete token.json.
const SCOPES = [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/script.projects",
"https://www.googleapis.com/auth/spreadsheets"
];
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 Apps Script API.
authorize(JSON.parse(content), callScriptFunction);
});
/**
* 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("294862899955-bb0929ato2qem8cqllggrpuqpqit191v.apps.googleusercontent.com", "Ds4-q0G3QZog4UamQrc3HFrW", "https://script.google.com/oauthcallback");
// 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) console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
function callScriptFunction(auth) {
var scriptId = "MwnuiFwldt-0ZLyLnoi0Q5kfoO49Cn6ao";
var script = google.script('v1');
script.scripts.run({
auth: auth,
resource: {
function: 'automateSheet',
parameters: [
process.argv[2],
process.argv[3],
process.argv[4],
process.argv[5],
process.argv[6],
process.argv[7],
process.argv[8],
]
},
scriptId: scriptId,
devMode: true
}, function (err, resp) {
if (err) {
console.log(err);
}
else {
var r = resp.data;
if ("error" in r) {
console.log("Error: %o", r.error);
} else {
console.log("Result: %o", r.response.result);
}
}
});
}
Here's the page Google gives me when I agree to allow the app access to my account:
i found the answer here:
Not receiving Google OAuth refresh token
where the guy said to go to https://myaccount.google.com/u/2/permissions?pli=1&pageId=none
then revoke your apps permission. it worked for me. because every time after the first time, the refresh token does not appear
I think there are two issues going on. One, it looks like your Authorized redirect URI isn't set correctly. It needs to be set to a page on your server that listens for the code URL parameter after the user authorizes. You can set it in the Google Cloud console under API > Credentials > 0Auth Client Settings > Authorized redirect URIs.
Second, I had an issue where sometimes I wouldn't get a refresh token. Turns out I had to add prompt: 'consent' to the generateAuthUrl request to force it to always return a refresh token, by always taking the user through the full authorization flow (instead of skipping steps if they recently authenticated). So your function would be:
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
prompt: 'consent',
});
Hope that helps. For reference: https://github.com/googleapis/google-api-nodejs-client/issues/750#issuecomment-368873635

Confusion on Google Drive API, how can I download contents of folders? (node.js)

Hi I am trying to take the data returned to me from using the google drive api quickstart for node, and download the contents of docx files directly to my repo.
Here is my code so far, pretty much taken directly from this quickstart page, https://developers.google.com/drive/v3/web/quickstart/nodejs
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/drive-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'drive-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
// Drive API.
authorize(JSON.parse(content), listFiles);
});
/**
* 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);
}
https://www.googleapis.com/drive/v3/files/fileId/text/plain
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listFiles(auth) {
var service = google.drive('v3');
service.files.list({
auth: auth,
pageSize: 10,
fields: "nextPageToken, files(id, name)"
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var files = response.files;
if (files.length == 0) {
console.log('No files found.');
} else {
console.log('Files:');
for (var i = 0; i < files.length; i++) {
var file = files[i];
if(file.name.includes('doc')){
************ download file somehow ************
}
}
}
});
}
From reading their documentation(https://developers.google.com/drive/v3/reference/ and https://developers.google.com/drive/v3/reference/files/export#http-request), it looks like I'm supposed to export the url with the id to whatever mimetype I need, but even in the browser this is giving me the error:
"Not found"
For instance it tells me to use this url:
https://www.googleapis.com/drive/v3/files///export
given the id:
"1QekrRHbSLu0mDZt64gTMyCOQlWgubufycK1-3249YQY"
https://www.googleapis.com/drive/v3/files/1QekrRHbSLu0mDZt64gTMyCOQlWgubufycK1-3249YQY/text/plain/export
The browser says "Not found"
if I go to the same url without /text/plain/export (https://www.googleapis.com/drive/v3/files/1QekrRHbSLu0mDZt64gTMyCOQlWgubufycK1-3249YQY/) I get the error:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
can anyone tell me how to save my file?
How about this sample script?
Modification points :
When Daily Limit for Unauthenticated Use Exceeded... is shown, Drive API had been possibly used without authorization.
In your script, files cannot been downloaded using the scope of var SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];.
Modified script :
In order to use this sample, please do as follows.
Replace from your listFiles() to my one.
Set folderid. This script can retrieve files in the folder with folderid.
Delete drive-nodejs-quickstart.json. I think that there is the file at .credentials in your home directory.
Change the SCOPE from var SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']; to var SCOPES = ['https://www.googleapis.com/auth/drive.readonly'];.
Run script, retrieve the code and authorize.
And this sample supposes as follows. So please confirm them.
quickstart is used and default quickstart works fine. If this doesn't work, please confirm https://developers.google.com/drive/v3/web/quickstart/nodejs again.
From your file ID, it seems that you want to download Google Document. So in this script, files.export is used.
From your question, it seems that you want to convert from Google Document to text data. So in this script, the Google Document is downloaded as text data.
Files in the trash box are ignored.
Method of file search is performed by if (e.name.includes('doc')) { in your script.
Script :
function listFiles(auth) {
var service = google.drive('v3');
var folderid = "### Folder ID ###";
service.files.list({
auth: auth,
q: "'" + folderid + "' in parents and trashed=false",
fields: "files(id, name)"
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
response.files.forEach(function(e){
if (e.name.includes('doc')) {
var dlfile = fs.createWriteStream(e.name + ".txt");
service.files.export({
auth: auth,
fileId: e.id,
mimeType: 'text/plain'
}).on('end', function() {
console.log("Done.");
}).on('error', function(err) {
console.error(err);
return process.exit();
}).pipe(dlfile);
}
});
});
}
If I misunderstand your question, I'm sorry.

Error: Script Error in Node.js to Google Apps Script

I'm trying to take the Google Apps Script Node.js tutorial and adapt it to be a generic Google Apps Script function caller. Here is what I came up with
var fs = require('fs');
var path=require('path');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var SCOPES = ['https://www.googleapis.com/auth/drive'];
var TOKEN_DIR = path.dirname(path.dirname(path.dirname(process.execPath))) + '/other/credentials/';
var TOKEN_PATH = TOKEN_DIR + 'script-nodejs-quickstart.json';
//I ADDED THIS FUNCTION WITH THE PARAMETERS
exports.run=function (myfunction,myparam){
// 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
// Google Apps Script Execution API.
console.log('one ran');
//TRANSFER THE PARAMETERS TO authorize
authorize(JSON.parse(content), callFunction,myfunction,myparam);
});
}
/**
* 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,myfunction,myparam) {
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);
console.log('now two');
//TRANSFER THE PARAMETES TO callFunction
callback(oauth2Client,myfunction,myparam);
}
});
}
/**
* 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: \n', 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);
}
/**
* Call an Apps Script function to list the folders in the user's root
* Drive folder.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function callFunction(auth,myfunction,myparam) {
console.log('now we are at the last');
var scriptId = 'my apps script id';
var script = google.script('v1');
// Make the API request. The request object is included here as 'resource'.
//PARAMETERS APPLIED
script.scripts.run({
auth: auth,
resource: {
function: myfunction,
parameters: myparam,
devMode:true
},
scriptId: scriptId
}, function(err, resp) {
console.log(myfunction,myparam,auth);
if (err) {
// The API encountered a problem before the script started executing.
console.log('The API returned an error: ' + err);
return;
}
if (resp.error) {
// The API executed, but the script returned an error.
// Extract the first (and only) set of error details. The values of this
// object are the script's 'errorMessage' and 'errorType', and an array
// of stack trace elements.
var error = resp.error.details[0];
console.log('Script error message: ' + error.errorMessage);
console.log('Script error stacktrace:');
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start executing.
for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
var trace = error.scriptStackTraceElements[i];
console.log('\t%s: %s', trace.function, trace.lineNumber);
}
}
} else {
exports.result(resp.response.result);
}
});
}
exports.result=function(result){
return result;
}
Everything in CAPS is my commenting.
My problem is that every time I test it out it returns:
The API returned an error: Error: ScriptError
Permissions for the api and the app script do line up. so it isnt a permissions error.
It doesn't give me any other details. Does anyone know what is causing this error?
It is likely you have a scope problem. I found that the correct scope to use for spreadhseets is https://www.googleapis.com/auth/spreadsheets. This can be seen in the script editor under File -> Project properties -> Scopes.
This is due to an earlier problem with how the Node.js library parsed responses, which was fixed in https://github.com/google/google-auth-library-nodejs/commit/323b794f3aa8bf7291041620155451c3f3b2b4d2. If you re-install google-auth-library then you should see the full error details.
I have bumped into the same issue and solved by adding proper scopes to my script.
Make sure you have exact same list of scopes in your node.js script with the ones in apps script by looking at "Files > Project properties > Scopes".
This is not mentioned in Quick Start, but following document does describe it: https://developers.google.com/apps-script/guides/rest/api#general_procedure

Resources