gristlabs_grist-core/app/server/lib/GoogleImport.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

96 lines
3.1 KiB
TypeScript

import {drive} from '@googleapis/drive';
import {Readable} from 'form-data';
import {GaxiosError, GaxiosPromise} from 'gaxios';
import {FetchError, Response as FetchResponse, Headers} from 'node-fetch';
import {getGoogleAuth} from "app/server/lib/GoogleAuth";
import contentDisposition from 'content-disposition';
const
SPREADSHEETS_MIMETYPE = 'application/vnd.google-apps.spreadsheet',
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
export async function downloadFromGDrive(url: string, code?: string) {
const fileId = fileIdFromUrl(url);
const key = process.env.GOOGLE_API_KEY;
if (!key) {
throw new Error("Can't download file from Google Drive. Api key is not configured");
}
if (!fileId) {
throw new Error(`Can't download from ${url}. Url is not valid`);
}
const googleDrive = await initDriveApi(code);
const fileRes = await googleDrive.files.get({
key,
fileId
});
if (fileRes.data.mimeType === SPREADSHEETS_MIMETYPE) {
let filename = fileRes.data.name;
if (filename && !filename.includes(".")) {
filename = `${filename}.xlsx`;
}
return await asFetchResponse(googleDrive.files.export(
{key, fileId, alt: 'media', mimeType: XLSX_MIMETYPE},
{responseType: 'stream'}
), filename);
} else {
return await asFetchResponse(googleDrive.files.get(
{key, fileId, alt: 'media'},
{responseType: 'stream'}
), fileRes.data.name);
}
}
async function initDriveApi(code?: string) {
if (code) {
// Create drive with access token.
const auth = getGoogleAuth();
const token = await auth.getToken(code);
if (token.tokens) {
auth.setCredentials(token.tokens);
}
return drive({version: 'v3', auth: code ? auth : undefined});
}
// Create drive for public access.
return drive({version: 'v3'});
}
async function asFetchResponse(req: GaxiosPromise<Readable>, filename?: string | null) {
try {
const res = await req;
const headers = new Headers(res.headers);
if (filename) {
headers.set("content-disposition", contentDisposition(filename));
}
return new FetchResponse(res.data, {
headers,
status: res.status,
statusText: res.statusText
});
} catch (err) {
const error: GaxiosError<Readable> = err;
if (!error.response) {
// Fetch throws exception on network error.
// https://github.com/node-fetch/node-fetch/blob/master/docs/ERROR-HANDLING.md
throw new FetchError(error.message, "system", error.code || "unknown");
} else {
// Fetch returns failure response on http error
const resInit = error.response ? {
status: error.response.status,
headers: new Headers(error.response.headers),
statusText: error.response.statusText
} : undefined;
return new FetchResponse(error.response.data, resInit);
}
}
}
export function isDriveUrl(url: string) {
return !!fileIdFromUrl(url);
}
function fileIdFromUrl(url: string) {
if (!url) { return null; }
const match = /^https:\/\/(docs|drive).google.com\/(spreadsheets|file)\/d\/([^/?]*)/i.exec(url);
return match ? match[3] : null;
}