@mcpher/gas-fakes 1.2.28 → 1.2.29
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 +2 -2
- package/src/services/calendarapp/fakecalendar.js +81 -11
- package/src/services/calendarapp/fakecalendarapp.js +133 -1
- package/src/services/calendarapp/fakecalendarevent.js +507 -1
- package/src/services/calendarapp/fakecalendareventseries.js +412 -0
- package/src/services/calendarapp/fakeeventguest.js +44 -0
- package/src/services/calendarapp/fakeeventrecurrence.js +38 -1
- package/src/services/calendarapp/fakerecurrencerule.js +48 -0
- package/src/services/enums/calendarenums.js +42 -37
- package/src/services/gmailapp/fakegmailapp.js +6 -10
- package/src/services/scriptapp/behavior.js +19 -25
- package/src/support/backoff.js +7 -1
- package/src/support/sxcalendar.js +15 -3
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeCalendarEventSeries } from './fakecalendareventseries.js';
|
|
3
|
+
import { newFakeEventGuest } from './fakeeventguest.js';
|
|
4
|
+
import * as CalendarEnums from '../enums/calendarenums.js';
|
|
2
5
|
|
|
3
6
|
export const newFakeCalendarEvent = (...args) => {
|
|
4
7
|
return Proxies.guard(new FakeCalendarEvent(...args));
|
|
@@ -30,15 +33,518 @@ export class FakeCalendarEvent {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
// --- Identity ---
|
|
37
|
+
|
|
33
38
|
getId() {
|
|
34
39
|
return this.__resource.iCalUID || this.__resource.id;
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
getOriginalCalendarId() {
|
|
43
|
+
return this.__calendarId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- Content ---
|
|
47
|
+
|
|
37
48
|
getTitle() {
|
|
38
49
|
return this.__resource.summary || '';
|
|
39
50
|
}
|
|
40
51
|
|
|
52
|
+
setTitle(title) {
|
|
53
|
+
this.__checkWriteAccess();
|
|
54
|
+
Calendar.Events.patch({ summary: title }, this.__calendarId, this.__id);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getDescription() {
|
|
59
|
+
return this.__resource.description || '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setDescription(description) {
|
|
63
|
+
this.__checkWriteAccess();
|
|
64
|
+
Calendar.Events.patch({ description }, this.__calendarId, this.__id);
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getLocation() {
|
|
69
|
+
return this.__resource.location || '';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setLocation(location) {
|
|
73
|
+
this.__checkWriteAccess();
|
|
74
|
+
Calendar.Events.patch({ location }, this.__calendarId, this.__id);
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// --- Time ---
|
|
79
|
+
|
|
80
|
+
getStartTime() {
|
|
81
|
+
const r = this.__resource;
|
|
82
|
+
if (r.start.dateTime) return new Date(r.start.dateTime);
|
|
83
|
+
if (r.start.date) return new Date(r.start.date);
|
|
84
|
+
return new Date();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getEndTime() {
|
|
88
|
+
const r = this.__resource;
|
|
89
|
+
if (r.end.dateTime) return new Date(r.end.dateTime);
|
|
90
|
+
if (r.end.date) return new Date(r.end.date);
|
|
91
|
+
return new Date();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
setTime(startTime, endTime) {
|
|
95
|
+
this.__checkWriteAccess();
|
|
96
|
+
Calendar.Events.patch({
|
|
97
|
+
start: { dateTime: startTime.toISOString() },
|
|
98
|
+
end: { dateTime: endTime.toISOString() }
|
|
99
|
+
}, this.__calendarId, this.__id);
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setAllDayDate(date) {
|
|
104
|
+
this.__checkWriteAccess();
|
|
105
|
+
const toDateString = (d) => d.toISOString().split('T')[0];
|
|
106
|
+
const nextDay = new Date(date);
|
|
107
|
+
nextDay.setDate(nextDay.getDate() + 1);
|
|
108
|
+
|
|
109
|
+
Calendar.Events.patch({
|
|
110
|
+
start: { date: toDateString(date) },
|
|
111
|
+
end: { date: toDateString(nextDay) }
|
|
112
|
+
}, this.__calendarId, this.__id);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
isAllDayEvent() {
|
|
117
|
+
const r = this.__resource;
|
|
118
|
+
return !!r.start.date;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getAllDayStartDate() {
|
|
122
|
+
const r = this.__resource;
|
|
123
|
+
if (r.start.date) return new Date(r.start.date);
|
|
124
|
+
return new Date(r.start.dateTime); // Fallback if not all-day
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getAllDayEndDate() {
|
|
128
|
+
const r = this.__resource;
|
|
129
|
+
if (r.end.date) return new Date(r.end.date);
|
|
130
|
+
return new Date(r.end.dateTime); // Fallback if not all-day
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// --- Recurring ---
|
|
134
|
+
|
|
135
|
+
isRecurringEvent() {
|
|
136
|
+
const r = this.__resource;
|
|
137
|
+
return !!r.recurringEventId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getEventSeries() {
|
|
141
|
+
const r = this.__resource;
|
|
142
|
+
const seriesId = r.recurringEventId || this.__id; // If not an instance, it might be the series master itself? Or single event
|
|
143
|
+
// If it's a single event, getEventSeries usually returns a series containing just this event (conceptually) or null?
|
|
144
|
+
// Docs say: "Gets the event series this event belongs to."
|
|
145
|
+
return newFakeCalendarEventSeries(this.__calendarId, { id: seriesId });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// --- Guests ---
|
|
149
|
+
|
|
150
|
+
addGuest(email) {
|
|
151
|
+
this.__checkWriteAccess();
|
|
152
|
+
this.__checkGuestWhitelist(email);
|
|
153
|
+
|
|
154
|
+
const resource = this.__resource;
|
|
155
|
+
const attendees = resource.attendees || [];
|
|
156
|
+
// Check if already there
|
|
157
|
+
if (!attendees.find(a => a.email === email)) {
|
|
158
|
+
attendees.push({ email });
|
|
159
|
+
Calendar.Events.patch({ attendees }, this.__calendarId, this.__id, { sendUpdates: 'all' });
|
|
160
|
+
}
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
removeGuest(email) {
|
|
165
|
+
this.__checkWriteAccess();
|
|
166
|
+
const resource = this.__resource;
|
|
167
|
+
const attendees = resource.attendees || [];
|
|
168
|
+
const newAttendees = attendees.filter(a => a.email !== email);
|
|
169
|
+
if (newAttendees.length !== attendees.length) {
|
|
170
|
+
Calendar.Events.patch({ attendees: newAttendees }, this.__calendarId, this.__id);
|
|
171
|
+
}
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getGuestList(includeOwner = false) {
|
|
176
|
+
const r = this.__resource;
|
|
177
|
+
if (!r.attendees) return [];
|
|
178
|
+
return r.attendees.map(a => newFakeEventGuest(a)).filter(g => includeOwner || g.getEmail() !== r.creator.email);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getGuestByEmail(email) {
|
|
182
|
+
const guests = this.getGuestList(true);
|
|
183
|
+
return guests.find(g => g.getEmail() === email) || null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getCreators() {
|
|
187
|
+
const r = this.__resource;
|
|
188
|
+
// In API v3, creator is a single object {email, displayName, self}
|
|
189
|
+
// Apps Script returns string[].
|
|
190
|
+
return r.creator ? [r.creator.email] : [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- Reminders ---
|
|
194
|
+
|
|
195
|
+
addEmailReminder(minutesBefore) {
|
|
196
|
+
this.__checkWriteAccess();
|
|
197
|
+
this.__addReminder('email', minutesBefore);
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
addPopupReminder(minutesBefore) {
|
|
202
|
+
this.__checkWriteAccess();
|
|
203
|
+
this.__addReminder('popup', minutesBefore);
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
addSmsReminder(minutesBefore) {
|
|
208
|
+
this.__checkWriteAccess();
|
|
209
|
+
this.__addReminder('sms', minutesBefore);
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
__addReminder(method, minutes) {
|
|
214
|
+
const r = this.__resource;
|
|
215
|
+
const reminders = r.reminders || { useDefault: true, overrides: [] };
|
|
216
|
+
if (reminders.useDefault) {
|
|
217
|
+
reminders.useDefault = false;
|
|
218
|
+
reminders.overrides = []; // Start fresh if we were using default
|
|
219
|
+
}
|
|
220
|
+
if (!reminders.overrides) reminders.overrides = [];
|
|
221
|
+
reminders.overrides.push({ method, minutes });
|
|
222
|
+
Calendar.Events.patch({ reminders }, this.__calendarId, this.__id);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
getReminders() {
|
|
226
|
+
const r = this.__resource;
|
|
227
|
+
if (r.reminders && r.reminders.useDefault) return []; // Or default reminders? API implies explicit ones.
|
|
228
|
+
return (r.reminders && r.reminders.overrides) ? r.reminders.overrides.map(o => o.minutes) : [];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getEmailReminders() {
|
|
232
|
+
return this.__getRemindersByMethod('email');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
getPopupReminders() {
|
|
236
|
+
return this.__getRemindersByMethod('popup');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
getSmsReminders() {
|
|
240
|
+
return this.__getRemindersByMethod('sms');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
__getRemindersByMethod(method) {
|
|
244
|
+
const r = this.__resource;
|
|
245
|
+
if (r.reminders && r.reminders.useDefault) return [];
|
|
246
|
+
if (!r.reminders || !r.reminders.overrides) return [];
|
|
247
|
+
return r.reminders.overrides.filter(o => o.method === method).map(o => o.minutes);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
removeAllReminders() {
|
|
251
|
+
this.__checkWriteAccess();
|
|
252
|
+
Calendar.Events.patch({ reminders: { useDefault: false, overrides: [] } }, this.__calendarId, this.__id);
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
resetRemindersToDefault() {
|
|
257
|
+
this.__checkWriteAccess();
|
|
258
|
+
Calendar.Events.patch({ reminders: { useDefault: true } }, this.__calendarId, this.__id);
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// --- Tags (Extended Properties) ---
|
|
263
|
+
|
|
264
|
+
setTag(key, value) {
|
|
265
|
+
this.__checkWriteAccess();
|
|
266
|
+
const r = this.__resource;
|
|
267
|
+
const extendedProperties = r.extendedProperties || { shared: {} };
|
|
268
|
+
if (!extendedProperties.shared) extendedProperties.shared = {};
|
|
269
|
+
extendedProperties.shared[key] = value;
|
|
270
|
+
Calendar.Events.patch({ extendedProperties }, this.__calendarId, this.__id);
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getTag(key) {
|
|
275
|
+
const r = this.__resource;
|
|
276
|
+
return (r.extendedProperties && r.extendedProperties.shared && r.extendedProperties.shared[key]) || null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getAllTagKeys() {
|
|
280
|
+
const r = this.__resource;
|
|
281
|
+
return (r.extendedProperties && r.extendedProperties.shared) ? Object.keys(r.extendedProperties.shared) : [];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
deleteTag(key) {
|
|
285
|
+
this.__checkWriteAccess();
|
|
286
|
+
const r = this.__resource;
|
|
287
|
+
if (r.extendedProperties && r.extendedProperties.shared && r.extendedProperties.shared[key]) {
|
|
288
|
+
delete r.extendedProperties.shared[key];
|
|
289
|
+
Calendar.Events.patch({ extendedProperties: r.extendedProperties }, this.__calendarId, this.__id);
|
|
290
|
+
}
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// --- Metadata & Permissions ---
|
|
295
|
+
|
|
296
|
+
getDateCreated() {
|
|
297
|
+
return new Date(this.__resource.created);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
getLastUpdated() {
|
|
301
|
+
return new Date(this.__resource.updated);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
isOwnedByMe() {
|
|
305
|
+
const r = this.__resource;
|
|
306
|
+
// Assuming 'me' logic or checking against session user
|
|
307
|
+
const currentUserEmail = Session.getEffectiveUser().getEmail();
|
|
308
|
+
return r.creator && r.creator.email === currentUserEmail;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getVisibility() {
|
|
312
|
+
const r = this.__resource;
|
|
313
|
+
// Map 'private', 'public', 'confidential', 'default'
|
|
314
|
+
const v = r.visibility || 'default';
|
|
315
|
+
return CalendarEnums.Visibility[v.toUpperCase()] || CalendarEnums.Visibility.DEFAULT;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
setVisibility(visibility) {
|
|
319
|
+
this.__checkWriteAccess();
|
|
320
|
+
const v = visibility.toString().toLowerCase();
|
|
321
|
+
Calendar.Events.patch({ visibility: v }, this.__calendarId, this.__id);
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
getColor() {
|
|
326
|
+
// Returns the event color ID as a string, or an empty string if no color is set.
|
|
327
|
+
return this.__resource.colorId || '';
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
setColor(color) {
|
|
331
|
+
this.__checkWriteAccess();
|
|
332
|
+
// color should be an enum value or a colorId string
|
|
333
|
+
const colorId = color.toString();
|
|
334
|
+
Calendar.Events.patch({ colorId }, this.__calendarId, this.__id);
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
getEventType() {
|
|
339
|
+
const r = this.__resource;
|
|
340
|
+
const typeMap = {
|
|
341
|
+
'default': CalendarEnums.EventType.DEFAULT,
|
|
342
|
+
'outOfOffice': CalendarEnums.EventType.OUT_OF_OFFICE,
|
|
343
|
+
'focusTime': CalendarEnums.EventType.FOCUS_TIME,
|
|
344
|
+
'workingLocation': CalendarEnums.EventType.WORKING_LOCATION
|
|
345
|
+
};
|
|
346
|
+
return typeMap[r.eventType] || CalendarEnums.EventType.DEFAULT;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
getMyStatus() {
|
|
350
|
+
const r = this.__resource;
|
|
351
|
+
const me = Session.getEffectiveUser().getEmail();
|
|
352
|
+
const attendee = (r.attendees || []).find(a => a.email === me);
|
|
353
|
+
if (!attendee) return CalendarEnums.GuestStatus.NO;
|
|
354
|
+
|
|
355
|
+
const map = {
|
|
356
|
+
'accepted': CalendarEnums.GuestStatus.YES,
|
|
357
|
+
'declined': CalendarEnums.GuestStatus.NO,
|
|
358
|
+
'tentative': CalendarEnums.GuestStatus.MAYBE,
|
|
359
|
+
'needsAction': CalendarEnums.GuestStatus.INVITED
|
|
360
|
+
};
|
|
361
|
+
return map[attendee.responseStatus] || CalendarEnums.GuestStatus.INVITED;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
setMyStatus(status) {
|
|
365
|
+
this.__checkWriteAccess();
|
|
366
|
+
const me = Session.getEffectiveUser().getEmail();
|
|
367
|
+
const r = this.__resource;
|
|
368
|
+
const attendees = r.attendees || [];
|
|
369
|
+
let attendee = attendees.find(a => a.email === me);
|
|
370
|
+
|
|
371
|
+
const map = {};
|
|
372
|
+
map[CalendarEnums.GuestStatus.YES] = 'accepted';
|
|
373
|
+
map[CalendarEnums.GuestStatus.NO] = 'declined';
|
|
374
|
+
map[CalendarEnums.GuestStatus.MAYBE] = 'tentative';
|
|
375
|
+
map[CalendarEnums.GuestStatus.INVITED] = 'needsAction';
|
|
376
|
+
|
|
377
|
+
const apiStatus = map[status];
|
|
378
|
+
if (!apiStatus) return this;
|
|
379
|
+
|
|
380
|
+
if (!attendee) {
|
|
381
|
+
attendee = { email: me, responseStatus: apiStatus };
|
|
382
|
+
attendees.push(attendee);
|
|
383
|
+
} else {
|
|
384
|
+
attendee.responseStatus = apiStatus;
|
|
385
|
+
}
|
|
386
|
+
Calendar.Events.patch({ attendees }, this.__calendarId, this.__id);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
getTransparency() {
|
|
391
|
+
const r = this.__resource;
|
|
392
|
+
const t = r.transparency || 'opaque';
|
|
393
|
+
return t === 'transparent' ? CalendarEnums.EventTransparency.TRANSPARENT : CalendarEnums.EventTransparency.OPAQUE;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
setTransparency(transparency) {
|
|
397
|
+
this.__checkWriteAccess();
|
|
398
|
+
const t = transparency === CalendarEnums.EventTransparency.TRANSPARENT ? 'transparent' : 'opaque';
|
|
399
|
+
Calendar.Events.patch({ transparency: t }, this.__calendarId, this.__id);
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
setAllDayDates(startDate, endDate) {
|
|
404
|
+
this.__checkWriteAccess();
|
|
405
|
+
const toDateString = (d) => d.toISOString().split('T')[0];
|
|
406
|
+
// Apps Script setAllDayDates uses exclusive end date logic same as createAllDayEvent
|
|
407
|
+
Calendar.Events.patch({
|
|
408
|
+
start: { date: toDateString(startDate) },
|
|
409
|
+
end: { date: toDateString(endDate) }
|
|
410
|
+
}, this.__calendarId, this.__id);
|
|
411
|
+
return this;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
guestsCanInviteOthers() {
|
|
415
|
+
return !!this.__resource.guestsCanInviteOthers;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
setGuestsCanInviteOthers(guestsCanInviteOthers) {
|
|
419
|
+
this.__checkWriteAccess();
|
|
420
|
+
Calendar.Events.patch({ guestsCanInviteOthers }, this.__calendarId, this.__id);
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
guestsCanModify() {
|
|
425
|
+
return !!this.__resource.guestsCanModify;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
setGuestsCanModify(guestsCanModify) {
|
|
429
|
+
this.__checkWriteAccess();
|
|
430
|
+
Calendar.Events.patch({ guestsCanModify }, this.__calendarId, this.__id);
|
|
431
|
+
return this;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
guestsCanSeeGuests() {
|
|
435
|
+
return !!this.__resource.guestsCanSeeGuests;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
setGuestsCanSeeGuests(guestsCanSeeGuests) {
|
|
439
|
+
this.__checkWriteAccess();
|
|
440
|
+
Calendar.Events.patch({ guestsCanSeeGuests }, this.__calendarId, this.__id);
|
|
441
|
+
return this;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
anyoneCanAddSelf() {
|
|
445
|
+
return !!this.__resource.anyoneCanAddSelf;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
setAnyoneCanAddSelf(anyoneCanAddSelf) {
|
|
449
|
+
this.__checkWriteAccess();
|
|
450
|
+
Calendar.Events.patch({ anyoneCanAddSelf }, this.__calendarId, this.__id);
|
|
451
|
+
return this;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// --- Deletion ---
|
|
455
|
+
|
|
456
|
+
deleteEvent() {
|
|
457
|
+
this.__checkWriteAccess();
|
|
458
|
+
Calendar.Events.delete(this.__calendarId, this.__id);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// --- Helpers ---
|
|
462
|
+
|
|
463
|
+
__checkWriteAccess() {
|
|
464
|
+
this.__checkUsage('write');
|
|
465
|
+
this.__checkPermission('write');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
__checkUsage(type) {
|
|
469
|
+
const serviceName = 'CalendarApp';
|
|
470
|
+
const behavior = ScriptApp.__behavior;
|
|
471
|
+
if (behavior && behavior.sandboxMode) {
|
|
472
|
+
const settings = behavior.sandboxService[serviceName];
|
|
473
|
+
let limit = settings && settings.usageLimit;
|
|
474
|
+
if (limit) {
|
|
475
|
+
if (typeof limit === 'number') {
|
|
476
|
+
const total = (settings.usageCount.read || 0) + (settings.usageCount.write || 0) + (settings.usageCount.trash || 0);
|
|
477
|
+
if (total >= limit) {
|
|
478
|
+
throw new Error(`Calendar total usage limit of ${limit} exceeded`);
|
|
479
|
+
}
|
|
480
|
+
settings.incrementUsage(type);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
let specificLimit = limit[type];
|
|
485
|
+
if (specificLimit !== undefined) {
|
|
486
|
+
const current = settings.usageCount[type] || 0;
|
|
487
|
+
if (current >= specificLimit) {
|
|
488
|
+
throw new Error(`Calendar ${type} usage limit of ${specificLimit} exceeded`);
|
|
489
|
+
}
|
|
490
|
+
settings.incrementUsage(type);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
__checkPermission(accessType) {
|
|
497
|
+
const behavior = ScriptApp.__behavior;
|
|
498
|
+
if (!behavior || !behavior.sandboxMode) return;
|
|
499
|
+
|
|
500
|
+
const calendarId = this.__calendarId;
|
|
501
|
+
|
|
502
|
+
// Session-created calendars are always writable
|
|
503
|
+
if (behavior.isKnownCalendar(calendarId)) return;
|
|
504
|
+
|
|
505
|
+
// Check whitelist
|
|
506
|
+
const settings = behavior.sandboxService.CalendarApp;
|
|
507
|
+
const whitelist = settings && settings.calendarWhitelist;
|
|
508
|
+
|
|
509
|
+
if (!whitelist) {
|
|
510
|
+
throw new Error(`Access to calendar ${calendarId} is denied. No calendar whitelist configured.`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Get calendar name
|
|
514
|
+
let calendarName = '';
|
|
515
|
+
try {
|
|
516
|
+
const cal = Calendar.Calendars.get(calendarId);
|
|
517
|
+
calendarName = cal.summary;
|
|
518
|
+
} catch (e) { }
|
|
519
|
+
|
|
520
|
+
// Primary check
|
|
521
|
+
if (calendarId === 'primary') {
|
|
522
|
+
const entry = whitelist.find(item => item.name === 'Primary' || item.name === 'primary');
|
|
523
|
+
if (entry && entry[accessType]) return;
|
|
524
|
+
// If primary is not explicitly in whitelist, we deny write access by default in sandbox mode
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const entry = whitelist.find(item => item.name === calendarName);
|
|
528
|
+
if (entry && entry[accessType]) return;
|
|
529
|
+
|
|
530
|
+
throw new Error(`${accessType} access to calendar "${calendarName}" (${calendarId}) is denied by sandbox rules`);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
__checkGuestWhitelist(email) {
|
|
534
|
+
const behavior = ScriptApp.__behavior;
|
|
535
|
+
if (!behavior || !behavior.sandboxMode) return;
|
|
536
|
+
|
|
537
|
+
const gmailSettings = behavior.sandboxService.GmailApp;
|
|
538
|
+
const whitelist = gmailSettings && gmailSettings.emailWhitelist;
|
|
539
|
+
|
|
540
|
+
if (whitelist) {
|
|
541
|
+
if (!whitelist.includes(email)) {
|
|
542
|
+
throw new Error(`Adding guest ${email} is denied by Gmail sandbox whitelist rules`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
41
547
|
toString() {
|
|
42
548
|
return 'CalendarEvent';
|
|
43
549
|
}
|
|
44
|
-
}
|
|
550
|
+
}
|