aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json51
-rw-r--r--package.json1
-rw-r--r--src/client/util/History.ts23
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts93
4 files changed, 56 insertions, 112 deletions
diff --git a/package-lock.json b/package-lock.json
index 68611f50a..79e81128b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -201,7 +201,6 @@
"prosemirror-view": "^1.32.5",
"pug": "^3.0.2",
"puppeteer": "^24.1.1",
- "query-string": "^9.1.1",
"querystring-es3": "^0.2.1",
"raw-loader": "^4.0.2",
"rc-switch": "^4.1.0",
@@ -19674,15 +19673,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/decode-uri-component": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
- "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- }
- },
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -21777,18 +21767,6 @@
"node": ">=8"
}
},
- "node_modules/filter-obj": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
- "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
@@ -32945,23 +32923,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/query-string": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.1.tgz",
- "integrity": "sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==",
- "license": "MIT",
- "dependencies": {
- "decode-uri-component": "^0.4.1",
- "filter-obj": "^5.1.0",
- "split-on-first": "^3.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/querystring-es3": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
@@ -36166,18 +36127,6 @@
"integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==",
"license": "BDS-3-Clause"
},
- "node_modules/split-on-first": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
- "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/split-skip": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
diff --git a/package.json b/package.json
index a090ab9b2..5d036156f 100644
--- a/package.json
+++ b/package.json
@@ -285,7 +285,6 @@
"prosemirror-view": "^1.32.5",
"pug": "^3.0.2",
"puppeteer": "^24.1.1",
- "query-string": "^9.1.1",
"querystring-es3": "^0.2.1",
"raw-loader": "^4.0.2",
"rc-switch": "^4.1.0",
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cf156014d..9728e3177 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,7 +1,6 @@
/* eslint-disable no-use-before-define */
/* eslint-disable no-empty */
/* eslint-disable no-param-reassign */
-import queryString from 'query-string';
import { Doc } from '../../fields/Doc';
import { OmitKeys, ClientUtils } from '../../ClientUtils';
import { DocServer } from '../DocServer';
@@ -79,7 +78,7 @@ export namespace HistoryUtil {
// }
// }
- const parsers: { [type: string]: (pathname: string[], opts: queryString.ParsedQuery) => ParsedUrl | undefined } = {};
+ const parsers: { [type: string]: (pathname: string[], opts: URLSearchParams) => ParsedUrl | undefined } = {};
const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
type ParserValue = true | 'none' | 'json' | ((value: string) => string | null | (string | null)[]);
@@ -88,7 +87,7 @@ export namespace HistoryUtil {
[key: string]: ParserValue;
};
- function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: queryString.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
+ function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: URLSearchParams, current: ParsedUrl) => ParsedUrl | null | undefined) {
function parseValue(parser: ParserValue, value: string | (string | null)[] | null | undefined) {
if (value === undefined || value === null) {
return value;
@@ -105,21 +104,21 @@ export namespace HistoryUtil {
parsers[type] = (pathname, opts) => {
const current: DocUrl & { [key: string]: null | (string | null)[] | string } = { type: 'doc', docId: '' };
for (const required in requiredFields) {
- if (!(required in opts)) {
+ if (!opts.has(required)) {
return undefined;
}
const parser = requiredFields[required];
- const value = parseValue(parser, opts[required]);
+ const value = parseValue(parser, opts.get(required));
if (value !== null && value !== undefined) {
current[required] = value;
}
}
for (const opt in optionalFields) {
- if (!(opt in opts)) {
+ if (!opts.has(opt)) {
continue;
}
const parser = optionalFields[opt];
- const value = parseValue(parser, opts[opt]);
+ const value = parseValue(parser, opts.get(opt));
if (value !== undefined) {
current[opt] = value;
}
@@ -145,12 +144,12 @@ export namespace HistoryUtil {
path = customStringifier(state, path);
}
const queryObj = OmitKeys(state, keys).extract;
- const query: { [key: string]: string | null } = {};
+ const query = new URLSearchParams();
Object.keys(queryObj).forEach(key => {
- query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]);
+ query.set(key, queryObj[key] === null ? '' : JSON.stringify(queryObj[key]));
});
- const qstr = queryString.stringify(query);
- return path + (queryString ? `?${qstr}` : '');
+ const qstr = query.toString();
+ return path + (qstr ? `?${qstr}` : '');
};
}
@@ -167,7 +166,7 @@ export namespace HistoryUtil {
export function parseUrl(location: Location | URL): ParsedUrl | undefined {
const pathname = location.pathname.substring(1);
const { search } = location;
- const opts = search.length ? queryString.parse(search, { sort: false }) : {};
+ const opts = new URLSearchParams(search);
const pathnameSplit = pathname.split('/');
const type = pathnameSplit[0];
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 21c405bee..7373df473 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,7 +1,7 @@
+/* eslint-disable no-use-before-define */
import { GaxiosResponse } from 'gaxios';
import { Credentials, OAuth2Client, OAuth2ClientOptions } from 'google-auth-library';
import { google } from 'googleapis';
-import * as qs from 'query-string';
import * as request from 'request-promise';
import { Opt } from '../../../fields/Doc';
import { Database } from '../../database';
@@ -21,7 +21,6 @@ const scope = ['documents.readonly', 'documents', 'presentations', 'presentation
* This namespace manages server side authentication for Google API queries, either
* from the standard v1 APIs or the Google Photos REST API.
*/
-
export namespace GoogleApiServerUtils {
/**
* As we expand out to more Google APIs that are accessible from
@@ -71,29 +70,29 @@ export namespace GoogleApiServerUtils {
/**
* A briefer format for the response from a 'googleapis' API request
*/
- export type ApiResponse = Promise<GaxiosResponse>;
+ export type ApiResponse = Promise<GaxiosResponse<unknown>>;
/**
* A generic form for a handler that executes some request on the endpoint
*/
- export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse;
+ export type ApiRouter = (endpoint: Endpoint, parameters: Record<string, unknown>) => ApiResponse;
/**
* A generic form for the asynchronous function that actually submits the
- * request to the API and returns the corresporing response. Helpful when
+ * request to the API and returns the corresponding response. Helpful when
* making an extensible endpoint definition.
*/
- export type ApiHandler = (parameters: any, methodOptions?: any) => ApiResponse;
+ export type ApiHandler = (parameters: Record<string, unknown>, methodOptions?: Record<string, unknown>) => ApiResponse;
/**
* A literal union type indicating the valid actions for these 'googleapis'
- * requestions
+ * requests
*/
export type Action = 'create' | 'retrieve' | 'update';
/**
* An interface defining any entity on which one can invoke
- * anuy of the following handlers. All 'googleapis' wrappers
+ * any of the following handlers. All 'googleapis' wrappers
* such as google.docs().documents and google.slides().presentations
* satisfy this interface.
*/
@@ -109,7 +108,7 @@ export namespace GoogleApiServerUtils {
* of needless duplicate clients that would arise from
* making one new client instance per request.
*/
- const authenticationClients = new Map<String, OAuth2Client>();
+ const authenticationClients = new Map<string, OAuth2Client>();
/**
* This function receives the target sector ("which G-Suite app's API am I interested in?")
@@ -120,23 +119,21 @@ export namespace GoogleApiServerUtils {
* @returns the relevant 'googleapis' wrapper, if any
*/
export async function GetEndpoint(sector: string, userId: string): Promise<Endpoint | void> {
- return new Promise(async resolve => {
- const auth = await retrieveOAuthClient(userId);
- if (!auth) {
- return resolve();
- }
- let routed: Opt<Endpoint>;
- const parameters: any = { auth, version: 'v1' };
- switch (sector) {
- case Service.Documents:
- routed = google.docs(parameters).documents;
- break;
- case Service.Slides:
- routed = google.slides(parameters).presentations;
- break;
- }
- resolve(routed);
- });
+ const auth = await retrieveOAuthClient(userId);
+ if (!auth) {
+ return;
+ }
+ let routed: Opt<Endpoint>;
+ const parameters: { version: 'v1' } = { /* auth, */ version: 'v1' }; ///* auth: OAuth2Client;*/
+ switch (sector) {
+ case Service.Documents:
+ routed = google.docs(parameters).documents;
+ break;
+ case Service.Slides:
+ routed = google.slides(parameters).presentations;
+ break;
+ }
+ return routed;
}
/**
@@ -149,19 +146,17 @@ export namespace GoogleApiServerUtils {
* security.
*/
export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client | void> {
- return new Promise(async resolve => {
- const { credentials, refreshed } = await retrieveCredentials(userId);
- if (!credentials) {
- return resolve();
- }
- let client = authenticationClients.get(userId);
- if (!client) {
- authenticationClients.set(userId, (client = generateClient(credentials)));
- } else if (refreshed) {
- client.setCredentials(credentials);
- }
- resolve(client);
- });
+ const { credentials, refreshed } = await retrieveCredentials(userId);
+ if (!credentials) {
+ return;
+ }
+ let client = authenticationClients.get(userId);
+ if (!client) {
+ authenticationClients.set(userId, (client = generateClient(credentials)));
+ } else if (refreshed) {
+ client.setCredentials(credentials);
+ }
+ return client;
}
/**
@@ -173,7 +168,9 @@ export namespace GoogleApiServerUtils {
*/
function generateClient(credentials?: Credentials): OAuth2Client {
const client = new google.auth.OAuth2(oAuthOptions);
- credentials && client.setCredentials(credentials);
+ if (credentials) {
+ client.setCredentials(credentials);
+ }
return client;
}
@@ -206,7 +203,7 @@ export namespace GoogleApiServerUtils {
*/
export async function processNewUser(userId: string, authenticationCode: string): Promise<EnrichedCredentials> {
const credentials = await new Promise<Credentials>((resolve, reject) => {
- worker.getToken(authenticationCode, async (err, credentials) => {
+ worker.getToken(authenticationCode, (err, credentials) => {
if (err || !credentials) {
reject(err);
return;
@@ -221,7 +218,7 @@ export namespace GoogleApiServerUtils {
/**
* This type represents the union of the full set of OAuth2 credentials
- * and all of a Google user's publically available information. This is the strucure
+ * and all of a Google user's publicly available information. This is the structure
* of the JSON object we ultimately store in the googleAuthentication table of the database.
*/
export type EnrichedCredentials = Credentials & { userInfo: UserInfo };
@@ -297,15 +294,15 @@ export namespace GoogleApiServerUtils {
async function refreshAccessToken(credentials: Credentials, userId: string): Promise<Credentials> {
const headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
const { client_id, client_secret } = GoogleCredentialsLoader.ProjectCredentials;
- const url = `https://oauth2.googleapis.com/token?${qs.stringify({
- refreshToken: credentials.refresh_token,
+ const params = new URLSearchParams({
+ refresh_token: credentials.refresh_token!,
client_id,
client_secret,
grant_type: 'refresh_token',
- })}`;
- const { access_token, expires_in } = await new Promise<any>(async resolve => {
- const response = await request.post(url, headerParameters);
- resolve(JSON.parse(response));
+ });
+ const url = `https://oauth2.googleapis.com/token?${params.toString()}`;
+ const { access_token, expires_in } = await new Promise<{ access_token: string; expires_in: number }>(resolve => {
+ request.post(url, headerParameters).then(response => resolve(JSON.parse(response)));
});
// expires_in is in seconds, but we're building the new expiry date in milliseconds
const expiry_date = new Date().getTime() + expires_in * 1000;