@mcpher/gas-fakes 1.2.31 → 2.0.0

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.
Files changed (53) hide show
  1. package/.gcloudignore +24 -0
  2. package/README.md +5 -5
  3. package/appsscript.json +101 -0
  4. package/exgcp.sh +4 -4
  5. package/package.json +5 -6
  6. package/src/cli/app.js +9 -1
  7. package/src/cli/executor.js +32 -2
  8. package/src/cli/lib-manager.js +0 -1
  9. package/src/cli/setup.js +330 -228
  10. package/src/cli/utils.js +1 -1
  11. package/src/services/advcalendar/clapis.js +4 -2
  12. package/src/services/advdocs/docapis.js +4 -3
  13. package/src/services/advdrive/drapis.js +8 -5
  14. package/src/services/advforms/formsapis.js +4 -3
  15. package/src/services/advgmail/gmailapis.js +4 -3
  16. package/src/services/advsheets/shapis.js +5 -10
  17. package/src/services/advslides/slapis.js +4 -3
  18. package/src/services/driveapp/fakedriveapp.js +10 -2
  19. package/src/services/logger/fakelogger.js +6 -3
  20. package/src/services/scriptapp/app.js +16 -12
  21. package/src/services/scriptapp/behavior.js +132 -107
  22. package/src/services/session/fakesession.js +24 -9
  23. package/src/services/slidesapp/fakepresentation.js +19 -8
  24. package/src/services/slidesapp/fakeslide.js +45 -20
  25. package/src/services/spreadsheetapp/fakesheet.js +9 -7
  26. package/src/services/stores/fakestores.js +20 -18
  27. package/src/services/stores/gasflex.js +13 -14
  28. package/src/services/urlfetchapp/app.js +3 -3
  29. package/src/support/auth.js +227 -55
  30. package/src/support/slogger.js +42 -0
  31. package/src/support/sxauth.js +42 -39
  32. package/src/support/sxcalendar.js +9 -43
  33. package/src/support/sxdocs.js +6 -42
  34. package/src/support/sxdrive.js +32 -73
  35. package/src/support/sxfetch.js +36 -30
  36. package/src/support/sxforms.js +9 -40
  37. package/src/support/sxgmail.js +9 -37
  38. package/src/support/sxretry.js +79 -0
  39. package/src/support/sxsheets.js +6 -37
  40. package/src/support/sxslides.js +5 -36
  41. package/src/support/sxtoken.js +15 -0
  42. package/src/support/syncit.js +27 -10
  43. package/src/support/utils.js +13 -1
  44. package/src/support/workersync/sxfunctions.js +2 -0
  45. package/src/support/workersync/synclogger.js +22 -5
  46. package/src/support/workersync/worker.js +8 -11
  47. package/testlib.sh +1 -1
  48. package/README.RU.md +0 -373
  49. package/env.setup.template +0 -16
  50. package/run.js +0 -35
  51. package/setup.js +0 -689
  52. package/src/Code.js +0 -3
  53. package/src/appsscript.json +0 -5
package/src/cli/utils.js CHANGED
@@ -4,7 +4,7 @@ const require = createRequire(import.meta.url);
4
4
  const pjson = require("../../package.json");
5
5
 
6
6
  export const VERSION = pjson.version;
7
- export const CLI_VERSION = "0.0.19";
7
+ export const CLI_VERSION = "0.0.20";
8
8
  export const MCP_VERSION = "0.0.7";
9
9
 
