@mcpher/gas-fakes 1.2.32 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gcloudignore +24 -0
- package/README.md +5 -5
- package/appsscript.json +101 -0
- package/exgcp.sh +4 -4
- package/package.json +5 -6
- package/src/cli/app.js +9 -1
- package/src/cli/lib-manager.js +0 -1
- package/src/cli/setup.js +330 -228
- package/src/services/advcalendar/clapis.js +4 -2
- package/src/services/advdocs/docapis.js +4 -3
- package/src/services/advdrive/drapis.js +8 -5
- package/src/services/advforms/formsapis.js +4 -3
- package/src/services/advgmail/gmailapis.js +4 -3
- package/src/services/advsheets/shapis.js +5 -10
- package/src/services/advslides/slapis.js +4 -3
- package/src/services/driveapp/fakedriveapp.js +10 -2
- package/src/services/logger/fakelogger.js +6 -3
- package/src/services/scriptapp/app.js +16 -11
- package/src/services/scriptapp/behavior.js +132 -107
- package/src/services/session/fakesession.js +24 -9
- package/src/services/slidesapp/fakepresentation.js +19 -8
- package/src/services/slidesapp/fakeslide.js +45 -20
- package/src/services/spreadsheetapp/fakesheet.js +9 -7
- package/src/services/stores/fakestores.js +20 -18
- package/src/services/stores/gasflex.js +13 -14
- package/src/services/urlfetchapp/app.js +3 -3
- package/src/support/auth.js +227 -55
- package/src/support/slogger.js +42 -0
- package/src/support/sxauth.js +42 -39
- package/src/support/sxcalendar.js +9 -43
- package/src/support/sxdocs.js +6 -42
- package/src/support/sxdrive.js +19 -76
- package/src/support/sxfetch.js +36 -30
- package/src/support/sxforms.js +9 -40
- package/src/support/sxgmail.js +9 -37
- package/src/support/sxretry.js +79 -0
- package/src/support/sxsheets.js +6 -37
- package/src/support/sxslides.js +5 -36
- package/src/support/sxtoken.js +15 -0
- package/src/support/syncit.js +27 -10
- package/src/support/workersync/sxfunctions.js +2 -0
- package/src/support/workersync/synclogger.js +22 -5
- package/src/support/workersync/worker.js +8 -11
- package/README.RU.md +0 -373
- package/env.setup.template +0 -16
- package/run.js +0 -35
- package/setup.js +0 -689
- package/src/Code.js +0 -3
- package/src/appsscript.json +0 -5
package/setup.js
DELETED
|
@@ -1,689 +0,0 @@
|
|
|
1
|
-
// setup.js: Setup for gas-fakes.
|
|
2
|
-
|
|
3
|
-
import prompts from "prompts";
|
|
4
|
-
import dotenv from "dotenv";
|
|
5
|
-
import fs from "fs/promises";
|
|
6
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import os from "os";
|
|
9
|
-
import { execSync } from "child_process";
|
|
10
|
-
|
|
11
|
-
// --- Utility Functions ---
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Search .env file
|
|
15
|
-
* @param {string} dir - Start directory
|
|
16
|
-
* @returns {Promise<string[]>}
|
|
17
|
-
*/
|
|
18
|
-
async function findEnvFiles(dir) {
|
|
19
|
-
try {
|
|
20
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
21
|
-
const promises = entries.map((entry) => {
|
|
22
|
-
const fullPath = path.join(dir, entry.name);
|
|
23
|
-
if (entry.isDirectory()) {
|
|
24
|
-
if (entry.name === "node_modules") {
|
|
25
|
-
return Promise.resolve([]);
|
|
26
|
-
}
|
|
27
|
-
return findEnvFiles(fullPath);
|
|
28
|
-
} else if (entry.isFile() && entry.name === ".env") {
|
|
29
|
-
return Promise.resolve(fullPath);
|
|
30
|
-
}
|
|
31
|
-
return Promise.resolve([]);
|
|
32
|
-
});
|
|
33
|
-
const results = await Promise.all(promises);
|
|
34
|
-
return results.flat();
|
|
35
|
-
} catch (err) {
|
|
36
|
-
console.error(`No directory: ${dir}`);
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Checks if the gcloud CLI is installed and available in the system's PATH.
|
|
43
|
-
* If not, it prints an informative message and exits the script.
|
|
44
|
-
*/
|
|
45
|
-
function checkForGcloudCli() {
|
|
46
|
-
try {
|
|
47
|
-
// Execute a simple, non-destructive command to check if gcloud exists.
|
|
48
|
-
// The output is ignored to keep the console clean on success.
|
|
49
|
-
execSync("gcloud --version", { stdio: "ignore" });
|
|
50
|
-
} catch (error) {
|
|
51
|
-
// The command failed, likely because gcloud is not installed or not in the PATH.
|
|
52
|
-
console.error("\n[Error] Google Cloud SDK (gcloud CLI) not found.");
|
|
53
|
-
console.error(
|
|
54
|
-
"This script requires the gcloud CLI to manage authentication and Google Cloud services."
|
|
55
|
-
);
|
|
56
|
-
console.error("Please install it by following the official instructions:");
|
|
57
|
-
console.error("https://cloud.google.com/sdk/gcloud");
|
|
58
|
-
process.exit(1); // Exit the script with an error code.
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Helper function to run a shell command and print its output.
|
|
64
|
-
* @param {string} command The command to execute.
|
|
65
|
-
*/
|
|
66
|
-
function runCommand(command) {
|
|
67
|
-
try {
|
|
68
|
-
// Execute the command, inheriting stdio to show output/errors in real-time.
|
|
69
|
-
execSync(command, { stdio: "inherit" });
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error(`\nError executing command: ${command}`);
|
|
72
|
-
// The error message from the command is already shown due to 'inherit' stdio.
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// --- Exported Command Implementations ---
|
|
78
|
-
export async function initializeConfiguration(options = {}) {
|
|
79
|
-
let envPath;
|
|
80
|
-
|
|
81
|
-
if (options.env) {
|
|
82
|
-
envPath = path.resolve(process.cwd(), options.env);
|
|
83
|
-
console.log(`-> Using specified .env path: ${envPath}`);
|
|
84
|
-
} else {
|
|
85
|
-
const foundFiles = await findEnvFiles(process.cwd());
|
|
86
|
-
if (foundFiles.length > 0) {
|
|
87
|
-
const choices = foundFiles.map((file) => ({
|
|
88
|
-
title: file,
|
|
89
|
-
value: file,
|
|
90
|
-
}));
|
|
91
|
-
choices.push({
|
|
92
|
-
title: "Create a new .env file in the current directory",
|
|
93
|
-
value: "new",
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const response = await prompts({
|
|
97
|
-
type: "select",
|
|
98
|
-
name: "envPathSelection",
|
|
99
|
-
message: "Found .env file(s). Which one would you like to use?",
|
|
100
|
-
choices: choices,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
if (typeof response.envPathSelection === "undefined") {
|
|
104
|
-
console.log("Initialization cancelled.");
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (response.envPathSelection === "new") {
|
|
109
|
-
envPath = path.join(process.cwd(), ".env");
|
|
110
|
-
} else {
|
|
111
|
-
envPath = response.envPathSelection;
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
console.log(
|
|
115
|
-
"No .env file found. A new one will be created in the current directory."
|
|
116
|
-
);
|
|
117
|
-
envPath = path.join(process.cwd(), ".env");
|
|
118
|
-
}
|
|
119
|
-
console.log(`-> Using .env file at: ${envPath}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let existingConfig = {};
|
|
123
|
-
if (existsSync(envPath)) {
|
|
124
|
-
console.log(
|
|
125
|
-
"Found existing .env file. Loading current values as defaults."
|
|
126
|
-
);
|
|
127
|
-
existingConfig = dotenv.parse(readFileSync(envPath));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
console.log("--------------------------------------------------");
|
|
131
|
-
console.log("Configuring .env for gas-fakes");
|
|
132
|
-
console.log("Press Enter to accept the default value in brackets.");
|
|
133
|
-
console.log("Use Space to select/deselect scopes.");
|
|
134
|
-
console.log("--------------------------------------------------");
|
|
135
|
-
|
|
136
|
-
const existingExtraScopes = existingConfig.EXTRA_SCOPES
|
|
137
|
-
? existingConfig.EXTRA_SCOPES.split(",").filter((s) => s)
|
|
138
|
-
: [];
|
|
139
|
-
|
|
140
|
-
const responses = {};
|
|
141
|
-
|
|
142
|
-
// --- Stage 1: Basic Info ---
|
|
143
|
-
const basicInfoQuestions = [
|
|
144
|
-
{
|
|
145
|
-
type: "text",
|
|
146
|
-
name: "GCP_PROJECT_ID",
|
|
147
|
-
message: "Enter your GCP Project ID",
|
|
148
|
-
initial:
|
|
149
|
-
existingConfig.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
type: "text",
|
|
153
|
-
name: "DRIVE_TEST_FILE_ID",
|
|
154
|
-
message:
|
|
155
|
-
"Enter a test Drive file ID for authentication checks (optional)",
|
|
156
|
-
initial: existingConfig.DRIVE_TEST_FILE_ID || "",
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
|
|
160
|
-
const basicInfoResponses = await prompts(basicInfoQuestions);
|
|
161
|
-
if (typeof basicInfoResponses.GCP_PROJECT_ID === "undefined") {
|
|
162
|
-
console.log("Initialization cancelled.");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
Object.assign(responses, basicInfoResponses);
|
|
166
|
-
|
|
167
|
-
// --- Stage 2: Scopes ---
|
|
168
|
-
|
|
169
|
-
const DEFAULT_SCOPES_VALUES = [
|
|
170
|
-
"https://www.googleapis.com/auth/userinfo.email",
|
|
171
|
-
"openid",
|
|
172
|
-
"https://www.googleapis.com/auth/cloud-platform",
|
|
173
|
-
];
|
|
174
|
-
console.log(
|
|
175
|
-
"\nThe following default scopes are required for basic operations and will be enabled automatically:"
|
|
176
|
-
);
|
|
177
|
-
DEFAULT_SCOPES_VALUES.forEach((scope) => console.log(` - ${scope}`));
|
|
178
|
-
responses.DEFAULT_SCOPES = DEFAULT_SCOPES_VALUES;
|
|
179
|
-
|
|
180
|
-
const extraScopeQuestion = {
|
|
181
|
-
type: "multiselect",
|
|
182
|
-
name: "EXTRA_SCOPES",
|
|
183
|
-
message: "Select any extra scopes your script requires",
|
|
184
|
-
|
|
185
|
-
// i think we only need to have drive (which we must have for all the others anyway)
|
|
186
|
-
choices: [
|
|
187
|
-
{
|
|
188
|
-
title: "Workspace resources",
|
|
189
|
-
value: "https://www.googleapis.com/auth/drive",
|
|
190
|
-
},
|
|
191
|
-
/*
|
|
192
|
-
{
|
|
193
|
-
title: "Sheets (full access)",
|
|
194
|
-
value: "https://www.googleapis.com/auth/spreadsheets",
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
title: "Docs (full access)",
|
|
198
|
-
value: "https://www.googleapis.com/auth/documents",
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
title: "Forms (full access)",
|
|
202
|
-
value: "https://www.googleapis.com/auth/forms",
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
title: "Gmail (send mail)",
|
|
206
|
-
value: "https://www.googleapis.com/auth/gmail.send",
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
title: "Gmail (full access)",
|
|
210
|
-
value: "https://www.googleapis.com/auth/gmail.modify",
|
|
211
|
-
},
|
|
212
|
-
*/
|
|
213
|
-
{
|
|
214
|
-
// actually labels are not sensitive
|
|
215
|
-
title: "Gmail labels",
|
|
216
|
-
value: "https://www.googleapis.com/auth/gmail.labels",
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
sensitivity: "sensitive",
|
|
220
|
-
title: "Gmail compose",
|
|
221
|
-
value: "https://www.googleapis.com/auth/gmail.compose",
|
|
222
|
-
},
|
|
223
|
-
].map((scope) => ({
|
|
224
|
-
...scope,
|
|
225
|
-
title: scope.sensitivity
|
|
226
|
-
? `[${scope.sensitivity}] ${scope.title}`
|
|
227
|
-
: scope.title,
|
|
228
|
-
// because we always need drive for ant extra scopes
|
|
229
|
-
selected:
|
|
230
|
-
existingExtraScopes.length > 0
|
|
231
|
-
? existingExtraScopes.includes(scope.value)
|
|
232
|
-
: scope.value === "https://www.googleapis.com/auth/drive",
|
|
233
|
-
})),
|
|
234
|
-
hint: "- Use space to select/deselect. Press Enter to submit.",
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// to check for any kind of sensitivity
|
|
238
|
-
const sensitiveScopesList = extraScopeQuestion.choices.filter(
|
|
239
|
-
(scope) => scope.sensitivity
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
const extraScopeResponses = await prompts(extraScopeQuestion);
|
|
243
|
-
|
|
244
|
-
if (typeof extraScopeResponses.EXTRA_SCOPES === "undefined") {
|
|
245
|
-
console.log("Initialization cancelled.");
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
Object.assign(responses, extraScopeResponses);
|
|
249
|
-
|
|
250
|
-
const selectedExtraScopes = responses.EXTRA_SCOPES || [];
|
|
251
|
-
|
|
252
|
-
const usesSensitiveScopes = sensitiveScopesList.some((s) =>
|
|
253
|
-
selectedExtraScopes.includes(s.value)
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
if (usesSensitiveScopes) {
|
|
257
|
-
console.log("\n--------------------------------------------------");
|
|
258
|
-
console.log(
|
|
259
|
-
"You have selected sensitive or restricted scopes. Google requires an OAuth client credential file for these."
|
|
260
|
-
);
|
|
261
|
-
console.log(
|
|
262
|
-
"See the getting started guide https://github.com/brucemcpherson/gas-fakes/blob/main/GETTING_STARTED.md for how."
|
|
263
|
-
);
|
|
264
|
-
console.log("--------------------------------------------------");
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const clientCredentialQuestion = {
|
|
268
|
-
type: "text",
|
|
269
|
-
name: "CLIENT_CREDENTIAL_FILE",
|
|
270
|
-
message: usesSensitiveScopes
|
|
271
|
-
? "Enter the path and filename for your OAuth client credentials JSON"
|
|
272
|
-
: "Enter path to OAuth client credentials JSON (optional)",
|
|
273
|
-
initial: existingConfig.CLIENT_CREDENTIAL_FILE || "",
|
|
274
|
-
validate: (input) => {
|
|
275
|
-
const trimmedInput = input.trim();
|
|
276
|
-
|
|
277
|
-
if (usesSensitiveScopes) {
|
|
278
|
-
if (trimmedInput === "") {
|
|
279
|
-
return "This field is required for the selected sensitive scopes.";
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
if (trimmedInput === "") {
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const resolvedPath = path.resolve(process.cwd(), trimmedInput);
|
|
288
|
-
if (!existsSync(resolvedPath)) {
|
|
289
|
-
return `File not found at '${resolvedPath}'. Please check the path and try again.`;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return true;
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const clientCredentialResponse = await prompts(clientCredentialQuestion);
|
|
297
|
-
if (typeof clientCredentialResponse.CLIENT_CREDENTIAL_FILE === "undefined") {
|
|
298
|
-
console.log("Initialization cancelled.");
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
Object.assign(responses, clientCredentialResponse);
|
|
302
|
-
|
|
303
|
-
// --- Stage 3: Remaining Config ---
|
|
304
|
-
const defaultScopesDisplay = `\n - Default: [${responses.DEFAULT_SCOPES.join(
|
|
305
|
-
", "
|
|
306
|
-
)}]`;
|
|
307
|
-
const extraScopesDisplay =
|
|
308
|
-
responses.EXTRA_SCOPES && responses.EXTRA_SCOPES.length > 0
|
|
309
|
-
? `\n - Extra: [${responses.EXTRA_SCOPES.join(", ")}]`
|
|
310
|
-
: "\n - Extra: [None]";
|
|
311
|
-
|
|
312
|
-
const remainingQuestions = [
|
|
313
|
-
{
|
|
314
|
-
type: "toggle",
|
|
315
|
-
name: "QUIET",
|
|
316
|
-
message: "Run gas-fakes package in quiet mode",
|
|
317
|
-
initial: existingConfig.QUIET === "true" ? true : false,
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
type: "select",
|
|
321
|
-
name: "LOG_DESTINATION",
|
|
322
|
-
message: `Selected Scopes:${defaultScopesDisplay}${extraScopesDisplay}\n\nEnter logging destination`,
|
|
323
|
-
choices: [
|
|
324
|
-
{ title: "CONSOLE", value: "CONSOLE" },
|
|
325
|
-
{ title: "CLOUD", value: "CLOUD" },
|
|
326
|
-
{ title: "BOTH", value: "BOTH" },
|
|
327
|
-
{ title: "NONE", value: "NONE" },
|
|
328
|
-
],
|
|
329
|
-
initial:
|
|
330
|
-
["CONSOLE", "CLOUD", "BOTH", "NONE"].indexOf(
|
|
331
|
-
existingConfig.LOG_DESTINATION
|
|
332
|
-
) > -1
|
|
333
|
-
? ["CONSOLE", "CLOUD", "BOTH", "NONE"].indexOf(
|
|
334
|
-
existingConfig.LOG_DESTINATION
|
|
335
|
-
)
|
|
336
|
-
: 0,
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
type: "select",
|
|
340
|
-
name: "STORE_TYPE",
|
|
341
|
-
message: "Enter storage type",
|
|
342
|
-
choices: [
|
|
343
|
-
{ title: "FILE", value: "FILE" },
|
|
344
|
-
{ title: "UPSTASH", value: "UPSTASH" },
|
|
345
|
-
],
|
|
346
|
-
initial:
|
|
347
|
-
["FILE", "UPSTASH"].indexOf(existingConfig.STORE_TYPE?.toUpperCase()) >
|
|
348
|
-
-1
|
|
349
|
-
? ["FILE", "UPSTASH"].indexOf(existingConfig.STORE_TYPE.toUpperCase())
|
|
350
|
-
: 0,
|
|
351
|
-
},
|
|
352
|
-
];
|
|
353
|
-
|
|
354
|
-
const remainingResponses = await prompts(remainingQuestions);
|
|
355
|
-
if (typeof remainingResponses.LOG_DESTINATION === "undefined") {
|
|
356
|
-
console.log("Initialization cancelled.");
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
Object.assign(responses, remainingResponses);
|
|
360
|
-
|
|
361
|
-
// Convert scope arrays to comma-separated strings for saving
|
|
362
|
-
if (Array.isArray(responses.DEFAULT_SCOPES)) {
|
|
363
|
-
responses.DEFAULT_SCOPES = responses.DEFAULT_SCOPES.join(",");
|
|
364
|
-
}
|
|
365
|
-
if (Array.isArray(responses.EXTRA_SCOPES)) {
|
|
366
|
-
responses.EXTRA_SCOPES = responses.EXTRA_SCOPES.join(",");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (responses.STORE_TYPE === "UPSTASH") {
|
|
370
|
-
const upstashQuestions = [
|
|
371
|
-
{
|
|
372
|
-
type: "text",
|
|
373
|
-
name: "UPSTASH_REDIS_REST_URL",
|
|
374
|
-
message: "Enter your Upstash Redis REST URL",
|
|
375
|
-
initial: existingConfig.UPSTASH_REDIS_REST_URL || "",
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
type: "text",
|
|
379
|
-
name: "UPSTASH_REDIS_REST_TOKEN",
|
|
380
|
-
message: "Enter your Upstash Redis REST Token",
|
|
381
|
-
initial: existingConfig.UPSTASH_REDIS_REST_TOKEN || "",
|
|
382
|
-
},
|
|
383
|
-
];
|
|
384
|
-
const upstashResponses = await prompts(upstashQuestions);
|
|
385
|
-
|
|
386
|
-
if (typeof upstashResponses.UPSTASH_REDIS_REST_URL === "undefined") {
|
|
387
|
-
console.log("Initialization cancelled during Upstash configuration.");
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
Object.assign(responses, upstashResponses);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// --- Confirmation Step ---
|
|
394
|
-
console.log("\n------------------ Summary ------------------");
|
|
395
|
-
Object.entries(responses).forEach(([key, value]) => {
|
|
396
|
-
if (value !== undefined) console.log(`${key}: ${value}`);
|
|
397
|
-
});
|
|
398
|
-
console.log("-------------------------------------------");
|
|
399
|
-
|
|
400
|
-
const confirmSave = await prompts({
|
|
401
|
-
type: "confirm",
|
|
402
|
-
name: "save",
|
|
403
|
-
message: `Save this configuration to ${envPath}?`,
|
|
404
|
-
initial: true,
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
if (!confirmSave.save) {
|
|
408
|
-
console.log("Configuration discarded. No changes were made.");
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// --- File Writing Logic ---
|
|
413
|
-
console.log(`Writing configuration to "${envPath}"...`);
|
|
414
|
-
const inits =
|
|
415
|
-
responses.STORE_TYPE !== "UPSTASH"
|
|
416
|
-
? { UPSTASH_REDIS_REST_TOKEN: "", UPSTASH_REDIS_REST_URL: "" }
|
|
417
|
-
: {};
|
|
418
|
-
const finalConfig = { ...existingConfig, ...responses, ...inits };
|
|
419
|
-
|
|
420
|
-
console.log("\n------------------ Final output ------------------");
|
|
421
|
-
const envContent = Reflect.ownKeys(finalConfig)
|
|
422
|
-
.map((key) => {
|
|
423
|
-
const item = finalConfig[key];
|
|
424
|
-
const res = `${key}="${(item.toString() || "").trim()}"`;
|
|
425
|
-
console.log(res);
|
|
426
|
-
return res;
|
|
427
|
-
})
|
|
428
|
-
.join("\n");
|
|
429
|
-
/* replacing this to include existing values
|
|
430
|
-
let envContent = `
|
|
431
|
-
# Google Cloud Project ID (required)
|
|
432
|
-
GCP_PROJECT_ID="${finalConfig.GCP_PROJECT_ID || ""}"
|
|
433
|
-
|
|
434
|
-
# Path to OAuth client credentials for restricted scopes (optional)
|
|
435
|
-
CLIENT_CREDENTIAL_FILE="${finalConfig.CLIENT_CREDENTIAL_FILE || ""}"
|
|
436
|
-
|
|
437
|
-
# A test file ID for checking authentication (optional)
|
|
438
|
-
DRIVE_TEST_FILE_ID="${finalConfig.DRIVE_TEST_FILE_ID || ""}"
|
|
439
|
-
|
|
440
|
-
# Storage configuration for PropertiesService and CacheService ('FILE' or 'UPSTASH')
|
|
441
|
-
STORE_TYPE="${finalConfig.STORE_TYPE || "FILE"}"
|
|
442
|
-
|
|
443
|
-
# Logging destination for Logger.log() ('CONSOLE', 'CLOUD', 'BOTH', 'NONE')
|
|
444
|
-
LOG_DESTINATION="${finalConfig.LOG_DESTINATION || "CONSOLE"}"
|
|
445
|
-
|
|
446
|
-
# Scopes for authentication
|
|
447
|
-
DEFAULT_SCOPES="${finalConfig.DEFAULT_SCOPES || ""}"
|
|
448
|
-
EXTRA_SCOPES="${finalConfig.EXTRA_SCOPES || ""}"
|
|
449
|
-
`.trim();
|
|
450
|
-
|
|
451
|
-
if (finalConfig.STORE_TYPE === "UPSTASH") {
|
|
452
|
-
envContent += `
|
|
453
|
-
|
|
454
|
-
# Upstash credentials (only used if STORE_TYPE is 'UPSTASH')
|
|
455
|
-
UPSTASH_REDIS_REST_URL="${finalConfig.UPSTASH_REDIS_REST_URL || ""}"
|
|
456
|
-
UPSTASH_REDIS_REST_TOKEN="${finalConfig.UPSTASH_REDIS_REST_TOKEN || ""}"
|
|
457
|
-
`;
|
|
458
|
-
}
|
|
459
|
-
*/
|
|
460
|
-
writeFileSync(envPath, envContent + "\n", "utf8");
|
|
461
|
-
|
|
462
|
-
console.log("--------------------------------------------------");
|
|
463
|
-
console.log("Setup complete. Your .env file has been updated.");
|
|
464
|
-
console.log("--------------------------------------------------");
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Handles the 'auth' command to authenticate with Google Cloud.
|
|
469
|
-
*/
|
|
470
|
-
export function authenticateUser() {
|
|
471
|
-
// First, check if gcloud CLI is available.
|
|
472
|
-
checkForGcloudCli();
|
|
473
|
-
|
|
474
|
-
const rootDirectory = process.cwd();
|
|
475
|
-
const envPath = path.join(rootDirectory, ".env");
|
|
476
|
-
|
|
477
|
-
if (!existsSync(envPath)) {
|
|
478
|
-
console.error(`Error: .env file not found at '${envPath}'`);
|
|
479
|
-
console.error("Please run './gas-fakes.js init' first.");
|
|
480
|
-
process.exit(1);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
dotenv.config({ path: envPath, quiet: true });
|
|
484
|
-
|
|
485
|
-
const {
|
|
486
|
-
GCP_PROJECT_ID,
|
|
487
|
-
DEFAULT_SCOPES,
|
|
488
|
-
EXTRA_SCOPES,
|
|
489
|
-
CLIENT_CREDENTIAL_FILE,
|
|
490
|
-
AC,
|
|
491
|
-
} = process.env;
|
|
492
|
-
|
|
493
|
-
if (!GCP_PROJECT_ID) {
|
|
494
|
-
console.error("Error: GCP_PROJECT_ID is not set in your .env file.");
|
|
495
|
-
process.exit(1);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const defaultScopes =
|
|
499
|
-
DEFAULT_SCOPES ||
|
|
500
|
-
"https://www.googleapis.com/auth/userinfo.email,openid,https://www.googleapis.com/auth/cloud-platform";
|
|
501
|
-
const extraScopes =
|
|
502
|
-
EXTRA_SCOPES ||
|
|
503
|
-
"https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/spreadsheets";
|
|
504
|
-
|
|
505
|
-
let scopes = defaultScopes;
|
|
506
|
-
if (extraScopes && extraScopes.length > 0) {
|
|
507
|
-
scopes += (extraScopes.startsWith(",") ? "" : ",") + extraScopes;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const driveAccessFlag = "--enable-gdrive-access";
|
|
511
|
-
|
|
512
|
-
console.log(`...requesting scopes ${scopes}`);
|
|
513
|
-
|
|
514
|
-
let clientFlag = "";
|
|
515
|
-
if (CLIENT_CREDENTIAL_FILE) {
|
|
516
|
-
console.log("...attempting to use enhanced client credentials");
|
|
517
|
-
|
|
518
|
-
let clientPath = CLIENT_CREDENTIAL_FILE;
|
|
519
|
-
if (!path.isAbsolute(clientPath)) {
|
|
520
|
-
clientPath = path.join(rootDirectory, clientPath);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (existsSync(clientPath)) {
|
|
524
|
-
clientFlag = `--client-id-file="${clientPath}"`;
|
|
525
|
-
} else {
|
|
526
|
-
console.error(
|
|
527
|
-
`Error: Client credential file specified in .env not found at '${clientPath}'`
|
|
528
|
-
);
|
|
529
|
-
process.exit(1);
|
|
530
|
-
}
|
|
531
|
-
} else {
|
|
532
|
-
console.log(
|
|
533
|
-
"\n...CLIENT_CREDENTIAL_FILE is not set. Using default Application Default Credentials (ADC)."
|
|
534
|
-
);
|
|
535
|
-
console.log(
|
|
536
|
-
"...if you have requested any sensitive scopes, you'll see 'This app is blocked message.'"
|
|
537
|
-
);
|
|
538
|
-
console.log(
|
|
539
|
-
"...To allow them see - https://github.com/brucemcpherson/gas-fakes/blob/main/GETTING_STARTED.md\n"
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
const projectId = GCP_PROJECT_ID;
|
|
544
|
-
const activeConfig = AC || "default";
|
|
545
|
-
|
|
546
|
-
console.log("Revoking previous credentials...");
|
|
547
|
-
try {
|
|
548
|
-
execSync("gcloud auth revoke --quiet", { stdio: "ignore" });
|
|
549
|
-
} catch (e) {
|
|
550
|
-
/* ignore */
|
|
551
|
-
}
|
|
552
|
-
try {
|
|
553
|
-
execSync("gcloud auth application-default revoke --quiet", {
|
|
554
|
-
stdio: "ignore",
|
|
555
|
-
});
|
|
556
|
-
} catch (e) {
|
|
557
|
-
/* ignore */
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
console.log(`Ensuring gcloud configuration '${activeConfig}' exists...`);
|
|
561
|
-
try {
|
|
562
|
-
execSync(`gcloud config configurations describe "${activeConfig}"`, {
|
|
563
|
-
stdio: "ignore",
|
|
564
|
-
});
|
|
565
|
-
console.log(`Configuration '${activeConfig}' already exists.`);
|
|
566
|
-
} catch (error) {
|
|
567
|
-
console.log(`Configuration '${activeConfig}' not found. Creating it...`);
|
|
568
|
-
runCommand(`gcloud config configurations create "${activeConfig}"`);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
console.log(`Activating gcloud configuration: ${activeConfig}`);
|
|
572
|
-
runCommand(`gcloud config configurations activate "${activeConfig}"`);
|
|
573
|
-
|
|
574
|
-
console.log(`Setting project to: ${projectId}`);
|
|
575
|
-
runCommand(`gcloud config set project ${projectId}`);
|
|
576
|
-
runCommand(`gcloud config set billing/quota_project ${projectId}`);
|
|
577
|
-
|
|
578
|
-
console.log("Initiating user login...");
|
|
579
|
-
runCommand(`gcloud auth login ${driveAccessFlag}`);
|
|
580
|
-
|
|
581
|
-
console.log("Initiating Application Default Credentials (ADC) login...");
|
|
582
|
-
runCommand(
|
|
583
|
-
`gcloud auth application-default login --scopes="${scopes}" ${clientFlag}`
|
|
584
|
-
);
|
|
585
|
-
runCommand(`gcloud auth application-default set-quota-project ${projectId}`);
|
|
586
|
-
|
|
587
|
-
// --- Verification ---
|
|
588
|
-
console.log("\nVerifying configuration...");
|
|
589
|
-
|
|
590
|
-
const gcloudConfigDir =
|
|
591
|
-
process.env.CLOUDSDK_CONFIG || path.join(os.homedir(), ".config", "gcloud");
|
|
592
|
-
const activeConfigPath = path.join(gcloudConfigDir, "active_config");
|
|
593
|
-
|
|
594
|
-
let currentConfig = "unknown";
|
|
595
|
-
if (existsSync(activeConfigPath)) {
|
|
596
|
-
currentConfig = readFileSync(activeConfigPath, "utf8").trim();
|
|
597
|
-
} else {
|
|
598
|
-
console.warn(
|
|
599
|
-
`Warning: Could not find active_config file at ${activeConfigPath}`
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const currentProject = execSync("gcloud config get project")
|
|
604
|
-
.toString()
|
|
605
|
-
.trim();
|
|
606
|
-
console.log(
|
|
607
|
-
`Active config is ${currentConfig} - project is ${currentProject}`
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
console.log("\nFetching token information...");
|
|
611
|
-
const userToken = execSync("gcloud auth print-access-token")
|
|
612
|
-
.toString()
|
|
613
|
-
.trim();
|
|
614
|
-
const appDefaultToken = execSync(
|
|
615
|
-
"gcloud auth application-default print-access-token"
|
|
616
|
-
)
|
|
617
|
-
.toString()
|
|
618
|
-
.trim();
|
|
619
|
-
|
|
620
|
-
console.log("\n...user token scopes");
|
|
621
|
-
runCommand(
|
|
622
|
-
`curl https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${userToken}`
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
console.log("\n...application default token scopes");
|
|
626
|
-
runCommand(
|
|
627
|
-
`curl https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${appDefaultToken}`
|
|
628
|
-
);
|
|
629
|
-
console.log("\nAuthentication process finished.");
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
/**
|
|
633
|
-
* Handles the 'enableAPIs' command to enable or disable necessary Google Cloud services based on options.
|
|
634
|
-
* @param {object} options Options object provided by commander.js.
|
|
635
|
-
*/
|
|
636
|
-
export function enableGoogleAPIs(options) {
|
|
637
|
-
checkForGcloudCli();
|
|
638
|
-
|
|
639
|
-
const API_SERVICES = {
|
|
640
|
-
drive: "drive.googleapis.com",
|
|
641
|
-
sheets: "sheets.googleapis.com",
|
|
642
|
-
forms: "forms.googleapis.com",
|
|
643
|
-
docs: "docs.googleapis.com",
|
|
644
|
-
gmail: "gmail.googleapis.com",
|
|
645
|
-
logging: "logging.googleapis.com",
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
const servicesToEnable = new Set();
|
|
649
|
-
const servicesToDisable = new Set();
|
|
650
|
-
if (options.all || Object.keys(options).length === 0) {
|
|
651
|
-
Object.values(API_SERVICES).forEach((service) =>
|
|
652
|
-
servicesToEnable.add(service)
|
|
653
|
-
);
|
|
654
|
-
} else {
|
|
655
|
-
for (const key in API_SERVICES) {
|
|
656
|
-
if (options[`e${key}`]) {
|
|
657
|
-
servicesToEnable.add(API_SERVICES[key]);
|
|
658
|
-
}
|
|
659
|
-
if (options[`d${key}`]) {
|
|
660
|
-
servicesToDisable.add(API_SERVICES[key]);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
if (servicesToEnable.size > 0) {
|
|
665
|
-
const enableList = Array.from(servicesToEnable);
|
|
666
|
-
console.log(`Enabling Google Cloud services: ${enableList.join(", ")}...`);
|
|
667
|
-
runCommand(`gcloud services enable ${enableList.join(" ")}`);
|
|
668
|
-
console.log("Services enabled successfully.");
|
|
669
|
-
}
|
|
670
|
-
if (servicesToDisable.size > 0) {
|
|
671
|
-
const disableList = Array.from(servicesToDisable);
|
|
672
|
-
console.log(
|
|
673
|
-
`Disabling Google Cloud services: ${disableList.join(", ")}...`
|
|
674
|
-
);
|
|
675
|
-
runCommand(`gcloud services disable ${disableList.join(" ")}`);
|
|
676
|
-
console.log("Services disabled successfully.");
|
|
677
|
-
}
|
|
678
|
-
if (
|
|
679
|
-
servicesToEnable.size === 0 &&
|
|
680
|
-
servicesToDisable.size === 0 &&
|
|
681
|
-
Object.keys(options).length > 0 &&
|
|
682
|
-
!options.all
|
|
683
|
-
) {
|
|
684
|
-
console.log("No specific APIs were selected to enable or disable.");
|
|
685
|
-
console.log(
|
|
686
|
-
"Use '--all' to enable all default APIs, or specify flags like '--edrive' or '--ddrive'."
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
}
|
package/src/Code.js
DELETED