From a49878cd9c5ed1d0886c70914ac11fb26c1be61e Mon Sep 17 00:00:00 2001 From: Yash Totale Date: Tue, 9 Feb 2021 14:36:50 -0800 Subject: [PATCH] Added Clean up Dry Runs Script --- .eslintignore | 38 ++++-- .gitignore | 3 +- .markdownlintignore | 10 +- .prettierignore | 36 ++++-- package.json | 3 +- scripts/clean-up-dry-runs.ts | 148 ++++++++++++++++++++++++ scripts/drive.ts | 39 ------- src/Helpers/AppsScript/src/form-data.ts | 2 +- src/Helpers/Logger/index.ts | 4 + src/script.ts | 1 - 10 files changed, 215 insertions(+), 69 deletions(-) create mode 100644 scripts/clean-up-dry-runs.ts delete mode 100644 scripts/drive.ts diff --git a/.eslintignore b/.eslintignore index 8cd1eb8..a3f301a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,15 +1,33 @@ -# dependencies +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Dependency directories node_modules/ -# testing -coverage/ +# Optional npm cache directory +.npm -# production -dist/ -out/ -build/ +# Optional eslint cache +.eslintcache -# misc -serviceaccount.json +# dotenv environment variables file .env -remind-template.html +.env.test + +# Outputs +out/ +output/ + +# Secrets +credentials.json +oauth-token.json +.clasprc.json + +# Misc +.DS_Store +remind-mail.html diff --git a/.gitignore b/.gitignore index 23e4df0..ebfcb8d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,8 @@ out/ output/ # Secrets -serviceaccount.json +credentials.json +oauth-token.json .clasprc.json # Misc diff --git a/.markdownlintignore b/.markdownlintignore index 20f1504..bab273e 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -1,10 +1,6 @@ -# dependencies +# Dependency directories node_modules/ -# testing -coverage/ - -# production -dist/ +# Outputs out/ -build/ +output/ diff --git a/.prettierignore b/.prettierignore index 129f73c..a3f301a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,15 +1,33 @@ -# dependencies +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Dependency directories node_modules/ -# testing -coverage/ +# Optional npm cache directory +.npm -# production -dist/ -out/ -build/ +# Optional eslint cache +.eslintcache -# misc -serviceaccount.json +# dotenv environment variables file .env +.env.test + +# Outputs +out/ +output/ + +# Secrets +credentials.json +oauth-token.json +.clasprc.json + +# Misc +.DS_Store remind-mail.html diff --git a/package.json b/package.json index 02b763d..f456a71 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "postinstall": "replace-in-file \"declare var console\" \"//declare var console\" node_modules/@types/google-apps-script/google-apps-script.base.d.ts", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", - "contributors:check": "all-contributors check" + "contributors:check": "all-contributors check", + "clean-up-dry-runs": "ts-node scripts/clean-up-dry-runs.ts" }, "dependencies": { "airtable": "^0.10.1", diff --git a/scripts/clean-up-dry-runs.ts b/scripts/clean-up-dry-runs.ts new file mode 100644 index 0000000..27f3564 --- /dev/null +++ b/scripts/clean-up-dry-runs.ts @@ -0,0 +1,148 @@ +// Externalss +import { readFile, writeFile } from "fs/promises"; +import { join } from "path"; +import { promisify } from "util"; +import readline, { createInterface } from "readline"; +import { google, Auth, drive_v3 } from "googleapis"; +import moment from "moment"; + +// Internals +import keyfile from "../credentials.json"; +import Logger from "../src/Helpers/Logger"; + +readline.Interface.prototype.question[promisify.custom] = function ( + prompt: string +) { + return new Promise((resolve) => + readline.Interface.prototype.question.call(this, prompt, resolve) + ); +}; + +// @ts-expect-error Defining new method +readline.Interface.prototype.questionAsync = promisify( + readline.Interface.prototype.question +); + +const TOKEN_PATH = join(__dirname, "..", "oauth-token.json"); + +const cleanUpDryRuns = async () => { + const oAuth2Client = new google.auth.OAuth2( + keyfile.installed.client_id, + keyfile.installed.client_secret, + keyfile.installed.redirect_uris[0] + ); + + await authorize(oAuth2Client); + + const drive = google.drive({ + version: "v3", + auth: oAuth2Client, + }); + + const dryRunsFolder = await getAllDryRuns(drive); + + for (const item of dryRunsFolder.files ?? []) { + Logger.line(); + const id = item.id ?? ""; + + const fileData = await getDryRunData(drive, id); + + Logger.bold(fileData.name ?? ""); + + const shouldDelete = shouldDeleteFile(fileData); + + if (shouldDelete) deleteFile(drive, id); + else { + Logger.success( + `This file is not stale! (modified less than 1 month ago)` + ); + } + } +}; + +const authorize = async ( + oAuth2Client: Auth.OAuth2Client +): Promise => { + Logger.log("Authorizing..."); + let token: Auth.Credentials; + try { + const rawToken = await readFile(TOKEN_PATH, "utf-8"); + token = JSON.parse(rawToken); + oAuth2Client.setCredentials(token); + } catch (e) { + const authUrl = oAuth2Client.generateAuthUrl({ + access_type: "offline", + scope: ["https://www.googleapis.com/auth/drive"], + }); + + Logger.line(); + Logger.log(`Authorize this app by visiting this url: ${authUrl}`); + + const rl = createInterface({ + input: process.stdin, + output: process.stdout, + }); + + Logger.line(); + // @ts-expect-error Defined method at the top of the file + const code = await rl.questionAsync("Enter the code from that page here: "); + + rl.close(); + + const { tokens } = await oAuth2Client.getToken(code); + token = tokens; + + oAuth2Client.setCredentials(token); + + await writeFile(TOKEN_PATH, JSON.stringify(token)); + + Logger.success(`Token stored at ${TOKEN_PATH}`); + Logger.line(); + } + Logger.success("Authorized!"); + return token; +}; + +const getAllDryRuns = async ( + drive: drive_v3.Drive +): Promise => { + Logger.line(); + Logger.log("Fetching 'Dry Run Forms' Folder..."); + const response = await drive.files.list({ + q: "'1q8N4gJ7A9XXuW1APiaf_BT5dYxd8tVOc' in parents and trashed = false", + }); + Logger.success("Fetched 'Dry Run Forms' Folder!"); + return response.data; +}; + +const getDryRunData = async ( + drive: drive_v3.Drive, + id: string +): Promise => { + const itemResponse = await drive.files.get({ + fileId: id, + fields: "name,trashed,modifiedTime", + }); + + const { data: fileData } = itemResponse; + return fileData; +}; + +const shouldDeleteFile = (fileData: drive_v3.Schema$File): boolean => { + const { modifiedTime, trashed } = fileData; + const oneMonthAgo = moment().subtract(1, "month"); + const isStale = moment(modifiedTime).isBefore(oneMonthAgo); + + return isStale && !trashed; +}; + +const deleteFile = async (drive: drive_v3.Drive, id: string) => { + Logger.warning(`This file is stale (>1 month since changes)`); + Logger.log("Deleting file..."); + await drive.files.delete({ + fileId: id, + }); + Logger.success("Deleted file!"); +}; + +cleanUpDryRuns(); diff --git a/scripts/drive.ts b/scripts/drive.ts deleted file mode 100644 index 06aea55..0000000 --- a/scripts/drive.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Externals -import { google } from "googleapis"; - -// Internals -import keyfile from "../serviceaccount.json"; - -const drive = async () => { - const auth = new google.auth.JWT( - keyfile.client_email, - undefined, - keyfile.private_key, - [ - "https://www.googleapis.com/auth/drive", - "https://www.googleapis.com/auth/forms", - ] - ); - - await auth.authorize(); - - const drive = google.drive({ - version: "v2", - auth, - }); - - const response = await drive.children.list({ - folderId: "1qfx3jwE7QE_TPgSuOHUn_s31FmcoUGEd", - }); - - const { data: folderData } = response; - - for (const item of folderData.items ?? []) { - const itemResponse = await drive.files.get({ - fileId: item.id ?? undefined, - }); - console.log(itemResponse); - } -}; - -drive(); diff --git a/src/Helpers/AppsScript/src/form-data.ts b/src/Helpers/AppsScript/src/form-data.ts index d360f1d..0c7ed47 100644 --- a/src/Helpers/AppsScript/src/form-data.ts +++ b/src/Helpers/AppsScript/src/form-data.ts @@ -19,7 +19,7 @@ export const initializeForm = ( title, DriveApp.getFolderById( dryRun - ? "1qfx3jwE7QE_TPgSuOHUn_s31FmcoUGEd" + ? "1q8N4gJ7A9XXuW1APiaf_BT5dYxd8tVOc" : "1fWj2K9WAQSxpC9jyOZkRfmOvY186I1Xf" ) ) diff --git a/src/Helpers/Logger/index.ts b/src/Helpers/Logger/index.ts index 35dbefb..965d528 100644 --- a/src/Helpers/Logger/index.ts +++ b/src/Helpers/Logger/index.ts @@ -97,6 +97,10 @@ class Logger { console.log(message); } + static line(): void { + console.log(); + } + static coloredLog( color: keyof typeof Logger.COLORS, message: string, diff --git a/src/script.ts b/src/script.ts index e29c1a3..3a46973 100644 --- a/src/script.ts +++ b/src/script.ts @@ -6,7 +6,6 @@ import Airtable from "airtable"; // Internals import { getProjectSuccessData } from "./Helpers/Airtable"; -// import { getSheetData, setSheetData, setUpSheets } from "./Helpers/Sheets"; import { checkReminderNeeded, checkRequiredFields,