From d271205a94bcb2ebc524b70f8a3ff98398e69252 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 12 Aug 2019 21:39:16 -0400 Subject: exporting text to google docs almost supported --- src/server/Message.ts | 1 + src/server/RouteStore.ts | 3 +- src/server/apis/google/GoogleApiServerUtils.ts | 109 +++++++++++++ src/server/apis/youtube/youtubeApiSample.d.ts | 2 + src/server/apis/youtube/youtubeApiSample.js | 179 +++++++++++++++++++++ .../credentials/google_docs_credentials.json | 1 + src/server/credentials/google_docs_token.json | 1 + src/server/index.ts | 33 +++- src/server/youtubeApi/youtubeApiSample.d.ts | 2 - src/server/youtubeApi/youtubeApiSample.js | 179 --------------------- 10 files changed, 321 insertions(+), 189 deletions(-) create mode 100644 src/server/apis/google/GoogleApiServerUtils.ts create mode 100644 src/server/apis/youtube/youtubeApiSample.d.ts create mode 100644 src/server/apis/youtube/youtubeApiSample.js create mode 100644 src/server/credentials/google_docs_credentials.json create mode 100644 src/server/credentials/google_docs_token.json delete mode 100644 src/server/youtubeApi/youtubeApiSample.d.ts delete mode 100644 src/server/youtubeApi/youtubeApiSample.js (limited to 'src/server') diff --git a/src/server/Message.ts b/src/server/Message.ts index aaee143e8..4ec390ade 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,4 +1,5 @@ import { Utils } from "../Utils"; +import { google, docs_v1 } from "googleapis"; export class Message { private _name: string; diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index e30015e39..5d977006a 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -30,6 +30,7 @@ export enum RouteStore { reset = "/reset/:token", // APIS - cognitiveServices = "/cognitiveservices" + cognitiveServices = "/cognitiveservices", + googleDocs = "/googleDocs/" } \ No newline at end of file diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts new file mode 100644 index 000000000..817b2b696 --- /dev/null +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -0,0 +1,109 @@ +import { google, docs_v1 } from "googleapis"; +import { createInterface } from "readline"; +import { readFile, writeFile } from "fs"; +import { OAuth2Client } from "google-auth-library"; + +/** + * Server side authentication for Google Api queries. + */ +export namespace GoogleApiServerUtils { + + // If modifying these scopes, delete token.json. + const prefix = 'https://www.googleapis.com/auth/'; + const SCOPES = [ + 'documents.readonly', + 'documents', + 'drive', + 'drive.file', + ]; + // 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. + export const parseBuffer = (data: Buffer) => JSON.parse(data.toString()); + + export namespace Docs { + + export interface CredentialPaths { + credentials: string; + token: string; + } + + export type Endpoint = docs_v1.Docs; + + export const GetEndpoint = async (paths: CredentialPaths) => { + return new Promise((resolve, reject) => { + readFile(paths.credentials, (err, credentials) => { + if (err) { + reject(err); + return console.log('Error loading client secret file:', err); + } + return authorize(parseBuffer(credentials), paths.token).then(auth => { + resolve(google.docs({ version: "v1", auth })); + }); + }); + }); + }; + + } + + /** + * Create an OAuth2 client with the given credentials, and returns the promise resolving to the authenticated client + * @param {Object} credentials The authorization client credentials. + */ + export function authorize(credentials: any, token_path: string): Promise { + const { client_secret, client_id, redirect_uris } = credentials.installed; + const oAuth2Client = new google.auth.OAuth2( + client_id, client_secret, redirect_uris[0]); + + return new Promise((resolve, reject) => { + readFile(token_path, (err, token) => { + // Check if we have previously stored a token. + if (err) { + return getNewToken(oAuth2Client, token_path).then(resolve, reject); + } + oAuth2Client.setCredentials(parseBuffer(token)); + resolve(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: OAuth2Client, token_path: string) { + return new Promise((resolve, reject) => { + const authUrl = oAuth2Client.generateAuthUrl({ + access_type: 'offline', + scope: SCOPES.map(relative => prefix + relative), + }); + console.log('Authorize this app by visiting this url:', authUrl); + const rl = 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 || !token) { + reject(err); + return console.error('Error retrieving access token', err); + } + oAuth2Client.setCredentials(token); + // Store the token to disk for later program executions + writeFile(token_path, JSON.stringify(token), (err) => { + if (err) { + console.error(err); + reject(err); + } + console.log('Token stored to', token_path); + }); + resolve(oAuth2Client); + }); + }); + }); + } + +} \ No newline at end of file diff --git a/src/server/apis/youtube/youtubeApiSample.d.ts b/src/server/apis/youtube/youtubeApiSample.d.ts new file mode 100644 index 000000000..427f54608 --- /dev/null +++ b/src/server/apis/youtube/youtubeApiSample.d.ts @@ -0,0 +1,2 @@ +declare const YoutubeApi: any; +export = YoutubeApi; \ No newline at end of file diff --git a/src/server/apis/youtube/youtubeApiSample.js b/src/server/apis/youtube/youtubeApiSample.js new file mode 100644 index 000000000..50b3c7b38 --- /dev/null +++ b/src/server/apis/youtube/youtubeApiSample.js @@ -0,0 +1,179 @@ +const fs = require('fs'); +const readline = require('readline'); +const { google } = require('googleapis'); +const OAuth2 = google.auth.OAuth2; + + +// If modifying these scopes, delete your previously saved credentials +// at ~/.credentials/youtube-nodejs-quickstart.json +let SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']; +let TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH || + process.env.USERPROFILE) + '/.credentials/'; +let TOKEN_PATH = TOKEN_DIR + 'youtube-nodejs-quickstart.json'; + +module.exports.readApiKey = (callback) => { + fs.readFile('client_secret.json', function processClientSecrets(err, content) { + if (err) { + console.log('Error loading client secret file: ' + err); + return; + } + callback(content); + }); +} + +module.exports.authorizedGetChannel = (apiKey) => { + //this didnt get called + // Authorize a client with the loaded credentials, then call the YouTube API. + authorize(JSON.parse(apiKey), getChannel); +} + +module.exports.authorizedGetVideos = (apiKey, userInput, callBack) => { + authorize(JSON.parse(apiKey), getVideos, { userInput: userInput, callBack: callBack }); +} + +module.exports.authorizedGetVideoDetails = (apiKey, videoIds, callBack) => { + authorize(JSON.parse(apiKey), getVideoDetails, { videoIds: videoIds, callBack: callBack }); +} + + +/** + * 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, args = {}) { + let clientSecret = credentials.installed.client_secret; + let clientId = credentials.installed.client_id; + let redirectUrl = credentials.installed.redirect_uris[0]; + let 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, args); + } + }); +} + +/** + * 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); + }); + 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); + } + }); +} + +function getVideos(auth, args) { + let service = google.youtube('v3'); + service.search.list({ + auth: auth, + part: 'id, snippet', + type: 'video', + q: args.userInput, + maxResults: 10 + }, function (err, response) { + if (err) { + console.log('The API returned an error: ' + err); + return; + } + let videos = response.data.items; + args.callBack(videos); + }); +} + +function getVideoDetails(auth, args) { + if (args.videoIds === undefined) { + return; + } + let service = google.youtube('v3'); + service.videos.list({ + auth: auth, + part: 'contentDetails, statistics', + id: args.videoIds + }, function (err, response) { + if (err) { + console.log('The API returned an error from details: ' + err); + return; + } + let videoDetails = response.data.items; + args.callBack(videoDetails); + }); +} \ No newline at end of file diff --git a/src/server/credentials/google_docs_credentials.json b/src/server/credentials/google_docs_credentials.json new file mode 100644 index 000000000..8d097d363 --- /dev/null +++ b/src/server/credentials/google_docs_credentials.json @@ -0,0 +1 @@ +{"installed":{"client_id":"343179513178-ud6tvmh275r2fq93u9eesrnc66t6akh9.apps.googleusercontent.com","project_id":"quickstart-1565056383187","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"w8KIFSc0MQpmUYHed4qEzn8b","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}} \ No newline at end of file diff --git a/src/server/credentials/google_docs_token.json b/src/server/credentials/google_docs_token.json new file mode 100644 index 000000000..07c02d56c --- /dev/null +++ b/src/server/credentials/google_docs_token.json @@ -0,0 +1 @@ +{"access_token":"ya29.GltjB4-x03xFpd2NY2555cxg1xlT_ajqRi78M9osOfdOF2jTIjlPkn_UZL8cUwVP0DPC8rH3vhhg8RpspFe8Vewx92shAO3RPos_uMH0CUqEiCiZlaaB5I3Jq3Mv","refresh_token":"1/teUKUqGKMLjVqs-eed0L8omI02pzSxMUYaxGc2QxBw0","scope":"https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents.readonly","token_type":"Bearer","expiry_date":1565654175862} \ No newline at end of file diff --git a/src/server/index.ts b/src/server/index.ts index eae018f13..9986e62d6 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -14,7 +14,6 @@ import * as mobileDetect from 'mobile-detect'; import * as passport from 'passport'; import * as path from 'path'; import * as request from 'request'; -import * as rp from 'request-promise'; import * as io from 'socket.io'; import { Socket } from 'socket.io'; import * as webpack from 'webpack'; @@ -36,19 +35,16 @@ const port = 1050; // default port to listen const serverPort = 4321; import expressFlash = require('express-flash'); import flash = require('connect-flash'); -import c = require("crypto"); import { Search } from './Search'; -import { debug } from 'util'; import _ = require('lodash'); import * as Archiver from 'archiver'; -import * as AdmZip from 'adm-zip'; -import * as YoutubeApi from './youtubeApi/youtubeApiSample.js'; +import AdmZip from 'adm-zip'; +import * as YoutubeApi from "./apis/youtube/youtubeApiSample"; import { Response } from 'express-serve-static-core'; +import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); const probe = require("probe-image-size"); -var SolrNode = require('solr-node'); -var shell = require('shelljs'); const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest)); let youtubeApiKey: string; @@ -790,6 +786,29 @@ function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any } } +const credentials = path.join(__dirname, "./credentials/google_docs_credentials.json"); +const token = path.join(__dirname, "./credentials/google_docs_token.json"); + +app.post(RouteStore.googleDocs + ":action", (req, res) => { + let parameters = req.body; + console.log(parameters, req.params.action); + + GoogleApiServerUtils.Docs.GetEndpoint({ credentials, token }).then(endpoint => { + let results: Promise | undefined; + switch (req.params.action) { + case "create": + results = endpoint.documents.create(parameters).then(generated => generated.data.documentId); + break; + case "retrieve": + results = endpoint.documents.get(parameters).then(response => response.data); + break; + default: + results = undefined; + } + results && results.then(final => res.send(final)); + }); +}); + const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = { "number": "_n", "string": "_t", diff --git a/src/server/youtubeApi/youtubeApiSample.d.ts b/src/server/youtubeApi/youtubeApiSample.d.ts deleted file mode 100644 index 427f54608..000000000 --- a/src/server/youtubeApi/youtubeApiSample.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const YoutubeApi: any; -export = YoutubeApi; \ No newline at end of file diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js deleted file mode 100644 index 50b3c7b38..000000000 --- a/src/server/youtubeApi/youtubeApiSample.js +++ /dev/null @@ -1,179 +0,0 @@ -const fs = require('fs'); -const readline = require('readline'); -const { google } = require('googleapis'); -const OAuth2 = google.auth.OAuth2; - - -// If modifying these scopes, delete your previously saved credentials -// at ~/.credentials/youtube-nodejs-quickstart.json -let SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']; -let TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH || - process.env.USERPROFILE) + '/.credentials/'; -let TOKEN_PATH = TOKEN_DIR + 'youtube-nodejs-quickstart.json'; - -module.exports.readApiKey = (callback) => { - fs.readFile('client_secret.json', function processClientSecrets(err, content) { - if (err) { - console.log('Error loading client secret file: ' + err); - return; - } - callback(content); - }); -} - -module.exports.authorizedGetChannel = (apiKey) => { - //this didnt get called - // Authorize a client with the loaded credentials, then call the YouTube API. - authorize(JSON.parse(apiKey), getChannel); -} - -module.exports.authorizedGetVideos = (apiKey, userInput, callBack) => { - authorize(JSON.parse(apiKey), getVideos, { userInput: userInput, callBack: callBack }); -} - -module.exports.authorizedGetVideoDetails = (apiKey, videoIds, callBack) => { - authorize(JSON.parse(apiKey), getVideoDetails, { videoIds: videoIds, callBack: callBack }); -} - - -/** - * 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, args = {}) { - let clientSecret = credentials.installed.client_secret; - let clientId = credentials.installed.client_id; - let redirectUrl = credentials.installed.redirect_uris[0]; - let 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, args); - } - }); -} - -/** - * 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); - }); - 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); - } - }); -} - -function getVideos(auth, args) { - let service = google.youtube('v3'); - service.search.list({ - auth: auth, - part: 'id, snippet', - type: 'video', - q: args.userInput, - maxResults: 10 - }, function (err, response) { - if (err) { - console.log('The API returned an error: ' + err); - return; - } - let videos = response.data.items; - args.callBack(videos); - }); -} - -function getVideoDetails(auth, args) { - if (args.videoIds === undefined) { - return; - } - let service = google.youtube('v3'); - service.videos.list({ - auth: auth, - part: 'contentDetails, statistics', - id: args.videoIds - }, function (err, response) { - if (err) { - console.log('The API returned an error from details: ' + err); - return; - } - let videoDetails = response.data.items; - args.callBack(videoDetails); - }); -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 278cabf67a77edb3dd3bd9bb392550eeb08ab910 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 13 Aug 2019 02:06:32 -0400 Subject: more util functions and clean up --- .../apis/google_docs/GoogleApiClientUtils.ts | 71 +++++++++++++++++++--- src/client/views/MainView.tsx | 24 +++++--- src/server/index.ts | 1 - 3 files changed, 78 insertions(+), 18 deletions(-) (limited to 'src/server') diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 42e621f6b..71e5e1073 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -1,6 +1,7 @@ import { docs_v1 } from "googleapis"; import { PostToServer } from "../../../Utils"; import { RouteStore } from "../../../server/RouteStore"; +import { Opt } from "../../../new_fields/Doc"; export namespace GoogleApiClientUtils { @@ -11,33 +12,76 @@ export namespace GoogleApiClientUtils { Retrieve = "retrieve" } - export namespace Helpers { + export namespace Utils { export const fromRgb = (red: number, green: number, blue: number) => { return { color: { rgbColor: { red, green, blue } } }; }; + export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false) => { + let fragments: string[] = []; + if (document.body && document.body.content) { + for (let element of document.body.content) { + if (element.paragraph && element.paragraph.elements) { + for (let inner of element.paragraph.elements) { + if (inner && inner.textRun) { + let fragment = inner.textRun.content; + fragment && fragments.push(fragment); + } + } + } + } + } + let text = fragments.join(""); + return removeNewlines ? text.ReplaceAll("\n", "") : text; + }; + } export const ExampleDocumentSchema = { title: "This is a Google Doc Created From Dash Web", body: { content: [ + { + endIndex: 1, + sectionBreak: { + sectionStyle: { + columnSeparatorStyle: "NONE", + contentDirection: "LEFT_TO_RIGHT" + } + } + }, { paragraph: { elements: [ { textRun: { - content: "And this is its bold, blue text!!!", + content: "And this is its bold, blue text!!!\n", textStyle: { bold: true, - backgroundColor: Helpers.fromRgb(0, 0, 1) + backgroundColor: Utils.fromRgb(0, 0, 1) } } } ] } - } + }, + { + paragraph: { + elements: [ + { + textRun: { + content: "And this is its bold, blue text!!!\n", + textStyle: { + bold: true, + backgroundColor: Utils.fromRgb(0, 0, 1) + } + } + } + ] + } + }, + ] as docs_v1.Schema$StructuralElement[] } } as docs_v1.Schema$Document; @@ -66,20 +110,27 @@ export namespace GoogleApiClientUtils { } }; - let path = RouteStore.googleDocs + Actions.Retrieve; - export const Retrieve = async (documentId: string): Promise => { + export const Read = async (documentId: string, removeNewlines = false): Promise> => { + return Retrieve(documentId).then(schema => { + return schema ? Utils.extractText(schema, removeNewlines) : undefined; + }); + }; + + export const Retrieve = async (documentId: string): Promise> => { + let path = RouteStore.googleDocs + Actions.Retrieve; let parameters = { documentId }; - let documentContents: any; + let schema: Opt; try { - documentContents = await PostToServer(path, parameters); + schema = await PostToServer(path, parameters); } catch (e) { console.error(e); - documentContents = undefined; + schema = undefined; } finally { - return documentContents; + return schema; } }; + } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0a987de4a..3a5285a66 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -87,6 +87,11 @@ export class MainView extends React.Component { if (!("presentationView" in doc)) { doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })]); } + if (!("googleDocId" in doc)) { + GoogleApiClientUtils.Docs.Create().then(id => { + id && (doc.googleDocId = id); + }); + } CurrentUserUtils.UserDocument.activeWorkspace = doc; } } @@ -132,8 +137,6 @@ export class MainView extends React.Component { window.removeEventListener("keydown", KeyManager.Instance.handle); window.addEventListener("keydown", KeyManager.Instance.handle); - GoogleApiClientUtils.Docs.Create().then(id => console.log(id)); - reaction(() => { let workspaces = CurrentUserUtils.UserDocument.workspaces; let recent = CurrentUserUtils.UserDocument.recentlyClosed; @@ -151,6 +154,18 @@ export class MainView extends React.Component { }, { fireImmediately: true }); } + componentDidMount() { + reaction(() => this.mainContainer, () => { + let main = this.mainContainer, googleDocId; + if (main && (googleDocId = StrCast(main.googleDocId))) { + GoogleApiClientUtils.Docs.Read(googleDocId, true).then(text => { + text && Utils.CopyText(text); + console.log(text); + }); + } + }); + } + componentWillUnMount() { window.removeEventListener("keydown", KeyManager.Instance.handle); window.removeEventListener("pointerdown", this.globalPointerDown); @@ -573,11 +588,6 @@ export class MainView extends React.Component { render() { return (
- { - if (e.which === 13) { - GoogleApiClientUtils.Docs.Retrieve(e.currentTarget.value.trim()).then((res: any) => console.log(res)); - } - }} /> {this.dictationOverlay} {this.mainContent} diff --git a/src/server/index.ts b/src/server/index.ts index 9986e62d6..4ccb61681 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -791,7 +791,6 @@ const token = path.join(__dirname, "./credentials/google_docs_token.json"); app.post(RouteStore.googleDocs + ":action", (req, res) => { let parameters = req.body; - console.log(parameters, req.params.action); GoogleApiServerUtils.Docs.GetEndpoint({ credentials, token }).then(endpoint => { let results: Promise | undefined; -- cgit v1.2.3-70-g09d2 From 2b829d1028a61869858ecd48e2f1801819e17488 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 13 Aug 2019 22:15:47 -0400 Subject: robust google doc export api and cleanup --- .../apis/google_docs/GoogleApiClientUtils.ts | 185 ++++++++++++--------- src/client/views/MainView.tsx | 11 +- src/client/views/nodes/FormattedTextBox.tsx | 24 ++- src/new_fields/RichTextField.ts | 13 ++ src/server/index.ts | 25 ++- 5 files changed, 162 insertions(+), 96 deletions(-) (limited to 'src/server') diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 5e974b2e7..dda36f05a 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -1,7 +1,8 @@ import { docs_v1 } from "googleapis"; import { PostToServer } from "../../../Utils"; import { RouteStore } from "../../../server/RouteStore"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, Doc } from "../../../new_fields/Doc"; +import { isArray } from "util"; export namespace GoogleApiClientUtils { @@ -9,15 +10,12 @@ export namespace GoogleApiClientUtils { export enum Actions { Create = "create", - Retrieve = "retrieve" + Retrieve = "retrieve", + Update = "update" } export namespace Utils { - export const fromRgb = (red: number, green: number, blue: number) => { - return { color: { rgbColor: { red, green, blue } } }; - }; - export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false) => { let fragments: string[] = []; if (document.body && document.body.content) { @@ -36,55 +34,36 @@ export namespace GoogleApiClientUtils { return removeNewlines ? text.ReplaceAll("\n", "") : text; }; - } - - export const ExampleDocumentSchema = { - title: "This is a Google Doc Created From Dash Web", - body: { - content: [ - { - endIndex: 1, - sectionBreak: { - sectionStyle: { - columnSeparatorStyle: "NONE", - contentDirection: "LEFT_TO_RIGHT" + export const EndOf = (schema: docs_v1.Schema$Document): Opt => { + if (schema.body && schema.body.content) { + let paragraphs = schema.body.content.filter(el => el.paragraph); + if (paragraphs.length) { + let target = paragraphs[paragraphs.length - 1]; + if (target.paragraph && target.paragraph.elements) { + length = target.paragraph.elements.length; + if (length) { + let final = target.paragraph.elements[length - 1]; + return final.endIndex ? final.endIndex - 1 : undefined; } } - }, - { - paragraph: { - elements: [ - { - textRun: { - content: "And this is its bold, blue text!!!\n", - textStyle: { - bold: true, - backgroundColor: Utils.fromRgb(0, 0, 1) - } - } - } - ] - } - }, - { - paragraph: { - elements: [ - { - textRun: { - content: "And this is its bold, blue text!!!\n", - textStyle: { - bold: true, - backgroundColor: Utils.fromRgb(0, 0, 1) - } - } - } - ] - } - }, + } + } + }; - ] as docs_v1.Schema$StructuralElement[] - } - } as docs_v1.Schema$Document; + } + + export interface ReadOptions { + documentId: string; + removeNewlines?: boolean; + } + + export interface WriteOptions { + documentId?: string; + title?: string; + content: string | string[]; + index?: number; + store?: { receiver: Doc, key: string }; + } /** * After following the authentication routine, which connects this API call to the current signed in account @@ -96,45 +75,95 @@ export namespace GoogleApiClientUtils { * actual document body and its styling! * @returns the documentId of the newly generated document, or undefined if the creation process fails. */ - export const Create = async (schema?: docs_v1.Schema$Document): Promise => { + const Create = async (title?: string): Promise => { let path = RouteStore.googleDocs + Actions.Create; - let parameters = { requestBody: schema || ExampleDocumentSchema }; - let generatedId: string | undefined; + let parameters = { + requestBody: { + title: title || `Dash Export (${new Date().toDateString()})` + } + }; try { - generatedId = await PostToServer(path, parameters); - } catch (e) { - console.error(e); - generatedId = undefined; - } finally { - return generatedId; + let schema: docs_v1.Schema$Document = await PostToServer(path, parameters); + return schema.documentId; + } catch { + return undefined; } }; - export interface ReadOptions { - documentId: string; - removeNewlines?: boolean; - } + const Retrieve = async (documentId: string): Promise => { + let path = RouteStore.googleDocs + Actions.Retrieve; + let parameters = { + documentId + }; + try { + let schema: docs_v1.Schema$Document = await PostToServer(path, parameters); + return schema; + } catch { + return undefined; + } + }; + + const Update = async (documentId: string, requests: docs_v1.Schema$Request[]): Promise => { + let path = RouteStore.googleDocs + Actions.Update; + let parameters = { + documentId, + requestBody: { + requests + } + }; + try { + let replies: docs_v1.Schema$BatchUpdateDocumentResponse = await PostToServer(path, parameters); + console.log(replies); + return replies; + } catch { + return undefined; + } + }; - export const Read = async (options: ReadOptions): Promise> => { + export const Read = async (options: ReadOptions): Promise => { return Retrieve(options.documentId).then(schema => { return schema ? Utils.extractText(schema, options.removeNewlines) : undefined; }); }; - export const Retrieve = async (documentId: string): Promise> => { - let path = RouteStore.googleDocs + Actions.Retrieve; - let parameters = { documentId }; - let schema: Opt; - try { - schema = await PostToServer(path, parameters); - } catch (e) { - console.error(e); - schema = undefined; - } finally { - return schema; - } + export const ReadLines = async (options: ReadOptions) => { + return Retrieve(options.documentId).then(schema => { + if (!schema) { + return undefined; + } + let lines = Utils.extractText(schema).split("\n"); + return options.removeNewlines ? lines.filter(line => line.length) : lines; + }); }; + export const Write = async (options: WriteOptions): Promise => { + let target = options.documentId; + if (!target) { + if (!(target = await Create(options.title))) { + return undefined; + } + } + let index = options.index; + if (!index) { + let schema = await Retrieve(target); + if (!schema || !(index = Utils.EndOf(schema))) { + return undefined; + } + } + let text = options.content; + let request = { + insertText: { + text: isArray(text) ? text.join("\n") : text, + location: { index } + } + }; + return Update(target, [request]).then(res => { + if (res && options.store) { + options.store.receiver[options.store.key] = res.documentId; + } + return res; + }); + }; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 705eacc0c..7b15e9624 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -87,11 +87,6 @@ export class MainView extends React.Component { if (!("presentationView" in doc)) { doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })]); } - if (!("googleDocId" in doc)) { - GoogleApiClientUtils.Docs.Create().then(id => { - id && (doc.googleDocId = id); - }); - } CurrentUserUtils.UserDocument.activeWorkspace = doc; } } @@ -158,9 +153,9 @@ export class MainView extends React.Component { reaction(() => this.mainContainer, () => { let main = this.mainContainer, documentId; if (main && (documentId = StrCast(main.googleDocId))) { - GoogleApiClientUtils.Docs.Read({ documentId }).then(text => { - text && Utils.CopyText(text); - console.log(text); + let options = { documentId, removeNewlines: true }; + GoogleApiClientUtils.Docs.ReadLines(options).then(lines => { + console.log(lines); }); } }); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 44b5d2c21..8c2af7c9e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit, faSmile, faTextHeight } from '@fortawesome/free-solid-svg-icons'; +import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons'; import { action, IReactionDisposer, observable, reaction, runInAction, computed, Lambda, trace } from "mobx"; import { observer } from "mobx-react"; import { baseKeymap } from "prosemirror-commands"; @@ -38,9 +38,10 @@ import { For } from 'babel-types'; import { DateField } from '../../../new_fields/DateField'; import { Utils } from '../../../Utils'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; +import { GoogleApiClientUtils } from '../../apis/google_docs/GoogleApiClientUtils'; library.add(faEdit); -library.add(faSmile, faTextHeight); +library.add(faSmile, faTextHeight, faUpload); // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document // @@ -58,6 +59,8 @@ const richTextSchema = createSchema({ documentText: "string" }); +const googleDocKey = "googleDocId"; + type RichTextDocument = makeInterface<[typeof richTextSchema]>; const RichTextDocument = makeInterface(richTextSchema); @@ -661,7 +664,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" }); + if (!(googleDocKey in Doc.GetProto(this.props.Document))) { + ContextMenu.Instance.addItem({ description: "Export to Google Doc...", event: this.exportToGoogleDoc, icon: "upload" }); + } } + + exportToGoogleDoc = () => { + let dataDoc = Doc.GetProto(this.props.Document); + let data = Cast(dataDoc.data, RichTextField); + let content: string | undefined; + if (data && (content = data.plainText())) { + GoogleApiClientUtils.Docs.Write({ + title: StrCast(dataDoc.title), + store: { receiver: dataDoc, key: googleDocKey }, + content + }); + } + } + render() { let self = this; let style = this.props.isOverlay ? "scroll" : "hidden"; diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 89799b2af..dc66813e0 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -9,6 +9,7 @@ import { scriptingGlobal } from "../client/util/Scripting"; export class RichTextField extends ObjectField { @serializable(true) readonly Data: string; + private Extractor = /,\"text\":\"([^\"\}]*)\"\}/g; constructor(data: string) { super(); @@ -22,4 +23,16 @@ export class RichTextField extends ObjectField { [ToScriptString]() { return `new RichTextField("${this.Data}")`; } + + plainText = () => { + let contents = ""; + let matches: RegExpExecArray | null; + let considering = this.Data; + while ((matches = this.Extractor.exec(considering)) !== null) { + contents += matches[1]; + considering = considering.substring(matches.index + matches[0].length); + this.Extractor.lastIndex = 0; + } + return contents.length ? contents : undefined; + } } \ No newline at end of file diff --git a/src/server/index.ts b/src/server/index.ts index 4ccb61681..abaa29658 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -42,6 +42,7 @@ import AdmZip from 'adm-zip'; import * as YoutubeApi from "./apis/youtube/youtubeApiSample"; import { Response } from 'express-serve-static-core'; import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; +import { GaxiosResponse } from 'gaxios'; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); const probe = require("probe-image-size"); @@ -170,6 +171,13 @@ const read_text_file = (relativePath: string) => { }); }; +const write_text_file = (relativePath: string, contents: any) => { + let target = path.join(__dirname, relativePath); + return new Promise((resolve, reject) => { + fs.writeFile(target, contents, (err) => err ? reject(err) : resolve()); + }); +}; + app.get("/version", (req, res) => { exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout, stderr) => { if (err) { @@ -790,21 +798,22 @@ const credentials = path.join(__dirname, "./credentials/google_docs_credentials. const token = path.join(__dirname, "./credentials/google_docs_token.json"); app.post(RouteStore.googleDocs + ":action", (req, res) => { - let parameters = req.body; - GoogleApiServerUtils.Docs.GetEndpoint({ credentials, token }).then(endpoint => { - let results: Promise | undefined; + let results: Promise | undefined; + let documents = endpoint.documents; + let parameters = req.body; switch (req.params.action) { case "create": - results = endpoint.documents.create(parameters).then(generated => generated.data.documentId); + results = documents.create(parameters); break; case "retrieve": - results = endpoint.documents.get(parameters).then(response => response.data); + results = documents.get(parameters); + break; + case "update": + results = documents.batchUpdate(parameters); break; - default: - results = undefined; } - results && results.then(final => res.send(final)); + !results ? res.send(undefined) : results.then(response => res.send(response.data)); }); }); -- cgit v1.2.3-70-g09d2 From 48fcec82fa384ec260a02965f9f78c2e41256dd9 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 14 Aug 2019 03:41:11 -0400 Subject: clean up and regex improvement --- .../apis/google_docs/GoogleApiClientUtils.ts | 111 ++++++++++++--------- src/client/views/nodes/FormattedTextBox.tsx | 15 ++- src/new_fields/RichTextField.ts | 4 +- src/server/index.ts | 35 ++++--- 4 files changed, 99 insertions(+), 66 deletions(-) (limited to 'src/server') diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index f4fb87e0b..c6c7d7bd4 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -1,7 +1,7 @@ import { docs_v1 } from "googleapis"; import { PostToServer } from "../../../Utils"; import { RouteStore } from "../../../server/RouteStore"; -import { Opt, Doc } from "../../../new_fields/Doc"; +import { Opt } from "../../../new_fields/Doc"; import { isArray } from "util"; export namespace GoogleApiClientUtils { @@ -14,9 +14,42 @@ export namespace GoogleApiClientUtils { Update = "update" } + export type DocumentId = string; + export type Reference = DocumentId | CreateOptions; + export type TextContent = string | string[]; + export type IdHandler = (id: DocumentId) => any; + + export type CreationResult = Opt; + export type RetrievalResult = Opt; + export type UpdateResult = Opt; + export type ReadLinesResult = Opt; + export type ReadResult = Opt; + + export interface CreateOptions { + handler: IdHandler; // callback to process the documentId of the newly created Google Doc + title?: string; // if excluded, will use a default title annotated with the current date + } + + export interface RetrieveOptions { + documentId: DocumentId; + } + + export type ReadOptions = RetrieveOptions & { removeNewlines?: boolean }; + + export interface WriteOptions { + content: TextContent; + reference: Reference; + index?: number; // if excluded, will compute the last index of the document and append the content there + } + + export interface UpdateOptions { + documentId: DocumentId; + requests: docs_v1.Schema$Request[]; + } + export namespace Utils { - export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false) => { + export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false): string => { const fragments: string[] = []; if (document.body && document.body.content) { for (const element of document.body.content) { @@ -34,7 +67,7 @@ export namespace GoogleApiClientUtils { return removeNewlines ? text.ReplaceAll("\n", "") : text; }; - export const EndOf = (schema: docs_v1.Schema$Document): Opt => { + export const endOf = (schema: docs_v1.Schema$Document): number | undefined => { if (schema.body && schema.body.content) { const paragraphs = schema.body.content.filter(el => el.paragraph); if (paragraphs.length) { @@ -50,27 +83,8 @@ export namespace GoogleApiClientUtils { } }; - } + export const initialize = async (reference: Reference) => typeof reference === "string" ? reference : create(reference); - export type IdHandler = (id: DocumentId) => any; - export interface CreateOptions { - handler: IdHandler; - // if excluded, will use a default title annotated with the current date - title?: string; - } - - export interface ReadOptions { - documentId: string; - // if exluded, will preserve newlines - removeNewlines?: boolean; - } - - export type DocumentId = string; - export interface WriteOptions { - content: string | string[]; - reference: DocumentId | CreateOptions; - // if excluded, will compute the last index of the document and append the content there - index?: number; } /** @@ -82,7 +96,7 @@ export namespace GoogleApiClientUtils { * to store the new documentId returned from the creation process * @returns the documentId of the newly generated document, or undefined if the creation process fails. */ - const Create = async (options: CreateOptions): Promise => { + export const create = async (options: CreateOptions): Promise => { const path = RouteStore.googleDocs + Actions.Create; const parameters = { requestBody: { @@ -101,43 +115,40 @@ export namespace GoogleApiClientUtils { } }; - const Retrieve = async (documentId: string): Promise => { + export const retrieve = async (options: RetrieveOptions): Promise => { const path = RouteStore.googleDocs + Actions.Retrieve; - const parameters = { - documentId - }; try { - const schema: docs_v1.Schema$Document = await PostToServer(path, parameters); + const schema: RetrievalResult = await PostToServer(path, options); return schema; } catch { return undefined; } }; - const Update = async (documentId: string, requests: docs_v1.Schema$Request[]): Promise => { + export const update = async (options: UpdateOptions): Promise => { const path = RouteStore.googleDocs + Actions.Update; const parameters = { - documentId, + documentId: options.documentId, requestBody: { - requests + requests: options.requests } }; try { - const replies: docs_v1.Schema$BatchUpdateDocumentResponse = await PostToServer(path, parameters); + const replies: UpdateResult = await PostToServer(path, parameters); return replies; } catch { return undefined; } }; - export const Read = async (options: ReadOptions): Promise => { - return Retrieve(options.documentId).then(schema => { + export const read = async (options: ReadOptions): Promise => { + return retrieve(options).then(schema => { return schema ? Utils.extractText(schema, options.removeNewlines) : undefined; }); }; - export const ReadLines = async (options: ReadOptions) => { - return Retrieve(options.documentId).then(schema => { + export const readLines = async (options: ReadOptions): Promise => { + return retrieve(options).then(schema => { if (!schema) { return undefined; } @@ -146,27 +157,29 @@ export namespace GoogleApiClientUtils { }); }; - export const Write = async (options: WriteOptions): Promise => { - let documentId: string | undefined; - const ref = options.reference; - if (!(documentId = typeof ref === "string" ? ref : await Create(ref))) { + export const write = async (options: WriteOptions): Promise => { + const documentId = await Utils.initialize(options.reference); + if (!documentId) { return undefined; } let index = options.index; if (!index) { - let schema = await Retrieve(documentId); - if (!schema || !(index = Utils.EndOf(schema))) { + let schema = await retrieve({ documentId }); + if (!schema || !(index = Utils.endOf(schema))) { return undefined; } } const text = options.content; - const request = { - insertText: { - text: isArray(text) ? text.join("\n") : text, - location: { index } - } + const updateOptions = { + documentId, + requests: [{ + insertText: { + text: isArray(text) ? text.join("\n") : text, + location: { index } + } + }] }; - return Update(documentId, [request]); + return update(updateOptions); }; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 50ec27259..46aed9b2d 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -288,6 +288,19 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + componentWillMount() { + this.pollExportedCounterpart(); + } + + pollExportedCounterpart = async () => { + let dataDoc = Doc.GetProto(this.props.Document); + let documentId = StrCast(dataDoc[googleDocKey]); + if (documentId) { + let contents = await GoogleApiClientUtils.Docs.read({ documentId }); + contents ? console.log(contents) : delete dataDoc[googleDocKey]; + } + } + componentDidMount() { const config = { schema, @@ -675,7 +688,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (!data) { return; } - GoogleApiClientUtils.Docs.Write({ + GoogleApiClientUtils.Docs.write({ reference: { title: StrCast(dataDoc.title), handler: id => dataDoc[googleDocKey] = id diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 3e8803a34..4f782816c 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -9,7 +9,7 @@ import { scriptingGlobal } from "../client/util/Scripting"; export class RichTextField extends ObjectField { @serializable(true) readonly Data: string; - private Extractor = /,\"text\":\"([^\"\}]*)\"\}/g; + private Extractor = /,\"text\":\"([^\}]*)\"\}/g; constructor(data: string) { super(); @@ -33,6 +33,6 @@ export class RichTextField extends ObjectField { considering = considering.substring(matches.index + matches[0].length); this.Extractor.lastIndex = 0; } - return contents; + return contents.ReplaceAll("\\", ""); } } \ No newline at end of file diff --git a/src/server/index.ts b/src/server/index.ts index abaa29658..ef1829f30 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -43,6 +43,8 @@ import * as YoutubeApi from "./apis/youtube/youtubeApiSample"; import { Response } from 'express-serve-static-core'; import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; import { GaxiosResponse } from 'gaxios'; +import { Opt } from '../new_fields/Doc'; +import { docs_v1 } from 'googleapis'; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); const probe = require("probe-image-size"); @@ -797,23 +799,28 @@ function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any const credentials = path.join(__dirname, "./credentials/google_docs_credentials.json"); const token = path.join(__dirname, "./credentials/google_docs_token.json"); +type ApiResponse = Promise; +type ApiHandler = (endpoint: docs_v1.Resource$Documents, parameters: any) => ApiResponse; +type Action = "create" | "retrieve" | "update"; + +const EndpointHandlerMap = new Map([ + ["create", (api, params) => api.create(params)], + ["retrieve", (api, params) => api.get(params)], + ["update", (api, params) => api.batchUpdate(params)], +]); + app.post(RouteStore.googleDocs + ":action", (req, res) => { GoogleApiServerUtils.Docs.GetEndpoint({ credentials, token }).then(endpoint => { - let results: Promise | undefined; - let documents = endpoint.documents; - let parameters = req.body; - switch (req.params.action) { - case "create": - results = documents.create(parameters); - break; - case "retrieve": - results = documents.get(parameters); - break; - case "update": - results = documents.batchUpdate(parameters); - break; + let handler = EndpointHandlerMap.get(req.params.action); + if (handler) { + let execute = handler(endpoint.documents, req.body).then( + response => res.send(response.data), + rejection => res.send(rejection) + ); + execute.catch(exception => res.send(exception)); + return; } - !results ? res.send(undefined) : results.then(response => res.send(response.data)); + res.send(undefined); }); }); -- cgit v1.2.3-70-g09d2