gristlabs_grist-core/app/server/lib/GoogleExport.ts
Dmitry S 51ff72c15e (core) Faster builds all around.
Summary:
Building:
- Builds no longer wait for tsc for either client, server, or test targets. All use esbuild which is very fast.
- Build still runs tsc, but only to report errors. This may be turned off with `SKIP_TSC=1` env var.
- Grist-core continues to build using tsc.
- Esbuild requires ES6 module semantics. Typescript's esModuleInterop is turned
  on, so that tsc accepts and enforces correct usage.
- Client-side code is watched and bundled by webpack as before (using esbuild-loader)

Code changes:
- Imports must now follow ES6 semantics: `import * as X from ...` produces a
  module object; to import functions or class instances, use `import X from ...`.
- Everything is now built with isolatedModules flag. Some exports were updated for it.

Packages:
- Upgraded browserify dependency, and related packages (used for the distribution-building step).
- Building the distribution now uses esbuild's minification. babel-minify is no longer used.

Test Plan: Should have no behavior changes, existing tests should pass, and docker image should build too.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: alexmojaki

Differential Revision: https://phab.getgrist.com/D3506
2022-07-04 10:42:40 -04:00

87 lines
3.0 KiB
TypeScript

import {drive} from '@googleapis/drive';
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
import {RequestWithLogin} from 'app/server/lib/Authorizer';
import {makeXLSX} from 'app/server/lib/ExportXLSX';
import log from 'app/server/lib/log';
import {optStringParam} from 'app/server/lib/requestUtils';
import {Request, Response} from 'express';
import {PassThrough} from 'stream';
/**
* Endpoint logic for sending grist document to Google Drive. Grist document is first exported as an
* excel file and then pushed to Google Drive as a Google Spreadsheet.
*/
export async function exportToDrive(
activeDoc: ActiveDoc,
req: Request,
res: Response
) {
// Token should come from auth middleware
const access_token = optStringParam(req.query.access_token);
if (!access_token) {
throw new Error("No access token - Can't send file to Google Drive");
}
const mreq = req as RequestWithLogin;
const meta = {
docId: activeDoc.docName,
userId: mreq.userId,
altSessionId: mreq.altSessionId,
};
// Prepare file for exporting.
log.debug(`Export to drive - Preparing file for export`, meta);
const { name, data } = await prepareFile(activeDoc, req);
try {
// Send file to GDrive and get the url for a preview.
const url = await sendFileToDrive(name, data, access_token);
log.debug(`Export to drive - File exported, redirecting to Google Spreadsheet ${url}`, meta);
res.json({ url });
} catch (err) {
log.error("Export to drive - Error while sending file to GDrive", meta, err);
// Test if google returned a valid error message.
if (err.errors && err.errors.length) {
throw new Error(err.errors[0].message);
} else {
throw err;
}
}
}
// Creates spreadsheet file in a Google drive, by sending an excel and requesting for conversion.
async function sendFileToDrive(fileNameNoExt: string, data: ArrayBuffer, oauth_token: string): Promise<string> {
// Here we are asking google drive to convert excel file to a google spreadsheet
const requestBody = {
// name of the spreadsheet to create
name: fileNameNoExt,
// mime type of the google spreadsheet
mimeType: 'application/vnd.google-apps.spreadsheet'
};
// wrap buffer into a stream
const stream = new PassThrough();
stream.end(data);
// Define what gets send - excel file
const media = {
mimeType: 'application/vnd.ms-excel',
body: stream
};
const googleDrive = drive("v3");
const fileRes = await googleDrive.files.create({
requestBody, // what to do with file - convert to spreadsheet
oauth_token, // access token
media, // file
fields: "webViewLink" // return webViewLink after creating file
});
const url = fileRes.data.webViewLink;
if (!url) {
throw new Error("Google Api has not returned valid response");
}
return url;
}
// Makes excel file the same way as export to excel works.
async function prepareFile(doc: ActiveDoc, req: Request) {
const data = await makeXLSX(doc, req);
const name = (optStringParam(req.query.title) || doc.docName);
return { name, data };
}