aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-08-21 20:01:35 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-08-21 20:01:35 -0400
commitf4888ec4b2862cdf890ac1b0f6670b41398266df (patch)
tree76e00e66bae3ccbb4cc34e0bb8e2e9510f5c67c7
parent88454c8163115b1396a34f4836b5f6f04817a3f0 (diff)
can create google slides presentations
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts99
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx20
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts65
-rw-r--r--src/server/index.ts19
5 files changed, 123 insertions, 84 deletions
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 821c52270..52452cd95 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,4 +1,4 @@
-import { docs_v1 } from "googleapis";
+import { docs_v1, slides_v1 } from "googleapis";
import { PostToServer } from "../../../Utils";
import { RouteStore } from "../../../server/RouteStore";
import { Opt } from "../../../new_fields/Doc";
@@ -9,47 +9,47 @@ export const Pushes = "googleDocsPushCount";
export namespace GoogleApiClientUtils {
- export namespace Docs {
+ export enum Actions {
+ Create = "create",
+ Retrieve = "retrieve",
+ Update = "update"
+ }
- export enum Actions {
- Create = "create",
- Retrieve = "retrieve",
- Update = "update"
- }
+ export enum WriteMode {
+ Insert,
+ Replace
+ }
- export enum WriteMode {
- Insert,
- Replace
- }
+ export type DocumentId = string;
+ export type Reference = DocumentId | CreateOptions;
+ export type TextContent = string | string[];
+ export type IdHandler = (id: DocumentId) => any;
+ export type CreationResult = Opt<DocumentId>;
+ export type ReadLinesResult = Opt<{ title?: string, bodyLines?: string[] }>;
+ export type ReadResult = { title?: string, body?: string };
- export type DocumentId = string;
- export type Reference = DocumentId | CreateOptions;
- export type TextContent = string | string[];
- export type IdHandler = (id: DocumentId) => any;
+ export interface CreateOptions {
+ title?: string; // if excluded, will use a default title annotated with the current date
+ }
- export type CreationResult = Opt<DocumentId>;
- export type RetrievalResult = Opt<docs_v1.Schema$Document>;
- export type UpdateResult = Opt<docs_v1.Schema$BatchUpdateDocumentResponse>;
- export type ReadLinesResult = Opt<{ title?: string, bodyLines?: string[] }>;
- export type ReadResult = { title?: string, body?: string };
+ export interface RetrieveOptions {
+ documentId: DocumentId;
+ }
- 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 type ReadOptions = RetrieveOptions & { removeNewlines?: boolean };
- export interface RetrieveOptions {
- documentId: DocumentId;
- }
+ export interface WriteOptions {
+ mode: WriteMode;
+ content: TextContent;
+ reference: Reference;
+ index?: number; // if excluded, will compute the last index of the document and append the content there
+ }
- export type ReadOptions = RetrieveOptions & { removeNewlines?: boolean };
- export interface WriteOptions {
- mode: WriteMode;
- content: TextContent;
- reference: Reference;
- index?: number; // if excluded, will compute the last index of the document and append the content there
- }
+ export namespace Docs {
+
+ export type RetrievalResult = Opt<docs_v1.Schema$Document>;
+ export type UpdateResult = Opt<docs_v1.Schema$BatchUpdateDocumentResponse>;
export interface UpdateOptions {
documentId: DocumentId;
@@ -106,7 +106,7 @@ export namespace GoogleApiClientUtils {
* @returns the documentId of the newly generated document, or undefined if the creation process fails.
*/
export const create = async (options: CreateOptions): Promise<CreationResult> => {
- const path = RouteStore.googleDocs + Actions.Create;
+ const path = RouteStore.googleDocs + "Documents/" + Actions.Create;
const parameters = {
requestBody: {
title: options.title || `Dash Export (${new Date().toDateString()})`
@@ -115,17 +115,14 @@ export namespace GoogleApiClientUtils {
try {
const schema: docs_v1.Schema$Document = await PostToServer(path, parameters);
const generatedId = schema.documentId;
- if (generatedId) {
- options.handler(generatedId);
- return generatedId;
- }
+ return generatedId;
} catch {
return undefined;
}
};
export const retrieve = async (options: RetrieveOptions): Promise<RetrievalResult> => {
- const path = RouteStore.googleDocs + Actions.Retrieve;
+ const path = RouteStore.googleDocs + "Documents/" + Actions.Retrieve;
try {
const schema: RetrievalResult = await PostToServer(path, options);
return schema;
@@ -135,7 +132,7 @@ export namespace GoogleApiClientUtils {
};
export const update = async (options: UpdateOptions): Promise<UpdateResult> => {
- const path = RouteStore.googleDocs + Actions.Update;
+ const path = RouteStore.googleDocs + "Documents/" + Actions.Update;
const parameters = {
documentId: options.documentId,
requestBody: {
@@ -221,4 +218,24 @@ export namespace GoogleApiClientUtils {
}
+ export namespace Slides {
+
+ export const create = async (options: CreateOptions): Promise<CreationResult> => {
+ const path = RouteStore.googleDocs + "Slides/" + Actions.Create;
+ const parameters = {
+ requestBody: {
+ title: options.title || `Dash Export (${new Date().toDateString()})`
+ }
+ };
+ try {
+ const schema: slides_v1.Schema$Presentation = await PostToServer(path, parameters);
+ const generatedId = schema.presentationId;
+ return generatedId;
+ } catch {
+ return undefined;
+ }
+ };
+
+ }
+
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f28844009..3490a0f68 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,6 +40,7 @@ import { PreviewCursor } from './PreviewCursor';
import { FilterBox } from './search/FilterBox';
import PresModeMenu from './presentationview/PresentationModeMenu';
import { PresBox } from './nodes/PresBox';
+import { GoogleApiClientUtils } from '../apis/google_docs/GoogleApiClientUtils';
@observer
export class MainView extends React.Component {
@@ -120,6 +121,9 @@ export class MainView extends React.Component {
componentWillMount() {
var tag = document.createElement('script');
+ let title = "THIS IS MY FIRST DASH PRESENTATION";
+ GoogleApiClientUtils.Slides.create({ title }).then(id => console.log("We received this! ", id));
+
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c8722c6e3..408a6948e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -64,7 +64,7 @@ export const GoogleRef = "googleDocId";
type RichTextDocument = makeInterface<[typeof richTextSchema]>;
const RichTextDocument = makeInterface(richTextSchema);
-type PullHandler = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => void;
+type PullHandler = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => void;
@observer
export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
@@ -430,22 +430,20 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
pushToGoogleDoc = async () => {
- this.pullFromGoogleDoc(async (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => {
- let modes = GoogleApiClientUtils.Docs.WriteMode;
+ this.pullFromGoogleDoc(async (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => {
+ let modes = GoogleApiClientUtils.WriteMode;
let mode = modes.Replace;
- let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], "string");
+ let reference: Opt<GoogleApiClientUtils.Reference> = Cast(this.dataDoc[GoogleRef], "string");
if (!reference) {
mode = modes.Insert;
- reference = {
- title: StrCast(this.dataDoc.title),
- handler: id => this.dataDoc[GoogleRef] = id
- };
+ reference = { title: StrCast(this.dataDoc.title) };
}
let redo = async () => {
let data = Cast(this.dataDoc.data, RichTextField);
if (this._editorView && reference && data) {
let content = data[ToPlainText]();
let response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
+ response && (this.dataDoc[GoogleRef] = response.documentId);
let pushSuccess = response !== undefined && !("errors" in response);
dataDoc.unchanged = pushSuccess;
DocumentDecorations.Instance.startPushOutcome(pushSuccess);
@@ -465,14 +463,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
pullFromGoogleDoc = async (handler: PullHandler) => {
let dataDoc = this.dataDoc;
let documentId = StrCast(dataDoc[GoogleRef]);
- let exportState: GoogleApiClientUtils.Docs.ReadResult = {};
+ let exportState: GoogleApiClientUtils.ReadResult = {};
if (documentId) {
exportState = await GoogleApiClientUtils.Docs.read({ documentId });
}
UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
}
- updateState = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => {
+ updateState = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => {
let pullSuccess = false;
if (exportState !== undefined && exportState.body !== undefined && exportState.title !== undefined) {
const data = Cast(dataDoc.data, RichTextField);
@@ -496,7 +494,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
DocumentDecorations.Instance.startPullOutcome(pullSuccess);
}
- checkState = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => {
+ checkState = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => {
if (exportState !== undefined && exportState.body !== undefined && exportState.title !== undefined) {
let data = Cast(dataDoc.data, RichTextField);
if (data) {
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 817b2b696..ff027c501 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,7 +1,10 @@
-import { google, docs_v1 } from "googleapis";
+import { google, docs_v1, slides_v1 } from "googleapis";
import { createInterface } from "readline";
import { readFile, writeFile } from "fs";
import { OAuth2Client } from "google-auth-library";
+import { Opt } from "../../../new_fields/Doc";
+import { GlobalOptions } from "googleapis-common";
+import { GaxiosResponse } from "gaxios";
/**
* Server side authentication for Google Api queries.
@@ -13,38 +16,57 @@ export namespace GoogleApiServerUtils {
const SCOPES = [
'documents.readonly',
'documents',
+ 'presentations',
+ 'presentations.readonly',
'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 enum Sector {
+ Documents = "Documents",
+ Slides = "Slides"
+ }
+
- export interface CredentialPaths {
- credentials: string;
- token: string;
- }
+ export interface CredentialPaths {
+ credentials: string;
+ token: string;
+ }
- export type Endpoint = docs_v1.Docs;
+ export type ApiResponse = Promise<GaxiosResponse>;
+ export type ApiRouter = (endpoint: Endpoint, paramters: any) => ApiResponse;
+ export type ApiHandler = (parameters: any) => ApiResponse;
+ export type Action = "create" | "retrieve" | "update";
- export const GetEndpoint = async (paths: CredentialPaths) => {
- return new Promise<Endpoint>((resolve, reject) => {
- readFile(paths.credentials, (err, credentials) => {
- if (err) {
- reject(err);
- return console.log('Error loading client secret file:', err);
+ export type Endpoint = { get: ApiHandler, create: ApiHandler, batchUpdate: ApiHandler };
+ export type EndpointParameters = GlobalOptions & { version: "v1" };
+
+ export const GetEndpoint = async (sector: string, paths: CredentialPaths) => {
+ return new Promise<Opt<Endpoint>>((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 => {
+ let routed: Opt<Endpoint>;
+ let parameters: EndpointParameters = { auth, version: "v1" };
+ switch (sector) {
+ case Sector.Documents:
+ routed = google.docs(parameters).documents;
+ break;
+ case Sector.Slides:
+ routed = google.slides(parameters).presentations;
+ break;
}
- return authorize(parseBuffer(credentials), paths.token).then(auth => {
- resolve(google.docs({ version: "v1", auth }));
- });
+ resolve(routed);
});
});
- };
+ });
+ };
- }
/**
* Create an OAuth2 client with the given credentials, and returns the promise resolving to the authenticated client
@@ -105,5 +127,4 @@ export namespace GoogleApiServerUtils {
});
});
}
-
} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index ef1829f30..6aecb875a 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -45,6 +45,7 @@ import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
import { GaxiosResponse } from 'gaxios';
import { Opt } from '../new_fields/Doc';
import { docs_v1 } from 'googleapis';
+import { Endpoint } from 'googleapis-common';
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
const probe = require("probe-image-size");
@@ -799,21 +800,19 @@ 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<GaxiosResponse>;
-type ApiHandler = (endpoint: docs_v1.Resource$Documents, parameters: any) => ApiResponse;
-type Action = "create" | "retrieve" | "update";
-
-const EndpointHandlerMap = new Map<Action, ApiHandler>([
+const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["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 handler = EndpointHandlerMap.get(req.params.action);
- if (handler) {
- let execute = handler(endpoint.documents, req.body).then(
+app.post(RouteStore.googleDocs + ":sector/:action", (req, res) => {
+ let sector = req.params.sector;
+ let action = req.params.action;
+ GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Sector[sector], { credentials, token }).then(endpoint => {
+ let handler = EndpointHandlerMap.get(action);
+ if (endpoint && handler) {
+ let execute = handler(endpoint, req.body).then(
response => res.send(response.data),
rejection => res.send(rejection)
);