@mcpher/gas-fakes 1.2.26 → 1.2.28
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/package.json +1 -1
- package/src/cli/setup.js +35 -6
- package/src/cli/utils.js +8 -7
- package/src/index.js +1 -0
- package/src/services/advcalendar/clapis.js +14 -0
- package/src/services/advcalendar/fakeadvcalendarcalendarlist.js +30 -0
- package/src/services/advcalendar/fakeadvcalendarcalendars.js +25 -0
- package/src/services/advcalendar/fakeadvcalendarevents.js +51 -1
- package/src/services/calendarapp/app.js +11 -0
- package/src/services/calendarapp/fakecalendar.js +328 -0
- package/src/services/calendarapp/fakecalendarapp.js +147 -0
- package/src/services/calendarapp/fakecalendarevent.js +44 -0
- package/src/services/calendarapp/fakecalendareventseries.js +36 -0
- package/src/services/calendarapp/fakeeventrecurrence.js +19 -0
- package/src/services/enums/calendarenums.js +85 -0
- package/src/services/enums/slidesenums.js +1 -1
- package/src/services/formapp/fakechoiceitem.js +1 -1
- package/src/services/formapp/fakeform.js +4 -3
- package/src/services/formapp/fakeformitem.js +1 -2
- package/src/services/formapp/fakeformresponse.js +3 -2
- package/src/services/formapp/fakelistitem.js +3 -2
- package/src/services/scriptapp/behavior.js +56 -1
- package/src/services/slidesapp/fakeaffinetransform.js +55 -0
- package/src/services/slidesapp/fakeaffinetransformbuilder.js +64 -0
- package/src/services/slidesapp/fakeautofit.js +49 -0
- package/src/services/slidesapp/fakeautotext.js +29 -0
- package/src/services/slidesapp/fakeconnectionsite.js +24 -0
- package/src/services/slidesapp/fakelayout.js +38 -0
- package/src/services/slidesapp/fakeline.js +195 -0
- package/src/services/slidesapp/fakelinefill.js +58 -0
- package/src/services/slidesapp/fakelink.js +31 -0
- package/src/services/slidesapp/fakemaster.js +28 -0
- package/src/services/slidesapp/fakenotespage.js +19 -0
- package/src/services/slidesapp/fakepagebackground.js +17 -0
- package/src/services/slidesapp/fakepageelement.js +395 -0
- package/src/services/slidesapp/fakeparagraph.js +46 -0
- package/src/services/slidesapp/fakepoint.js +24 -0
- package/src/services/slidesapp/fakepresentation.js +61 -3
- package/src/services/slidesapp/fakeshape.js +49 -0
- package/src/services/slidesapp/fakeslide.js +232 -0
- package/src/services/slidesapp/fakeslidesapp.js +5 -0
- package/src/services/slidesapp/faketextrange.js +253 -0
- package/src/services/slidesapp/pageelementfactory.js +19 -0
- package/src/services/stores/gasflex.js +1 -1
- package/src/services/urlfetchapp/app.js +17 -1
- package/src/support/sxcalendar.js +61 -0
- package/src/support/sxfetch.js +39 -13
- package/src/support/sxforms.js +3 -1
- package/src/support/syncit.js +16 -2
- package/src/support/utils.js +2 -0
- package/src/support/workersync/sxfunctions.js +1 -0
package/package.json
CHANGED
package/src/cli/setup.js
CHANGED
|
@@ -171,6 +171,11 @@ export async function initializeConfiguration(options = {}) {
|
|
|
171
171
|
value: "https://www.googleapis.com/auth/gmail.modify",
|
|
172
172
|
},
|
|
173
173
|
*/
|
|
174
|
+
{
|
|
175
|
+
sensitivity: "sensitive",
|
|
176
|
+
title: "Calendar (full access)",
|
|
177
|
+
value: "https://www.googleapis.com/auth/calendar",
|
|
178
|
+
},
|
|
174
179
|
{
|
|
175
180
|
// actually labels are not sensitive
|
|
176
181
|
title: "Gmail labels",
|
|
@@ -408,9 +413,9 @@ export async function initializeConfiguration(options = {}) {
|
|
|
408
413
|
/**
|
|
409
414
|
* Handles the 'auth' command to authenticate with Google Cloud.
|
|
410
415
|
*/
|
|
411
|
-
export function authenticateUser() {
|
|
416
|
+
export async function authenticateUser() {
|
|
412
417
|
// First, check if gcloud CLI is available.
|
|
413
|
-
checkForGcloudCli();
|
|
418
|
+
await checkForGcloudCli();
|
|
414
419
|
|
|
415
420
|
const rootDirectory = process.cwd();
|
|
416
421
|
const envPath = path.join(rootDirectory, ".env");
|
|
@@ -486,13 +491,14 @@ export function authenticateUser() {
|
|
|
486
491
|
|
|
487
492
|
console.log("Revoking previous credentials...");
|
|
488
493
|
try {
|
|
489
|
-
execSync("gcloud auth revoke --quiet", { stdio: "ignore" });
|
|
494
|
+
execSync("gcloud auth revoke --quiet", { stdio: "ignore", shell: true });
|
|
490
495
|
} catch (e) {
|
|
491
496
|
/* ignore */
|
|
492
497
|
}
|
|
493
498
|
try {
|
|
494
499
|
execSync("gcloud auth application-default revoke --quiet", {
|
|
495
500
|
stdio: "ignore",
|
|
501
|
+
shell: true,
|
|
496
502
|
});
|
|
497
503
|
} catch (e) {
|
|
498
504
|
/* ignore */
|
|
@@ -502,6 +508,7 @@ export function authenticateUser() {
|
|
|
502
508
|
try {
|
|
503
509
|
execSync(`gcloud config configurations describe "${activeConfig}"`, {
|
|
504
510
|
stdio: "ignore",
|
|
511
|
+
shell: true,
|
|
505
512
|
});
|
|
506
513
|
console.log(`Configuration '${activeConfig}' already exists.`);
|
|
507
514
|
} catch (error) {
|
|
@@ -519,6 +526,26 @@ export function authenticateUser() {
|
|
|
519
526
|
console.log("Initiating user login...");
|
|
520
527
|
runCommandSync(`gcloud auth login ${driveAccessFlag}`);
|
|
521
528
|
|
|
529
|
+
// --- Verify that the user is actually logged in ---
|
|
530
|
+
try {
|
|
531
|
+
const currentAccount = execSync("gcloud config get-value account", {
|
|
532
|
+
encoding: "utf8",
|
|
533
|
+
shell: true,
|
|
534
|
+
}).trim();
|
|
535
|
+
|
|
536
|
+
if (!currentAccount || currentAccount === "(unset)") {
|
|
537
|
+
console.error("\n[Error] Login appeared to fail or no account selected.");
|
|
538
|
+
console.error(
|
|
539
|
+
"Please try running 'gcloud auth login' manually to diagnose issues."
|
|
540
|
+
);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
console.log(`Successfully logged in as: ${currentAccount}`);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.error("\n[Error] Failed to verify logged-in account.");
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
|
|
522
549
|
console.log("Initiating Application Default Credentials (ADC) login...");
|
|
523
550
|
runCommandSync(
|
|
524
551
|
`gcloud auth application-default login --scopes="${scopes}" ${clientFlag}`
|
|
@@ -543,7 +570,7 @@ export function authenticateUser() {
|
|
|
543
570
|
);
|
|
544
571
|
}
|
|
545
572
|
|
|
546
|
-
const currentProject = execSync("gcloud config get project")
|
|
573
|
+
const currentProject = execSync("gcloud config get project", { shell: true })
|
|
547
574
|
.toString()
|
|
548
575
|
.trim();
|
|
549
576
|
console.log(
|
|
@@ -551,11 +578,12 @@ export function authenticateUser() {
|
|
|
551
578
|
);
|
|
552
579
|
|
|
553
580
|
console.log("\nFetching token information...");
|
|
554
|
-
const userToken = execSync("gcloud auth print-access-token")
|
|
581
|
+
const userToken = execSync("gcloud auth print-access-token", { shell: true })
|
|
555
582
|
.toString()
|
|
556
583
|
.trim();
|
|
557
584
|
const appDefaultToken = execSync(
|
|
558
|
-
"gcloud auth application-default print-access-token"
|
|
585
|
+
"gcloud auth application-default print-access-token",
|
|
586
|
+
{ shell: true }
|
|
559
587
|
)
|
|
560
588
|
.toString()
|
|
561
589
|
.trim();
|
|
@@ -586,6 +614,7 @@ export function enableGoogleAPIs(options) {
|
|
|
586
614
|
docs: "docs.googleapis.com",
|
|
587
615
|
gmail: "gmail.googleapis.com",
|
|
588
616
|
logging: "logging.googleapis.com",
|
|
617
|
+
calendar: "calendar"
|
|
589
618
|
};
|
|
590
619
|
|
|
591
620
|
const servicesToEnable = new Set();
|
package/src/cli/utils.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
1
|
+
import { spawn, execSync } from "child_process";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
|
|
4
3
|
const require = createRequire(import.meta.url);
|
|
5
4
|
const pjson = require("../../package.json");
|
|
6
5
|
|
|
7
6
|
export const VERSION = pjson.version;
|
|
8
|
-
export const CLI_VERSION = "0.0.
|
|
7
|
+
export const CLI_VERSION = "0.0.19";
|
|
9
8
|
export const MCP_VERSION = "0.0.7";
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -26,7 +25,8 @@ export function normalizeScriptNewlines(text) {
|
|
|
26
25
|
*/
|
|
27
26
|
export function spawnCommand(command, args) {
|
|
28
27
|
return new Promise((resolve, reject) => {
|
|
29
|
-
|
|
28
|
+
// shell: true is important for Windows to find .cmd/.bat files correctly
|
|
29
|
+
const child = spawn(command, args, { shell: true });
|
|
30
30
|
let stdout = "";
|
|
31
31
|
let stderr = "";
|
|
32
32
|
|
|
@@ -73,10 +73,11 @@ export async function checkForGcloudCli() {
|
|
|
73
73
|
/**
|
|
74
74
|
* Helper function to run a shell command sync (used in setup).
|
|
75
75
|
*/
|
|
76
|
-
export
|
|
77
|
-
const { execSync } = await import("child_process");
|
|
76
|
+
export function runCommandSync(command) {
|
|
78
77
|
try {
|
|
79
|
-
|
|
78
|
+
// shell: true is explicitly added to ensure compatibility on Windows,
|
|
79
|
+
// especially for handling interactive commands or batch files like gcloud.cmd
|
|
80
|
+
execSync(command, { stdio: "inherit", shell: true });
|
|
80
81
|
} catch (error) {
|
|
81
82
|
console.error(`\nError executing command: ${command}`);
|
|
82
83
|
process.exit(1);
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import './services/urlfetchapp/app.js'
|
|
|
7
7
|
import './services/utilities/app.js'
|
|
8
8
|
import './services/spreadsheetapp/app.js'
|
|
9
9
|
import './services/gmailapp/app.js'
|
|
10
|
+
import './services/calendarapp/app.js'
|
|
10
11
|
import './services/session/app.js'
|
|
11
12
|
import './services/advdrive/app.js'
|
|
12
13
|
import './services/advsheets/app.js'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { Auth } from '../../support/auth.js'
|
|
3
|
+
import { syncLog } from '../../support/workersync/synclogger.js'
|
|
4
|
+
|
|
5
|
+
let __client = null;
|
|
6
|
+
syncLog('...importing Calendar API');
|
|
7
|
+
export const getCalendarApiClient = () => {
|
|
8
|
+
const auth = Auth.getAuthClient()
|
|
9
|
+
if (!__client) {
|
|
10
|
+
syncLog('Creating new Calendar API client');
|
|
11
|
+
__client = google.calendar({ version: 'v3', auth });
|
|
12
|
+
}
|
|
13
|
+
return __client;
|
|
14
|
+
}
|
|
@@ -18,4 +18,34 @@ class FakeAdvCalendarCalendarList extends FakeAdvResource {
|
|
|
18
18
|
this.calendar = mainService;
|
|
19
19
|
this.__fakeObjectType = "Calendar.CalendarList";
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
list(options = {}) {
|
|
23
|
+
const { response, data } = this._call("list", options);
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(calendarId, options) {
|
|
28
|
+
const { response, data } = this._call("get", { calendarId, ...options });
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
insert(requestBody, options) {
|
|
33
|
+
const { response, data } = this._call("insert", { requestBody, ...options });
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
patch(requestBody, calendarId, options) {
|
|
38
|
+
const { response, data } = this._call("patch", { calendarId, requestBody, ...options });
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
update(requestBody, calendarId, options) {
|
|
43
|
+
const { response, data } = this._call("update", { calendarId, requestBody, ...options });
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
remove(calendarId, options) {
|
|
48
|
+
const { response, data } = this._call("remove", { calendarId, ...options });
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
21
51
|
}
|
|
@@ -18,4 +18,29 @@ class FakeAdvCalendarCalendars extends FakeAdvResource {
|
|
|
18
18
|
this.calendar = mainService;
|
|
19
19
|
this.__fakeObjectType = "Calendar.Calendars";
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
get(calendarId, options) {
|
|
23
|
+
const { response, data } = this._call("get", { calendarId, ...options });
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
insert(requestBody, options) {
|
|
28
|
+
const { response, data } = this._call("insert", { requestBody, ...options });
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
patch(requestBody, calendarId, options) {
|
|
33
|
+
const { response, data } = this._call("patch", { calendarId, requestBody, ...options });
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
update(requestBody, calendarId, options) {
|
|
38
|
+
const { response, data } = this._call("update", { calendarId, requestBody, ...options });
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
delete(calendarId, options) {
|
|
43
|
+
const { response, data } = this._call("delete", { calendarId, ...options });
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
21
46
|
}
|
|
@@ -18,4 +18,54 @@ class FakeAdvCalendarEvents extends FakeAdvResource {
|
|
|
18
18
|
this.calendar = mainService;
|
|
19
19
|
this.__fakeObjectType = "Calendar.Events";
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
list(calendarId, options) {
|
|
23
|
+
const { response, data } = this._call("list", { calendarId, ...options });
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(calendarId, eventId, options) {
|
|
28
|
+
const { response, data } = this._call("get", { calendarId, eventId, ...options });
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
insert(requestBody, calendarId, options) {
|
|
33
|
+
const { response, data } = this._call("insert", { calendarId, requestBody, ...options });
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
update(requestBody, calendarId, eventId, options) {
|
|
38
|
+
const { response, data } = this._call("update", { calendarId, eventId, requestBody, ...options });
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
patch(requestBody, calendarId, eventId, options) {
|
|
43
|
+
const { response, data } = this._call("patch", { calendarId, eventId, requestBody, ...options });
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
delete(calendarId, eventId, options) {
|
|
48
|
+
const { response, data } = this._call("delete", { calendarId, eventId, ...options });
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
quickAdd(calendarId, text, options) {
|
|
53
|
+
const { response, data } = this._call("quickAdd", { calendarId, text, ...options });
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
move(calendarId, eventId, destinationCalendarId, options) {
|
|
58
|
+
const { response, data } = this._call("move", { calendarId, eventId, destinationCalendarId, ...options });
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
import(requestBody, calendarId, options) {
|
|
63
|
+
const { response, data } = this._call("import", { calendarId, requestBody, ...options });
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
instances(calendarId, eventId, options) {
|
|
68
|
+
const { response, data } = this._call("instances", { calendarId, eventId, ...options });
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* the idea here is to create an empty global entry for the singleton
|
|
5
|
+
* but only load it when it is actually used.
|
|
6
|
+
*/
|
|
7
|
+
import { newFakeCalendarApp as maker } from './fakecalendarapp.js';
|
|
8
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
9
|
+
|
|
10
|
+
let _app = null;
|
|
11
|
+
_app = lazyLoaderApp(_app, 'CalendarApp', maker)
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeCalendarEvent } from './fakecalendarevent.js';
|
|
3
|
+
import { newFakeCalendarEventSeries } from './fakecalendareventseries.js';
|
|
4
|
+
|
|
5
|
+
export const newFakeCalendar = (...args) => {
|
|
6
|
+
return Proxies.guard(new FakeCalendar(...args));
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a calendar in the CalendarApp service.
|
|
11
|
+
* @see https://developers.google.com/apps-script/reference/calendar/calendar
|
|
12
|
+
*/
|
|
13
|
+
export class FakeCalendar {
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} id The calendar ID.
|
|
16
|
+
* @param {object} resource The underlying Calendar resource (Advanced).
|
|
17
|
+
*/
|
|
18
|
+
constructor(id, resource) {
|
|
19
|
+
this.__id = id;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Internal helper to refresh or get the current resource (Calendars).
|
|
24
|
+
*/
|
|
25
|
+
get __resource() {
|
|
26
|
+
// calendar caching will will only update if its changed
|
|
27
|
+
return Calendar.Calendars.get(this.__id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Internal helper to get the current list entry resource (CalendarList).
|
|
32
|
+
* Used for user-specific properties like color, hidden, selected.
|
|
33
|
+
*/
|
|
34
|
+
get __listEntry() {
|
|
35
|
+
try {
|
|
36
|
+
return Calendar.CalendarList.get(this.__id);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getId() {
|
|
43
|
+
return this.__id || this.__resource.id;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getName() {
|
|
47
|
+
// summary can be in list entry (override) or calendar resource
|
|
48
|
+
const entry = this.__listEntry;
|
|
49
|
+
if (entry && entry.summaryOverride) return entry.summaryOverride;
|
|
50
|
+
return this.__resource.summary || '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setName(name) {
|
|
54
|
+
this.__checkWriteAccess();
|
|
55
|
+
// Sets the name of the calendar. This updates the global name (Calendars).
|
|
56
|
+
Calendar.Calendars.patch({ summary: name }, this.getId());
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getDescription() {
|
|
61
|
+
return this.__resource.description || '';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setDescription(description) {
|
|
65
|
+
this.__checkWriteAccess();
|
|
66
|
+
Calendar.Calendars.patch({ description }, this.getId());
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getTimeZone() {
|
|
71
|
+
return this.__resource.timeZone || '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setTimeZone(timeZone) {
|
|
75
|
+
this.__checkWriteAccess();
|
|
76
|
+
Calendar.Calendars.patch({ timeZone }, this.getId());
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getColor() {
|
|
81
|
+
const entry = this.__listEntry;
|
|
82
|
+
return entry ? entry.backgroundColor : '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setColor(color) {
|
|
86
|
+
this.__checkWriteAccess();
|
|
87
|
+
Calendar.CalendarList.patch({ backgroundColor: color }, this.getId());
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isHidden() {
|
|
92
|
+
const entry = this.__listEntry;
|
|
93
|
+
return entry ? !!entry.hidden : false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setHidden(hidden) {
|
|
97
|
+
this.__checkWriteAccess();
|
|
98
|
+
Calendar.CalendarList.patch({ hidden }, this.getId());
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
isSelected() {
|
|
103
|
+
const entry = this.__listEntry;
|
|
104
|
+
return entry ? !!entry.selected : false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setSelected(selected) {
|
|
108
|
+
this.__checkWriteAccess();
|
|
109
|
+
Calendar.CalendarList.patch({ selected }, this.getId());
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
isMyPrimaryCalendar() {
|
|
114
|
+
return this.getId() === 'primary' || (this.__listEntry && this.__listEntry.primary);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isOwnedByMe() {
|
|
118
|
+
const entry = this.__listEntry;
|
|
119
|
+
return entry ? entry.accessRole === 'owner' : false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Permanently deletes a calendar.
|
|
124
|
+
*/
|
|
125
|
+
deleteCalendar() {
|
|
126
|
+
this.__checkDeleteAccess();
|
|
127
|
+
Calendar.Calendars.remove(this.getId());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
unsubscribeFromCalendar() {
|
|
131
|
+
// Unsubscribes the user from a calendar (removes from list).
|
|
132
|
+
Calendar.CalendarList.remove(this.getId());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- Events ---
|
|
136
|
+
|
|
137
|
+
createEvent(title, startTime, endTime, options) {
|
|
138
|
+
if (startTime.getTime() >= endTime.getTime()) {
|
|
139
|
+
throw new Error('Event start date must be before event end date.');
|
|
140
|
+
}
|
|
141
|
+
this.__checkWriteAccess();
|
|
142
|
+
const resource = {
|
|
143
|
+
summary: title,
|
|
144
|
+
start: { dateTime: startTime.toISOString() },
|
|
145
|
+
end: { dateTime: endTime.toISOString() }
|
|
146
|
+
};
|
|
147
|
+
this.__applyEventOptions(resource, options);
|
|
148
|
+
|
|
149
|
+
const args = {};
|
|
150
|
+
if (options && options.sendInvites) {
|
|
151
|
+
args.sendUpdates = 'all';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const event = Calendar.Events.insert(resource, this.getId(), args);
|
|
155
|
+
return newFakeCalendarEvent(this.getId(), event);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
createAllDayEvent(title, startDate, endDateOrOptions, options) {
|
|
159
|
+
this.__checkWriteAccess();
|
|
160
|
+
|
|
161
|
+
let endDate = startDate;
|
|
162
|
+
let opts = options;
|
|
163
|
+
|
|
164
|
+
if (endDateOrOptions instanceof Date) {
|
|
165
|
+
endDate = endDateOrOptions;
|
|
166
|
+
} else if (typeof endDateOrOptions === 'object') {
|
|
167
|
+
opts = endDateOrOptions;
|
|
168
|
+
// Single day event, end date should be start date + 1 day for API v3 (exclusive)
|
|
169
|
+
const nextDay = new Date(startDate);
|
|
170
|
+
nextDay.setDate(nextDay.getDate() + 1);
|
|
171
|
+
endDate = nextDay;
|
|
172
|
+
} else {
|
|
173
|
+
// Just title and date
|
|
174
|
+
const nextDay = new Date(startDate);
|
|
175
|
+
nextDay.setDate(nextDay.getDate() + 1);
|
|
176
|
+
endDate = nextDay;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (startDate.getTime() >= endDate.getTime()) {
|
|
180
|
+
throw new Error('Event start date must be before event end date.');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const toDateString = (date) => date.toISOString().split('T')[0];
|
|
184
|
+
|
|
185
|
+
const resource = {
|
|
186
|
+
summary: title,
|
|
187
|
+
start: { date: toDateString(startDate) },
|
|
188
|
+
end: { date: toDateString(endDate) }
|
|
189
|
+
};
|
|
190
|
+
this.__applyEventOptions(resource, opts);
|
|
191
|
+
|
|
192
|
+
const args = {};
|
|
193
|
+
if (opts && opts.sendInvites) {
|
|
194
|
+
args.sendUpdates = 'all';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const event = Calendar.Events.insert(resource, this.getId(), args);
|
|
198
|
+
return newFakeCalendarEvent(this.getId(), event);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
createEventFromDescription(description) {
|
|
202
|
+
this.__checkWriteAccess();
|
|
203
|
+
const event = Calendar.Events.quickAdd(this.getId(), description);
|
|
204
|
+
return newFakeCalendarEvent(this.getId(), event);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getEvents(startTime, endTime, options) {
|
|
208
|
+
this.__checkReadAccess();
|
|
209
|
+
const args = {
|
|
210
|
+
timeMin: startTime.toISOString(),
|
|
211
|
+
timeMax: endTime.toISOString(),
|
|
212
|
+
singleEvents: true // Expand recurring events usually expected
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (options) {
|
|
216
|
+
if (options.start !== undefined) args.startIndex = options.start; // Note: Not in v3 standard list?
|
|
217
|
+
if (options.max !== undefined) args.maxResults = options.max;
|
|
218
|
+
if (options.search) args.q = options.search;
|
|
219
|
+
if (options.author) { /* Not directly supported in v3 list? */ }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const list = Calendar.Events.list(this.getId(), args);
|
|
223
|
+
return (list.items || []).map(item => newFakeCalendarEvent(this.getId(), item));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getEventsForDay(date, options) {
|
|
227
|
+
const startTime = new Date(date);
|
|
228
|
+
startTime.setHours(0, 0, 0, 0);
|
|
229
|
+
const endTime = new Date(date);
|
|
230
|
+
endTime.setHours(23, 59, 59, 999);
|
|
231
|
+
return this.getEvents(startTime, endTime, options);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
getEventById(iCalId) {
|
|
235
|
+
this.__checkReadAccess();
|
|
236
|
+
// Apps Script expects iCalUID. Try finding by iCalUID first using list.
|
|
237
|
+
// This avoids "Not Found" errors in the worker when passing iCalUID to events.get (which expects eventId)
|
|
238
|
+
const list = Calendar.Events.list(this.getId(), { iCalUID: iCalId });
|
|
239
|
+
if (list.items && list.items.length > 0) {
|
|
240
|
+
return newFakeCalendarEvent(this.getId(), list.items[0]);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Fallback: Try getting by eventId directly.
|
|
244
|
+
try {
|
|
245
|
+
const event = Calendar.Events.get(this.getId(), iCalId);
|
|
246
|
+
return newFakeCalendarEvent(this.getId(), event);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// --- Series ---
|
|
253
|
+
|
|
254
|
+
createEventSeries(title, startTime, endTime, recurrence, options) {
|
|
255
|
+
// TODO: Implement recurrence conversion to RRULE
|
|
256
|
+
// For now, simple insert without proper RRULE if Recurrence object not fully implemented
|
|
257
|
+
return this.createEvent(title, startTime, endTime, options); // Fallback
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
createAllDayEventSeries(title, startDate, recurrence, options) {
|
|
261
|
+
// TODO
|
|
262
|
+
return this.createAllDayEvent(title, startDate, options); // Fallback
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
getEventSeriesById(iCalId) {
|
|
266
|
+
// TODO
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// --- Internal ---
|
|
271
|
+
|
|
272
|
+
__applyEventOptions(resource, options) {
|
|
273
|
+
if (options) {
|
|
274
|
+
if (options.description) resource.description = options.description;
|
|
275
|
+
if (options.location) resource.location = options.location;
|
|
276
|
+
if (options.guests) resource.attendees = options.guests.split(',').map(e => ({ email: e.trim() }));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
__checkAccess(accessType) {
|
|
281
|
+
const behavior = ScriptApp.__behavior;
|
|
282
|
+
if (!behavior.sandboxMode) return true;
|
|
283
|
+
|
|
284
|
+
const calendarId = this.getId();
|
|
285
|
+
|
|
286
|
+
// Session-created calendars are always writable
|
|
287
|
+
if (behavior.isKnownCalendar(calendarId)) return true;
|
|
288
|
+
|
|
289
|
+
// Check whitelist
|
|
290
|
+
const settings = behavior.sandboxService.CalendarApp;
|
|
291
|
+
const whitelist = settings && settings.calendarWhitelist;
|
|
292
|
+
|
|
293
|
+
if (!whitelist) {
|
|
294
|
+
throw new Error(`Access to calendar ${calendarId} is denied. No calendar whitelist configured.`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const calendarName = this.getName();
|
|
298
|
+
// primary check
|
|
299
|
+
if (calendarId === 'primary') {
|
|
300
|
+
const entry = whitelist.find(item => item.name === 'Primary' || item.name === 'primary');
|
|
301
|
+
if (entry && entry[accessType]) return true;
|
|
302
|
+
// Default primary to accessible if not explicit? Or strictly follow whitelist?
|
|
303
|
+
// Existing code had "Primary calendar is always accessible" for *read* in CalendarApp.
|
|
304
|
+
// But here we check specific access.
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const entry = whitelist.find(item => item.name === calendarName);
|
|
308
|
+
if (entry && entry[accessType]) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
throw new Error(`${accessType} access to calendar "${calendarName}" (${calendarId}) is denied by sandbox rules`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
__checkDeleteAccess() {
|
|
316
|
+
return this.__checkAccess('write'); // Delete requires write usually? Or 'delete'?
|
|
317
|
+
}
|
|
318
|
+
__checkWriteAccess() {
|
|
319
|
+
return this.__checkAccess('write');
|
|
320
|
+
}
|
|
321
|
+
__checkReadAccess() {
|
|
322
|
+
return this.__checkAccess('read');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
toString() {
|
|
326
|
+
return 'Calendar';
|
|
327
|
+
}
|
|
328
|
+
}
|