aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-26 18:11:10 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-26 18:11:10 -0400
commite95387732e1fbff49ec035c3bec4b03324d814c8 (patch)
treea3392130fb8d8270f6792facc9a8557e4cb8d044 /src
parent10451e596f0b2032b3186286e6176b2c0cea4e52 (diff)
beginning to read from and write to database
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts92
-rw-r--r--src/client/Network.ts25
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts2
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts3
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx4
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/new_fields/RichTextUtils.ts4
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts51
-rw-r--r--src/server/database.ts34
-rw-r--r--src/server/index.ts12
10 files changed, 126 insertions, 103 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 5f06b5cec..ae8371f15 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -3,22 +3,20 @@ import v5 = require("uuid/v5");
import { Socket } from 'socket.io';
import { Message } from './server/Message';
import { RouteStore } from './server/RouteStore';
-import requestPromise = require('request-promise');
-import { CurrentUserUtils } from './server/authentication/models/current_user_utils';
-export class Utils {
+export namespace Utils {
- public static DRAG_THRESHOLD = 4;
+ export const DRAG_THRESHOLD = 4;
- public static GenerateGuid(): string {
+ export function GenerateGuid(): string {
return v4();
}
- public static GenerateDeterministicGuid(seed: string): string {
+ export function GenerateDeterministicGuid(seed: string): string {
return v5(seed, v5.URL);
}
- public static GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
+ export function GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
if (!ele) {
return { scale: 1, translateX: 1, translateY: 1 };
}
@@ -35,23 +33,23 @@ export class Utils {
* requested extension
* @param extension the specified sub-path to append to the window origin
*/
- public static prepend(extension: string): string {
+ export function prepend(extension: string): string {
return window.location.origin + extension;
}
- public static fileUrl(filename: string): string {
- return this.prepend(`/files/${filename}`);
+ export function fileUrl(filename: string): string {
+ return prepend(`/files/${filename}`);
}
- public static shareUrl(documentId: string): string {
- return this.prepend(`/doc/${documentId}?sharing=true`);
+ export function shareUrl(documentId: string): string {
+ return prepend(`/doc/${documentId}?sharing=true`);
}
- public static CorsProxy(url: string): string {
- return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
+ export function CorsProxy(url: string): string {
+ return prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
}
- public static CopyText(text: string) {
+ export function CopyText(text: string) {
var textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
@@ -63,7 +61,7 @@ export class Utils {
document.body.removeChild(textArea);
}
- public static fromRGBAstr(rgba: string) {
+ export function fromRGBAstr(rgba: string) {
let rm = rgba.match(/rgb[a]?\(([0-9]+)/);
let r = rm ? Number(rm[1]) : 0;
let gm = rgba.match(/rgb[a]?\([0-9]+,([0-9]+)/);
@@ -74,11 +72,12 @@ export class Utils {
let a = am ? Number(am[1]) : 0;
return { r: r, g: g, b: b, a: a };
}
- public static toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
+
+ export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")";
}
- public static HSLtoRGB(h: number, s: number, l: number) {
+ export function HSLtoRGB(h: number, s: number, l: number) {
// Must be fractions of 1
// s /= 100;
// l /= 100;
@@ -108,7 +107,7 @@ export class Utils {
return { r: r, g: g, b: b };
}
- public static RGBToHSL(r: number, g: number, b: number) {
+ export function RGBToHSL(r: number, g: number, b: number) {
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
@@ -150,7 +149,7 @@ export class Utils {
}
- public static GetClipboardText(): string {
+ export function GetClipboardText(): string {
var textArea = document.createElement("textarea");
document.body.appendChild(textArea);
textArea.focus();
@@ -163,51 +162,53 @@ export class Utils {
return val;
}
- public static loggingEnabled: Boolean = false;
- public static logFilter: number | undefined = undefined;
- private static log(prefix: string, messageName: string, message: any, receiving: boolean) {
- if (!this.loggingEnabled) {
+ export const loggingEnabled: Boolean = false;
+ export const logFilter: number | undefined = undefined;
+
+ function log(prefix: string, messageName: string, message: any, receiving: boolean) {
+ if (!loggingEnabled) {
return;
}
message = message || {};
- if (this.logFilter !== undefined && this.logFilter !== message.type) {
+ if (logFilter !== undefined && logFilter !== message.type) {
return;
}
let idString = (message.id || "").padStart(36, ' ');
prefix = prefix.padEnd(16, ' ');
console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)}`);
}
- private static loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
+
+ function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
return (args: any) => {
- this.log(prefix, messageName, args, true);
+ log(prefix, messageName, args, true);
func(args);
};
}
- public static Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
- this.log("Emit", message.Name, args, false);
+ export function Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
+ log("Emit", message.Name, args, false);
socket.emit(message.Message, args);
}
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
- this.log("Emit", message.Name, args, false);
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
+ log("Emit", message.Name, args, false);
if (fn) {
- socket.emit(message.Message, args, this.loggingCallback('Receiving', fn, message.Name));
+ socket.emit(message.Message, args, loggingCallback('Receiving', fn, message.Name));
} else {
- return new Promise<any>(res => socket.emit(message.Message, args, this.loggingCallback('Receiving', res, message.Name)));
+ return new Promise<any>(res => socket.emit(message.Message, args, loggingCallback('Receiving', res, message.Name)));
}
}
- public static AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
- socket.on(message.Message, this.loggingCallback('Incoming', handler, message.Name));
+ export function AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
+ socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
}
- public static AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
+ export function AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
- this.log('S receiving', message.Name, arg, true);
- handler([arg, this.loggingCallback('S sending', fn, message.Name)]);
+ log('S receiving', message.Name, arg, true);
+ handler([arg, loggingCallback('S sending', fn, message.Name)]);
});
}
}
@@ -291,15 +292,4 @@ export namespace JSONUtils {
return results;
}
-}
-
-export function PostToServer(relativeRoute: string, body?: any) {
- body = { userId: CurrentUserUtils.id, ...body };
- let options = {
- method: "POST",
- uri: Utils.prepend(relativeRoute),
- json: true,
- body
- };
- return requestPromise.post(options);
} \ No newline at end of file
diff --git a/src/client/Network.ts b/src/client/Network.ts
new file mode 100644
index 000000000..cb46105f8
--- /dev/null
+++ b/src/client/Network.ts
@@ -0,0 +1,25 @@
+import { Utils } from "../Utils";
+import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
+import requestPromise = require('request-promise');
+
+export async function PostToServer(relativeRoute: string, body?: any) {
+ let options = {
+ uri: Utils.prepend(relativeRoute),
+ method: "POST",
+ headers: { userId: CurrentUserUtils.id },
+ body,
+ json: true
+ };
+ return requestPromise.post(options);
+}
+
+export async function PostFormDataToServer(relativeRoute: string, formData: FormData) {
+ const parameters = {
+ method: 'POST',
+ headers: { userId: CurrentUserUtils.id },
+ body: formData,
+ };
+ const response = await fetch(relativeRoute, parameters);
+ const text = await response.json();
+ return text;
+} \ No newline at end of file
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 2c84741db..0f0f81891 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,9 +1,9 @@
import { docs_v1, slides_v1 } from "googleapis";
-import { PostToServer } from "../../../Utils";
import { RouteStore } from "../../../server/RouteStore";
import { Opt } from "../../../new_fields/Doc";
import { isArray } from "util";
import { EditorState } from "prosemirror-state";
+import { PostToServer } from "../../Network";
export const Pulls = "googleDocsPullCount";
export const Pushes = "googleDocsPushCount";
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index c45a49f1a..b1b29210a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,4 +1,4 @@
-import { PostToServer, Utils } from "../../../Utils";
+import { Utils } from "../../../Utils";
import { RouteStore } from "../../../server/RouteStore";
import { ImageField } from "../../../new_fields/URLField";
import { Cast, StrCast } from "../../../new_fields/Types";
@@ -14,6 +14,7 @@ import { NewMediaItemResult, MediaItem } from "../../../server/apis/google/Share
import { AssertionError } from "assert";
import { DocumentView } from "../../views/nodes/DocumentView";
import { DocumentManager } from "../../util/DocumentManager";
+import { PostToServer } from "../../Network";
export namespace GooglePhotos {
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 6670f685e..d0291aec4 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -21,6 +21,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import "./DirectoryImportBox.scss";
import BatchedArray from "array-batcher";
+import { PostFormDataToServer } from "../../Network";
const unsupported = ["text/html", "text/plain"];
interface FileResponse {
@@ -106,7 +107,6 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
const uploads = await BatchedArray.from(validated, { batchSize: 15 }).batchedMapAsync(async batch => {
const formData = new FormData();
- const parameters = { method: 'POST', body: formData };
batch.forEach(file => {
sizes.push(file.size);
@@ -114,7 +114,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
formData.append(Utils.GenerateGuid(), file);
});
- const responses = await (await fetch(RouteStore.upload, parameters)).json();
+ const responses = await PostFormDataToServer(RouteStore.upload, formData);
runInAction(() => this.completed += batch.length);
return responses as FileResponse[];
});
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b1f753635..296574a04 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -15,7 +15,7 @@ import { listSpec } from '../../new_fields/Schema';
import { BoolCast, Cast, FieldValue, StrCast, NumCast } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnOne, returnTrue, Utils, returnEmptyString, PostToServer } from '../../Utils';
+import { emptyFunction, returnOne, returnTrue, Utils, returnEmptyString } from '../../Utils';
import { DocServer } from '../DocServer';
import { ClientUtils } from '../util/ClientUtils';
import { DictationManager } from '../util/DictationManager';
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index 02079e92c..f3208ce41 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -7,17 +7,17 @@ import { FormattedTextBox } from "../client/views/nodes/FormattedTextBox";
import { Opt, Doc } from "./Doc";
import Color = require('color');
import { sinkListItem } from "prosemirror-schema-list";
-import { Utils, PostToServer } from "../Utils";
+import { Utils } from "../Utils";
import { RouteStore } from "../server/RouteStore";
import { Docs } from "../client/documents/Documents";
import { schema } from "../client/util/RichTextSchema";
import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
-import { SchemaHeaderField } from "./SchemaHeaderField";
import { DocServer } from "../client/DocServer";
import { Cast, StrCast } from "./Types";
import { Id } from "./FieldSymbols";
import { DocumentView } from "../client/views/nodes/DocumentView";
import { AssertionError } from "assert";
+import { PostToServer } from "../client/Network";
export namespace RichTextUtils {
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 684a8081b..665dcf862 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -66,6 +66,15 @@ export namespace GoogleApiServerUtils {
});
};
+ export const RetrieveAccessToken = (information: CredentialInformation) => {
+ return new Promise<string>((resolve, reject) => {
+ RetrieveCredentials(information).then(
+ credentials => resolve(credentials.token.access_token!),
+ error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
+ );
+ });
+ };
+
export const RetrieveCredentials = (information: CredentialInformation) => {
return new Promise<TokenResult>((resolve, reject) => {
readFile(information.credentialsPath, async (err, credentials) => {
@@ -78,15 +87,6 @@ export namespace GoogleApiServerUtils {
});
};
- export const RetrieveAccessToken = (information: CredentialInformation) => {
- return new Promise<string>((resolve, reject) => {
- RetrieveCredentials(information).then(
- credentials => resolve(credentials.token.access_token!),
- error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
- );
- });
- };
-
export const RetrievePhotosEndpoint = (paths: CredentialInformation) => {
return new Promise<any>((resolve, reject) => {
RetrieveAccessToken(paths).then(
@@ -107,7 +107,7 @@ export namespace GoogleApiServerUtils {
client_id, client_secret, redirect_uris[0]);
return new Promise<TokenResult>((resolve, reject) => {
- Database.Auxiliary.FetchGoogleAuthenticationToken(userId).then(token => {
+ Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(token => {
// Check if we have previously stored a token for this userId.
if (!token) {
return getNewToken(oAuth2Client, userId).then(resolve, reject);
@@ -123,8 +123,8 @@ export namespace GoogleApiServerUtils {
}
const refreshEndpoint = "https://oauth2.googleapis.com/token";
- const refreshToken = (credentials: Credentials, client_id: string, client_secret: string, oAuth2Client: OAuth2Client, token_path: string) => {
- return new Promise<TokenResult>((resolve, reject) => {
+ const refreshToken = (credentials: Credentials, client_id: string, client_secret: string, oAuth2Client: OAuth2Client, userId: string) => {
+ return new Promise<TokenResult>(resolve => {
let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
let queryParameters = {
refreshToken: credentials.refresh_token,
@@ -133,19 +133,13 @@ export namespace GoogleApiServerUtils {
grant_type: "refresh_token"
};
let url = `${refreshEndpoint}?${qs.stringify(queryParameters)}`;
- request.post(url, headerParameters).then(response => {
+ request.post(url, headerParameters).then(async response => {
let parsed = JSON.parse(response);
credentials.access_token = parsed.access_token;
credentials.expiry_date = new Date().getTime() + (parsed.expires_in * 1000);
- writeFile(token_path, JSON.stringify(credentials), (err) => {
- if (err) {
- console.error(err);
- reject(err);
- }
- console.log('Refreshed token stored to', token_path);
- oAuth2Client.setCredentials(credentials);
- resolve({ token: credentials, client: oAuth2Client });
- });
+ oAuth2Client.setCredentials(credentials);
+ await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, credentials);
+ resolve({ token: credentials, client: oAuth2Client });
});
});
};
@@ -156,7 +150,7 @@ export namespace GoogleApiServerUtils {
* @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) {
+ function getNewToken(oAuth2Client: OAuth2Client, userId: string) {
return new Promise<TokenResult>((resolve, reject) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
@@ -169,20 +163,13 @@ export namespace GoogleApiServerUtils {
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
- oAuth2Client.getToken(code, (err, token) => {
+ oAuth2Client.getToken(code, async (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);
- });
+ await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, token);
resolve({ token, client: oAuth2Client });
});
});
diff --git a/src/server/database.ts b/src/server/database.ts
index ce29478ad..890ac6b32 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -231,19 +231,37 @@ export namespace Database {
const GoogleAuthentication = "GoogleAuthentication";
- const SanitizedSingletonQuery = async (query: { [key: string]: any }, collection: string) => {
+ const SanitizedCappedQuery = async (query: { [key: string]: any }, collection: string, cap: number) => {
const cursor = await Instance.query(query, undefined, collection);
- const existing = (await cursor.toArray())[0];
- if (existing) {
- delete existing._id;
- }
- return existing;
+ const results = await cursor.toArray();
+ const slice = results.slice(0, Math.min(cap, results.length));
+ return slice.map(result => {
+ delete result._id;
+ return result;
+ });
+ };
+
+ const SanitizedSingletonQuery = async (query: { [key: string]: any }, collection: string) => {
+ const results = await SanitizedCappedQuery(query, collection, 1);
+ return results.length ? results[0] : undefined;
};
export const QueryUploadHistory = async (contentSize: number): Promise<Opt<DashUploadUtils.UploadInformation>> => {
return SanitizedSingletonQuery({ contentSize }, AuxiliaryCollections.GooglePhotosUploadHistory);
};
+ export namespace GoogleAuthenticationToken {
+
+ export const Fetch = async (userId: string) => {
+ return SanitizedSingletonQuery({ userId }, GoogleAuthentication);
+ };
+
+ export const Write = async (userId: string, token: any) => {
+ return Instance.insert({ userId, ...token }, GoogleAuthentication);
+ };
+
+ }
+
export const LogUpload = async (information: DashUploadUtils.UploadInformation) => {
const bundle = {
_id: Utils.GenerateDeterministicGuid(String(information.contentSize!)),
@@ -258,10 +276,6 @@ export namespace Database {
return Promise.all(pendingDeletions);
};
- export const FetchGoogleAuthenticationToken = async (userId: string) => {
- return SanitizedSingletonQuery({ userId }, GoogleAuthentication);
- };
-
}
} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 386ecce4d..2ff63ab78 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -810,7 +810,7 @@ const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerU
app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
let sector: GoogleApiServerUtils.Service = req.params.sector as GoogleApiServerUtils.Service;
let action: GoogleApiServerUtils.Action = req.params.action as GoogleApiServerUtils.Action;
- GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentialsPath, userId: req.body.userId }).then(endpoint => {
+ GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentialsPath, userId: req.headers.userId as string }).then(endpoint => {
let handler = EndpointHandlerMap.get(action);
if (endpoint && handler) {
let execute = handler(endpoint, req.body).then(
@@ -824,10 +824,11 @@ app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
});
});
-app.get(RouteStore.googlePhotosAccessToken, (req, res) => GoogleApiServerUtils.RetrieveAccessToken({ credentialsPath, userId: req.body.userId }).then(token => res.send(token)));
+app.get(RouteStore.googlePhotosAccessToken, (req, res) => GoogleApiServerUtils.RetrieveAccessToken({ credentialsPath, userId: req.headers.userId as string }).then(token => res.send(token)));
const tokenError = "Unable to successfully upload bytes for all images!";
const mediaError = "Unable to convert all uploaded bytes to media items!";
+const userIdError = "Unable to parse the identification of the user!";
export interface NewMediaItem {
description: string;
@@ -837,7 +838,12 @@ export interface NewMediaItem {
}
app.post(RouteStore.googlePhotosMediaUpload, async (req, res) => {
- const { userId, media } = req.body;
+ const { media } = req.body;
+ const { userId } = req.headers;
+
+ if (!userId || Array.isArray(userId)) {
+ return _error(res, userIdError);
+ }
await GooglePhotosUploadUtils.initialize({ credentialsPath, userId });