10
10
  /**
@@ -3,12 +3,14 @@ import { Auth } from '../../support/auth.js'
3
3
  import { syncLog } from '../../support/workersync/synclogger.js'
4
4
 
5
5
  let __client = null;
6
+ let __authClient = null;
6
7
  syncLog('...importing Calendar API');
7
8
  export const getCalendarApiClient = () => {
8
- const auth = Auth.getAuthClient()
9
- if (!__client) {
9
+ const auth = Auth.getAuthClient();
10
+ if (!__client || auth !== __authClient) {
10
11
  syncLog('Creating new Calendar API client');
11
12
  __client = google.calendar({ version: 'v3', auth });
13
+ __authClient = auth;
12
14
  }
13
15
  return __client;
14
16
  }
@@ -1,15 +1,16 @@
1
1
  import { google } from "googleapis";
2
2
  import { Auth } from '../../support/auth.js'
3
- import { syncLog} from '../../support/workersync/synclogger.js'
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
4
4
 
5
5
  let __client = null;
6
+ let __authClient = null;
6
7
 
7
8
  export const getDocsApiClient = () => {
8
9
  const auth = Auth.getAuthClient()
9
- if (!__client) {
10
+ if (!__client || auth !== __authClient) {
10
11
  syncLog('Creating new Docs API client');
11
12
  __client = google.docs({ version: 'v1', auth });
13
+ __authClient = auth;
12
14
  }
13
15
  return __client;
14
16
  }
15
-
@@ -1,14 +1,17 @@
1
1
  import { google } from "googleapis";
2
2
  import { Auth } from '../../support/auth.js'
3
- import { syncLog} from '../../support/workersync/synclogger.js'
4
-
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
5
4
  let __client = null;
6
- syncLog('...importing Drive API');
5
+ let __authClient = null;
6
+
7
7
  export const getDriveApiClient = () => {
8
- const auth = Auth.getAuthClient()
9
- if (!__client) {
8
+ const auth = Auth.getAuth(); // Now returns the patched AuthClient instance
9
+
10
+ if (!__client || auth !== __authClient) {
10
11
  syncLog('Creating new Drive API client');
12
+ // The Drive SDK sees a standard AuthClient and uses its (hijacked) getAccessToken
11
13
  __client = google.drive({ version: 'v3', auth });
14
+ __authClient = auth;
12
15
  }
13
16
  return __client;
14
17
  }
@@ -1,15 +1,16 @@
1
1
  import { google } from "googleapis";
2
2
  import { Auth } from '../../support/auth.js'
3
- import { syncLog} from '../../support/workersync/synclogger.js'
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
4
4
 
5
5
  let __client = null;
6
+ let __authClient = null;
6
7
 
7
8
  export const getFormsApiClient = () => {
8
9
  const auth = Auth.getAuthClient()
9
- if (!__client) {
10
+ if (!__client || auth !== __authClient) {
10
11
  syncLog('Creating new Forms API client');
11
12
  __client = google.forms({ version: 'v1', auth });
13
+ __authClient = auth;
12
14
  }
13
15
  return __client;
14
16
  }
15
-
@@ -1,15 +1,16 @@
1
1
  import { google } from "googleapis";
2
2
  import { Auth } from '../../support/auth.js'
3
- import { syncLog} from '../../support/workersync/synclogger.js'
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
4
4
 
5
5
  let __client = null;
6
+ let __authClient = null;
6
7
 
7
8
  export const getGmailApiClient = () => {
8
9
  const auth = Auth.getAuthClient()
9
- if (!__client) {
10
+ if (!__client || auth !== __authClient) {
10
11
  syncLog('Creating new Gmail API client');
11
12
  __client = google.gmail({ version: 'v1', auth });
13
+ __authClient = auth;
12
14
  }
13
15
  return __client;
14
16
  }
15
-
@@ -1,21 +1,16 @@
1
1
  import { google } from "googleapis";
2
2
  import { Auth } from '../../support/auth.js'
3
- import { syncLog} from '../../support/workersync/synclogger.js'
4
- let currentAuth = null;
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
4
+
5
5
  let __client = null;
6
+ let __authClient = null;
6
7
  syncLog('...importing Sheets API');
7
8
  export const getSheetsApiClient = () => {
8
9
  const auth = Auth.getAuthClient()
9
-
10
- if (__client && currentAuth !== auth) {
11
- syncLog('Auth has changed - creating new Sheets API client');
12
- __client = null;
13
- }
14
-
15
- currentAuth = auth;
16
- if (!__client) {
10
+ if (!__client || auth !== __authClient) {
17
11
  syncLog('Creating new Sheets API client');
18
12
  __client = google.sheets({ version: 'v4', auth });
13
+ __authClient = auth;
19
14
  }
20
15
  return __client;
21
16
  }
@@ -1,15 +1,16 @@
1
-
2
1
  import { google } from "googleapis";
3
2
  import { Auth } from '../../support/auth.js'
4
- import { syncLog} from '../../support/workersync/synclogger.js'
3
+ import { syncLog } from '../../support/workersync/synclogger.js'
5
4
 
6
5
  let __client = null;
6
+ let __authClient = null;
7
7
  syncLog('...importing Slides API');
8
8
  export const getSlidesApiClient = () => {
9
9
  const auth = Auth.getAuthClient()
10
- if (!__client) {
10
+ if (!__client || auth !== __authClient) {
11
11
  syncLog('Creating new Slides API client');
12
12
  __client = google.slides({ version: 'v1', auth });
13
+ __authClient = auth;
13
14
  }
14
15
  return __client;
15
16
  }
@@ -3,6 +3,8 @@ import { newFakeDriveFile } from './fakedrivefile.js'
3
3
  import { newFakeFolderApp } from './fakefolderapp.js'
4
4
  import { notYetImplemented, isFolder } from '../../support/helpers.js'
5
5
  import { Proxies } from '../../support/proxies.js'
6
+ import { Utils } from '../../support/utils.js'
7
+ const { is } = Utils
6
8
 
7
9
 
8
10
  /**
@@ -42,11 +44,17 @@ export class FakeDriveApp {
42
44
  * @returns {newFakeDriveFile|null}
43
45
  */
44
46
  getFileById(id) {
47
+ if (!is.nonEmptyString(id)) {
48
+ throw new Error(`API call to DriveApp.getFileById failed with error: Invalid argument: id`)
49
+ }
45
50
  const file = Drive.Files.get(id, {}, { allow404: true })
46
51
  return file ? newFakeDriveFile(file) : null
47
52
  }
48
53
 
49
54
  getFolderById(id) {
55
+ if (!is.nonEmptyString(id)) {
56
+ throw new Error(`API call to DriveApp.getFolderById failed with error: Invalid argument: id`)
57
+ }
50
58
  const file = Drive.Files.get(id, {}, { allow404: true })
51
59
  return file ? newFakeDriveFolder(file) : null
52
60
  }
@@ -86,9 +94,9 @@ export class FakeDriveApp {
86
94
  }
87
95
 
88
96
  searchFiles(params) {
89
- return this.folderApp.searchFiles({ params })
97
+ return this.folderApp.searchFiles({ params })
90
98
  }
91
-
99
+
92
100
  searchFolders(params) {
93
101
  return this.folderApp.searchFolders({ params })
94
102
  }
@@ -174,12 +174,15 @@ const writeToCloudOrConsole = (message, loggerInstance) => {
174
174
  loggerInstance.__cloudLogLink = base + encodedQueryParam + `?project=${projectId}`;
175
175
 
176
176
  const apiBase = 'https://logging.googleapis.com/v2/entries:write'
177
+ // Log the actual entry with its logName
178
+ metadata.logName = `projects/${projectId}/logs/${logName.replace('/', '%2F')}`
179
+
177
180
  const payload = JSON.stringify({
178
181
  entries: [
179
182
  metadata
180
183
  ]
181
184
  })
182
- const token = ScriptApp.getOAuthToken()
185
+ const token = ScriptApp.__getSourceOAuthToken()
183
186
  const headers = {
184
187
  Authorization: `Bearer ${token}`,
185
188
  'content-type': "application/json",
@@ -189,9 +192,9 @@ const writeToCloudOrConsole = (message, loggerInstance) => {
189
192
  headers,
190
193
  })
191
194
  if (response.getResponseCode() !== 200) {
192
- console.error ('logging failure', response.getContentText())
195
+ console.error('logging failure', response.getContentText())
193
196
  }
194
-
197
+
195
198
  }
196
199
 
197
200
  };
@@ -8,19 +8,22 @@ import { Syncit } from '../../support/syncit.js'
8
8
  import { Auth } from '../../support/auth.js'
9
9
  import { Proxies } from '../../support/proxies.js'
10
10
  import { newFakeBehavior } from './behavior.js'
11
- import { newCacheDropin , getUserIdFromToken} from '@mcpher/gas-flex-cache'
12
- import {slogger } from "../../support/slogger.js";
11
+ import { newCacheDropin } from '@mcpher/gas-flex-cache'
12
+ import { slogger } from "../../support/slogger.js";
13
13
  /**
14
14
  * fake ScriptApp.getOAuthToken
15
15
  * @return {string} token
16
16
  */
17
17
  const getOAuthToken = () => {
18
- if (Auth.isTokenExpired()) {
19
- const { accessToken, tokenInfo } = Syncit.fxRefreshToken();
20
- Auth.setAccessToken(accessToken);
21
- Auth.setTokenInfo(tokenInfo); // This will also update the expiry time
22
- }
23
- return Auth.getAccessToken();
18
+ return Syncit.fxGetAccessToken(Auth)
19
+ }
20
+
21
+ /**
22
+ * gets the token of the person running the script
23
+ * @return {string} token
24
+ */
25
+ const getSourceOAuthToken = () => {
26
+ return Syncit.fxGetSourceAccessTokenInfo(Auth).token
24
27
  }
25
28
 
26
29
 
@@ -66,7 +69,7 @@ const checkScopesMatch = (required) => {
66
69
  const scopes = Auth.getTokenScopes()
67
70
 
68
71
  // now we're syncronous all the way
69
- const tokened = new Set(scopes.split(" "))
72
+ const tokened = new Set((typeof scopes === 'string' ? scopes : "").split(" "))
70
73
 
71
74
  // see which ones are missing
72
75
  const missing = required.filter(s => {
@@ -115,6 +118,7 @@ if (typeof globalThis[name] === typeof undefined) {
115
118
 
116
119
  _app = {
117
120
  getOAuthToken,
121
+ __getSourceOAuthToken: getSourceOAuthToken,
118
122
  requireAllScopes,
119
123
  requireScopes,
120
124
  getScriptId: Auth.getScriptId,
@@ -122,14 +126,14 @@ if (typeof globalThis[name] === typeof undefined) {
122
126
  return Auth.getProjectId()
123
127
  },
124
128
  get __userId() {
125
- return Auth.getUserId()
129
+ // this is actually the active user/ not the effective user
130
+ return Auth.getActiveUser().id
126
131
  },
127
132
  AuthMode: {
128
133
  FULL: 'FULL'
129
134
  },
130
135
  __behavior: newFakeBehavior(),
131
- __newCacheDropin: newCacheDropin,
132
- __getUserIdFromToken: getUserIdFromToken
136
+ __newCacheDropin: newCacheDropin
133
137
  }
134
138
 
135
139
 
@@ -254,6 +254,10 @@ class FakeBehavior {
254
254
  this.__createdIds = new Set();
255
255
  this.__createdGmailIds = new Set();
256
256
  this.__createdCalendarIds = new Set();
257
+ // Specifically for sandbox mode - files created when sandbox mode is on
258
+ this.__allowedIds = new Set();
259
+ this.__allowedGmailIds = new Set();
260
+ this.__allowedCalendarIds = new Set();
257
261
  // in sandbox mode we only allow access to files created in this instance
258
262
  // this is to emulate the behavior of a drive.file scope
259
263
  this.__sandboxMode = false;
@@ -341,39 +345,69 @@ class FakeBehavior {
341
345
  get sandBoxMode() {
342
346
  return this.__sandboxMode;
343
347
  }
344
- addFile(id) {
348
+ resetDrive() {
349
+ this.__createdIds.clear();
350
+ this.__allowedIds.clear();
351
+ return this;
352
+ }
353
+ resetGmail() {
354
+ this.__createdGmailIds.clear();
355
+ this.__allowedGmailIds.clear();
356
+ return this;
357
+ }
358
+ resetCalendar() {
359
+ this.__createdCalendarIds.clear();
360
+ this.__allowedCalendarIds.clear();
361
+ return this;
362
+ }
363
+ reset() {
364
+ this.resetDrive();
365
+ this.resetGmail();
366
+ this.resetCalendar();
367
+ return this;
368
+ }
369
+ addFile(id, force = false) {
345
370
  if (!is.nonEmptyString(id)) {
346
371
  throw new Error(`Invalid sandbox id parameter (${id}) - must be a non-empty string`);
347
372
  }
348
- if (!this.isKnown(id)) {
349
- slogger.log(`...adding file ${id} to sandbox allowed list`);
373
+ if (this.sandboxMode || force) {
350
374
  this.__createdIds.add(id);
375
+ if (!this.__allowedIds.has(id)) {
376
+ slogger.log(`...adding file ${id} to sandbox allowed list`);
377
+ this.__allowedIds.add(id);
378
+ }
351
379
  }
352
380
  return id
353
381
  }
354
- addGmailId(id) {
382
+ addGmailId(id, force = false) {
355
383
  if (!is.nonEmptyString(id)) {
356
384
  throw new Error(`Invalid sandbox id parameter (${id}) - must be a non-empty string`);
357
385
  }
358
- if (!this.isKnownGmail(id)) {
359
- slogger.log(`...adding gmail id ${id} to sandbox allowed list`);
386
+ if (this.sandboxMode || force) {
360
387
  this.__createdGmailIds.add(id);
388
+ if (!this.__allowedGmailIds.has(id)) {
389
+ slogger.log(`...adding gmail id ${id} to sandbox allowed list`);
390
+ this.__allowedGmailIds.add(id);
391
+ }
361
392
  }
362
393
  return id
363
394
  }
364
- addCalendarId(id) {
395
+ addCalendarId(id, force = false) {
365
396
  if (!is.nonEmptyString(id)) {
366
397
  throw new Error(`Invalid sandbox id parameter (${id}) - must be a non-empty string`);
367
398
  }
368
- if (!this.isKnownCalendar(id)) {
369
- slogger.log(`...adding calendar id ${id} to sandbox allowed list`);
399
+ if (this.sandboxMode || force) {
370
400
  this.__createdCalendarIds.add(id);
401
+ if (!this.__allowedCalendarIds.has(id)) {
402
+ slogger.log(`...adding calendar id ${id} to sandbox allowed list`);
403
+ this.__allowedCalendarIds.add(id);
404
+ }
371
405
  }
372
406
  return id
373
407
  }
374
408
  isAccessible(id, serviceName, accessType = 'read') {
375
- if (this.sandboxMode && !is.nonEmptyString(id)) {
376
- throw new Error(`Invalid sandbox id parameter (${id}) - must be a non-empty string`);
409
+ if (!is.nonEmptyString(id)) {
410
+ throw new Error(`API call to ${serviceName} failed with error: Invalid argument: id`);
377
411
  }
378
412
 
379
413
  // Advanced services should inherit sandbox rules from their App counterparts.
@@ -458,114 +492,105 @@ class FakeBehavior {
458
492
  }
459
493
  trash() {
460
494
  let trashed = [];
461
-
462
- // Drive cleanup
463
- // Use DriveApp.cleanup if exists? DriveApp doesn't map 1:1 to services list exactly for this, but 'DriveApp' defaults to global.
464
- // Let's use global cleanup setting for Drive for now, OR DriveApp specific if we want consistent granularity.
465
- // For now, keep behavior: this.__cleanup controls generic/drive files.
466
- // OR: check `this.sandboxService.DriveApp.cleanup`?
467
- // Let's stick to user request: "separate cleanup property for gmail settings".
468
- // So global `this.__cleanup` works for Drive.
469
-
470
- // Actually, `this.__cleanup` getter on global overrides? No.
471
- // The prompt says "separate cleanup property for gmail settings".
472
- // So if I set `ScriptApp.__behavior.cleanup` it applies to everything unless overridden?
473
- // My implementation in FakeSandboxService maps `cleanup` to global if null.
474
- // So `gmailSettings.cleanup` will return strict boolean.
475
-
476
- if (this.__cleanup) {
477
- trashed = Array.from(this.__createdIds).reduce((acc, id) => {
478
- let d = null
479
- try {
480
- d = DriveApp.getFileById(id)
481
- } catch (e) {
482
- d = DriveApp.getFolderById(id)
483
- }
484
- if (d) {
485
- d.setTrashed(true);
486
- slogger.log(`...trashed file ${d.getName()} (${id})`);
487
- acc.push(id);
488
- }
489
- return acc;
490
- }, []);
491
- this.__createdIds.clear();
492
- } else {
493
- slogger.log('...skipping cleaning up sandbox files (Drive)');
494
- }
495
-
496
- // Clean up Gmail artifacts
497
- let trashedGmail = [];
498
- const gmailSettings = this.sandboxService.GmailApp;
499
- const gmailCleanup = gmailSettings && gmailSettings.cleanup; // This will return true/false (inherits or specific)
500
-
501
- if (gmailCleanup) {
502
- trashedGmail = Array.from(this.__createdGmailIds).reduce((acc, id) => {
503
- // Try to determine type or just try deleting as label, then message/thread?
504
- // IDs for labels vs threads/messages might overlap or be distinct formats.
505
- // Label IDs are usually strings like 'Label_123'. Thread IDs are hex strings.
506
- // We can try fetching as label first.
507
- try {
508
- // Try as label
509
- Gmail.Users.Labels.remove('me', id);
510
- slogger.log(`...deleted gmail label ${id}`);
511
- acc.push(id);
495
+ const wasSandbox = this.sandboxMode;
496
+ this.sandboxMode = false;
497
+ try {
498
+ // Drive cleanup
499
+ if (this.__cleanup) {
500
+ trashed = Array.from(this.__createdIds).reduce((acc, id) => {
501
+ let d = null
502
+ try {
503
+ d = DriveApp.getFileById(id)
504
+ } catch (e) {
505
+ d = DriveApp.getFolderById(id)
506
+ }
507
+ if (d) {
508
+ d.setTrashed(true);
509
+ slogger.log(`...trashed file ${d.getName()} (${id})`);
510
+ acc.push(id);
511
+ }
512
512
  return acc;
513
- } catch (e) { /* not a label or failed */ }
513
+ }, []);
514
+ this.__createdIds.clear();
515
+ this.__allowedIds.clear();
516
+ } else {
517
+ slogger.log('...skipping cleaning up sandbox files (Drive)');
518
+ }
519
+
520
+ // Clean up Gmail artifacts
521
+ let trashedGmail = [];
522
+ const gmailSettings = this.sandboxService.GmailApp;
523
+ const gmailCleanup = gmailSettings && gmailSettings.cleanup; // This will return true/false (inherits or specific)
524
+
525
+ if (gmailCleanup) {
526
+ trashedGmail = Array.from(this.__createdGmailIds).reduce((acc, id) => {
527
+ try {
528
+ // Try as label
529
+ Gmail.Users.Labels.remove('me', id);
530
+ slogger.log(`...deleted gmail label ${id}`);
531
+ acc.push(id);
532
+ return acc;
533
+ } catch (e) { /* not a label or failed */ }
534
+
535
+ try {
536
+ // Try as thread - move to trash
537
+ Gmail.Users.Threads.trash('me', id);
538
+ slogger.log(`...trashed gmail thread ${id}`);
539
+ acc.push(id);
540
+ return acc;
541
+ } catch (e) { /* not a thread */ }
542
+
543
+ try {
544
+ Gmail.Users.Messages.trash('me', id);
545
+ slogger.log(`...trashed gmail message ${id}`);
546
+ acc.push(id);
547
+ return acc;
548
+ } catch (e) { /* not a message */ }
514
549
 
515
- try {
516
- // Try as thread - move to trash
517
- Gmail.Users.Threads.trash('me', id);
518
- slogger.log(`...trashed gmail thread ${id}`);
519
- acc.push(id);
520
550
  return acc;
521
- } catch (e) { /* not a thread */ }
551
+ }, []);
552
+ this.__createdGmailIds.clear();
553
+ this.__allowedGmailIds.clear();
554
+ } else {
555
+ slogger.log('...skipping cleaning up sandbox files (Gmail)');
556
+ }
522
557
 
523
- try {
524
- Gmail.Users.Messages.trash('me', id);
525
- slogger.log(`...trashed gmail message ${id}`);
526
- acc.push(id);
558
+ // Clean up Calendar artifacts
559
+ let trashedCalendars = [];
560
+ const calendarSettings = this.sandboxService.CalendarApp;
561
+ const calendarCleanup = calendarSettings && calendarSettings.cleanup;
562
+
563
+ if (calendarCleanup) {
564
+ trashedCalendars = Array.from(this.__createdCalendarIds).reduce((acc, id) => {
565
+ try {
566
+ // Delete calendar
567
+ Calendar.Calendars.delete(id, { noLog404: true });
568
+ slogger.log(`...deleted calendar ${id}`);
569
+ acc.push(id);
570
+ } catch (e) {
571
+ slogger.log(`...failed to delete calendar ${id}: ${e.message}`);
572
+ }
527
573
  return acc;
528
- } catch (e) { /* not a message */ }
529
-
530
- return acc;
531
- }, []);
532
- this.__createdGmailIds.clear();
533
- } else {
534
- slogger.log('...skipping cleaning up sandbox files (Gmail)');
535
- }
536
-
537
- // Clean up Calendar artifacts
538
- let trashedCalendars = [];
539
- const calendarSettings = this.sandboxService.CalendarApp;
540
- const calendarCleanup = calendarSettings && calendarSettings.cleanup;
541
-
542
- if (calendarCleanup) {
543
- trashedCalendars = Array.from(this.__createdCalendarIds).reduce((acc, id) => {
544
- try {
545
- // Delete calendar
546
- Calendar.Calendars.delete(id, { noLog404: true });
547
- slogger.log(`...deleted calendar ${id}`);
548
- acc.push(id);
549
- } catch (e) {
550
- slogger.log(`...failed to delete calendar ${id}: ${e.message}`);
551
- }
552
- return acc;
553
- }, []);
554
- this.__createdCalendarIds.clear();
555
- } else {
556
- slogger.log('...skipping cleaning up sandbox calendars');
557
- }
574
+ }, []);
575
+ this.__createdCalendarIds.clear();
576
+ this.__allowedCalendarIds.clear();
577
+ } else {
578
+ slogger.log('...skipping cleaning up sandbox calendars');
579
+ }
558
580
 
559
- slogger.log(`...trashed ${trashed.length} sandboxed files, ${trashedGmail.length} gmail items, and ${trashedCalendars.length} calendars`);
581
+ slogger.log(`...trashed ${trashed.length} sandboxed files, ${trashedGmail.length} gmail items, and ${trashedCalendars.length} calendars`);
582
+ } finally {
583
+ this.sandboxMode = wasSandbox;
584
+ }
560
585
  return trashed;
561
586
  }
562
587
  isKnown(id) {
563
- return this.__createdIds.has(id);
588
+ return this.__allowedIds.has(id);
564
589
  }
565
590
  isKnownGmail(id) {
566
- return this.__createdGmailIds.has(id);
591
+ return this.__allowedGmailIds.has(id);
567
592
  }
568
593
  isKnownCalendar(id) {
569
- return this.__createdCalendarIds.has(id);
594
+ return this.__allowedCalendarIds.has(id);
570
595
  }
571
596
  }
@@ -1,10 +1,13 @@
1
- import { newFakeUser} from '../common/fakeuser.js'
2
- import { Auth } from '../../support/auth.js'
1
+ import { newFakeUser } from '../common/fakeuser.js'
3
2
  import { Proxies } from '../../support/proxies.js'
4
-
3
+ import { Syncit } from '../../support/syncit.js'
4
+ import { Auth } from '../../support/auth.js'
5
5
  class FakeSession {
6
- constructor () {
7
- this._activeUser = newFakeUser (Auth.getTokenInfo())
6
+ constructor() {
7
+ // Active user is the impersonated user (the one we're acting as)
8
+ this._activeUser = newFakeUser(Syncit.fxGetAccessTokenInfo().tokenInfo)
9
+ // Effective user is also the impersonated user in this context
10
+ this._effectiveUser = newFakeUser(Syncit.fxGetAccessTokenInfo().tokenInfo)
8
11
  }
9
12
  getActiveUser() {
10
13
  return this._activeUser
@@ -14,15 +17,27 @@ class FakeSession {
14
17
  * @returns {string}
15
18
  */
16
19
  getEffectiveUser() {
17
- return this.getActiveUser()
20
+ return this._effectiveUser
18
21
  }
19
22
  /**
20
23
  * @returns {string}
21
24
  */
22
25
  getActiveUserLocale() {
23
- const lang = process.env.LANG || ''
26
+ const lang =
27
+ process.env.LANG ||
28
+ process.env.LANGUAGE ||
29
+ process.env.LC_ALL ||
30
+ process.env.LC_MESSAGES ||
31
+ (function () {
32
+ try {
33
+ return Intl.DateTimeFormat().resolvedOptions().locale
34
+ } catch (e) {
35
+ return 'en'
36
+ }
37
+ })() ||
38
+ 'en'
24
39
  // it'll be a format like en_US.UTF-8 so we need to drop the encoding to be like apps script
25
- return lang.replace(/\_.*/,'')
40
+ return lang.split(/[._-]/)[0]
26
41
  }
27
42
  /**
28
43
  * this'll come from the manifest on Node (on Apps Script it'll be where the user is running from)
@@ -42,4 +57,4 @@ class FakeSession {
42
57
  }
43
58
  }
44
59
 
45
- export const newFakeSession = (...args) => Proxies.guard (new FakeSession(...args))
60
+ export const newFakeSession = (...args) => Proxies.guard(new FakeSession(...args))