Skip to content

Instantly share code, notes, and snippets.

@DinoChiesa
Last active January 14, 2020 14:40
Show Gist options
  • Save DinoChiesa/78789a90de46445f1016344879e5b344 to your computer and use it in GitHub Desktop.
Save DinoChiesa/78789a90de46445f1016344879e5b344 to your computer and use it in GitHub Desktop.
boilerplate for building a command-line app in nodejs that uses OAuth2 client id to update Google sheets
// updateSheets.js
// ------------------------------------------------------------------
//
// created: Tue Jan 14 05:22:42 2020
// last saved: <2020-January-14 06:34:31>
const fs = require('fs'),
path = require('path'),
util = require('util'),
{google} = require('googleapis'),
readline = require('readline'),
clipboardy = require('clipboardy'),
opn = require('opn');
const GOOG_APIS_SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
// ==================================================================
// The following nonsense is required in order to get
// ctrl-v (paste) events to work in readline on Windows.
readline.emitKeypressEvents(process.stdin);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
process.stdin.setRawMode(true);
process.stdin.on('keypress', (str, key) => {
if (key.ctrl && key.name === 'v') {
rl.write(clipboardy.readSync());
}
});
// ==================================================================
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function getNewGsuiteToken(oAuth2Client, tokenStashPath, projectId, callback) {
console.log('\nYou must authorize Sheet updater to create a new sheet.\n');
console.log('This script will now open a browser tab. After granting consent, you will');
console.log('receive a one-time code. Return here and paste it in, to continue....\n');
sleep(4200)
.then(() => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: GOOG_APIS_SCOPES
});
// Authorize this app by visiting the url
opn(authUrl, {wait: false});
rl.question('Paste the one-time-code: ', (code) => {
oAuth2Client.getToken(code, (e, token) => {
console.log(`Thanks! You have successfully authorized ${projectId} to do its thing.`);
if (e) {
console.log('cannot get token?');
console.log(e);
return callback(e);
}
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
let dataToStore = JSON.stringify(token, null, 2) + '\n';
fs.writeFile(tokenStashPath, dataToStore, (e) => {
if (e) {
console.error(e); // this is a non-fatal condition
}
else {
console.log('stored token in token stash: ' + tokenStashPath);
}
callback(null, oAuth2Client);
});
});
});
});
}
/**
* 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 oauth2Authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
const tokenStashPath = path.join('.', ".gsheets_token_stash.json");
// Check if there is a previously stashed token.
//console.log('token stash path: ' + tokenStashPath);
fs.readFile(tokenStashPath, (e, data) => {
if (e) {
console.log('cannot read stashed token: ' + e);
return getNewGsuiteToken(oAuth2Client, tokenStashPath, projectId, callback);
}
let stashedToken = JSON.parse(data);
//console.log('read stashed token: ' + util.format(stashedToken));
//let expiry = new Date(stashedToken.expiry_date);
//let now = new Date();
//console.log('expires: ' + expiry.toISOString());
//console.log('now: ' + now.toISOString());
// expiry and refresh is handled automagically
oAuth2Client.setCredentials(stashedToken);
callback(null, oAuth2Client);
});
}
// for setup, visit console.cloud.google.com
// IAM
// Create Credentials
// OAuth2 client id
// Other
// (name it)
// Save
// Edit
// Download JSON
// The downloaded credentials file ought to look like this:
// {
// "installed": {
// "client_id": "8675309-b9aa47c7aa3ed0bc03ac569c.apps.googleusercontent.com",
// "project_id": "my-project-name",
// "auth_uri": "https://accounts.google.com/o/oauth2/auth",
// "token_uri": "https://www.googleapis.com/oauth2/v3/token",
// "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
// "client_secret": "ABCDEFGHIBAADBEEF",
// "redirect_uris": [
// "urn:ietf:wg:oauth:2.0:oob",
// "http://localhost"
// ]
// }
// }
const clientCredentialsFile = path.join(".", "gsheets_client_credentials.json");
fs.readFile(clientCredentialsFile, (e, content) => {
if (e) {
console.log('Error loading client credentials file:', e);
return;
}
let credentials = JSON.parse(content).installed;
oauth2Authorize(credentials, doTheThing(credentials.project_id));
});
function doTheThing(projectId) {
return function(e, oAuth2Client) {
rl.close();
if (e) {
return console.log(e);
}
console.log(`${projectId} is authorized to do its thing.`);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment