@pipedream/google_calendar 0.3.3 → 0.3.4
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/actions/create-event/create-event.mjs +128 -0
- package/actions/delete-event/delete-event.mjs +37 -0
- package/actions/get-calendar/get-calendar.mjs +27 -0
- package/actions/get-event/get-event.mjs +37 -0
- package/actions/list-calendars/list-calendars.mjs +19 -0
- package/actions/query-free-busy-calendars/query-free-busy-calendars.mjs +34 -0
- package/actions/quick-add-event/quick-add-event.mjs +33 -0
- package/actions/update-event-attendees/update-event-attendees.mjs +74 -0
- package/common/constants.mjs +83 -0
- package/google_calendar.app.mjs +456 -0
- package/package.json +22 -20
- package/sources/common.mjs +74 -0
- package/sources/event-cancelled/event-cancelled.mjs +29 -0
- package/sources/event-ended/event-ended.mjs +35 -0
- package/sources/event-start/event-start.mjs +35 -0
- package/sources/new-calendar/new-calendar.mjs +68 -0
- package/sources/new-event/new-event.mjs +27 -0
- package/sources/new-event-search/new-event-search.mjs +38 -0
- package/sources/new-or-updated-event/new-or-updated-event.mjs +39 -0
- package/sources/new-or-updated-event-instant/new-or-updated-event-instant.mjs +289 -0
- package/google_calendar.app.js +0 -154
- package/sources/event-cancelled/event-cancelled.js +0 -65
- package/sources/event-ended/event-ended.js +0 -68
- package/sources/event-search/new-event-search.js +0 -70
- package/sources/event-start/event-start.js +0 -68
- package/sources/new-calendar/new-calendar.js +0 -50
- package/sources/new-event/new-event.js +0 -65
- package/sources/new-or-updated-event/new-or-updated-event.js +0 -64
- package/sources/new-or-updated-event-instant/new-or-updated-event-instant.js +0 -200
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import googleCalendar from "../../google_calendar.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "google_calendar-new-calendar",
|
|
5
|
+
name: "New Calendar",
|
|
6
|
+
description: "Emit an event when a calendar is created.",
|
|
7
|
+
version: "0.1.1",
|
|
8
|
+
type: "source",
|
|
9
|
+
props: {
|
|
10
|
+
db: "$.service.db",
|
|
11
|
+
googleCalendar,
|
|
12
|
+
timer: {
|
|
13
|
+
type: "$.interface.timer",
|
|
14
|
+
default: {
|
|
15
|
+
intervalSeconds: 15 * 60,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
hooks: {
|
|
20
|
+
async activate() {
|
|
21
|
+
// get list of calendars
|
|
22
|
+
const { items: calendars = [] } = await this.googleCalendar.listCalendars();
|
|
23
|
+
this.emitNewCalendars(calendars);
|
|
24
|
+
const calendarIds = calendars.map((item) => item.id);
|
|
25
|
+
this.setCalendarIds(calendarIds);
|
|
26
|
+
},
|
|
27
|
+
deactivate() {
|
|
28
|
+
this.setCalendarIds([]);
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
methods: {
|
|
32
|
+
setCalendarIds(calendarIds) {
|
|
33
|
+
this.db.set("calendarIds", calendarIds);
|
|
34
|
+
},
|
|
35
|
+
getCalendarIds() {
|
|
36
|
+
return this.db.get("calendarIds") || [];
|
|
37
|
+
},
|
|
38
|
+
generateMeta(calendar) {
|
|
39
|
+
const {
|
|
40
|
+
summary,
|
|
41
|
+
id,
|
|
42
|
+
} = calendar;
|
|
43
|
+
return {
|
|
44
|
+
summary,
|
|
45
|
+
id,
|
|
46
|
+
ts: Date.now(),
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
emitNewCalendars(calendars) {
|
|
50
|
+
const previousCalendarIds = this.getCalendarIds();
|
|
51
|
+
|
|
52
|
+
for (const calendar of calendars) {
|
|
53
|
+
if (!previousCalendarIds.includes(calendar.id)) {
|
|
54
|
+
const meta = this.generateMeta(calendar);
|
|
55
|
+
this.$emit(calendar, meta);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
async run() {
|
|
61
|
+
const { items: calendars = [] } = await this.googleCalendar.listCalendars();
|
|
62
|
+
|
|
63
|
+
this.emitNewCalendars(calendars);
|
|
64
|
+
|
|
65
|
+
const calendarIds = calendars.map((item) => item.id);
|
|
66
|
+
this.setCalendarIds(calendarIds);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import common from "../common.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
key: "google_calendar-new-event",
|
|
6
|
+
name: "New Event",
|
|
7
|
+
description: "Emits when an event is created",
|
|
8
|
+
version: "0.1.1",
|
|
9
|
+
type: "source",
|
|
10
|
+
dedupe: "unique", // Dedupe events based on the Google Calendar event ID
|
|
11
|
+
methods: {
|
|
12
|
+
...common.methods,
|
|
13
|
+
getConfig({ past }) {
|
|
14
|
+
const updatedMin = past.toISOString();
|
|
15
|
+
return {
|
|
16
|
+
calendarId: this.calendarId,
|
|
17
|
+
updatedMin,
|
|
18
|
+
singleEvents: true,
|
|
19
|
+
orderBy: "startTime",
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
isRelevant(event, { past }) {
|
|
23
|
+
const created = new Date(event.created);
|
|
24
|
+
return created > past && event.status !== "cancelled";
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import common from "../common.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
key: "google_calendar-new-event-search",
|
|
6
|
+
name: "Event Search",
|
|
7
|
+
description: "Emit when an event is created that matches a search",
|
|
8
|
+
version: "0.1.1",
|
|
9
|
+
type: "source",
|
|
10
|
+
dedupe: "unique", // Dedupe events based on the Google Calendar event ID
|
|
11
|
+
props: {
|
|
12
|
+
...common.props,
|
|
13
|
+
q: {
|
|
14
|
+
propDefinition: [
|
|
15
|
+
common.props.googleCalendar,
|
|
16
|
+
"q",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
methods: {
|
|
21
|
+
...common.methods,
|
|
22
|
+
getConfig({ past }) {
|
|
23
|
+
const updatedMin = past.toISOString();
|
|
24
|
+
return {
|
|
25
|
+
calendarId: this.calendarId,
|
|
26
|
+
updatedMin,
|
|
27
|
+
q: this.q,
|
|
28
|
+
singleEvents: true,
|
|
29
|
+
orderBy: "startTime",
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
isRelevant(event, { past }) {
|
|
33
|
+
const created = new Date(event.created);
|
|
34
|
+
// created in last 5 mins and not cancelled
|
|
35
|
+
return created > past && event.status !== "cancelled";
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import common from "../common.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
key: "google_calendar-new-or-updated-event",
|
|
6
|
+
name: "New or Updated Event",
|
|
7
|
+
description: "Emits when an event is created or updated (except when it's cancelled)",
|
|
8
|
+
version: "0.1.1",
|
|
9
|
+
type: "source",
|
|
10
|
+
dedupe: "unique", // Dedupe events based on the Google Calendar event ID
|
|
11
|
+
methods: {
|
|
12
|
+
...common.methods,
|
|
13
|
+
getConfig({ past }) {
|
|
14
|
+
const updatedMin = past.toISOString();
|
|
15
|
+
return {
|
|
16
|
+
calendarId: this.calendarId,
|
|
17
|
+
updatedMin,
|
|
18
|
+
singleEvents: true,
|
|
19
|
+
orderBy: "startTime",
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
isRelevant(event) {
|
|
23
|
+
return event.status !== "cancelled";
|
|
24
|
+
},
|
|
25
|
+
generateMeta(event) {
|
|
26
|
+
const {
|
|
27
|
+
id,
|
|
28
|
+
summary,
|
|
29
|
+
updated: tsString,
|
|
30
|
+
} = event;
|
|
31
|
+
const ts = Date.parse(tsString);
|
|
32
|
+
return {
|
|
33
|
+
id: `${id}-${ts}`,
|
|
34
|
+
summary,
|
|
35
|
+
ts,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
import googleCalendar from "../../google_calendar.app.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
key: "google_calendar-new-or-updated-event-instant",
|
|
6
|
+
type: "source",
|
|
7
|
+
name: "New or Updated Event (Instant)",
|
|
8
|
+
description: "Emit new calendar events when an event is created or updated (does not emit cancelled events)",
|
|
9
|
+
version: "0.1.4",
|
|
10
|
+
dedupe: "unique",
|
|
11
|
+
props: {
|
|
12
|
+
googleCalendar,
|
|
13
|
+
db: "$.service.db",
|
|
14
|
+
calendarIds: {
|
|
15
|
+
propDefinition: [
|
|
16
|
+
googleCalendar,
|
|
17
|
+
"calendarId",
|
|
18
|
+
],
|
|
19
|
+
type: "string[]",
|
|
20
|
+
label: "Calendars",
|
|
21
|
+
description: "Select one or more calendars to watch",
|
|
22
|
+
},
|
|
23
|
+
newOnly: {
|
|
24
|
+
label: "New events only?",
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "Emit new events only, and not updates to existing events",
|
|
27
|
+
optional: true,
|
|
28
|
+
default: false,
|
|
29
|
+
},
|
|
30
|
+
http: "$.interface.http",
|
|
31
|
+
timer: {
|
|
32
|
+
label: "Push notification renewal schedule",
|
|
33
|
+
description: "The Google Calendar API requires occasional renewal of push notification subscriptions. **This runs in the background, so you should not need to modify this schedule**.",
|
|
34
|
+
type: "$.interface.timer",
|
|
35
|
+
static: {
|
|
36
|
+
intervalSeconds: 60 * 60 * 23,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
hooks: {
|
|
41
|
+
async deploy() {
|
|
42
|
+
const events = [];
|
|
43
|
+
const params = {
|
|
44
|
+
maxResults: 25,
|
|
45
|
+
orderBy: "updated",
|
|
46
|
+
};
|
|
47
|
+
for (const calendarId of this.calendarIds) {
|
|
48
|
+
params.calendarId = calendarId;
|
|
49
|
+
const { items } = await this.googleCalendar.listEvents(params);
|
|
50
|
+
events.push(...items);
|
|
51
|
+
}
|
|
52
|
+
events.sort((a, b) => (Date.parse(a.updated) > Date.parse(b.updated))
|
|
53
|
+
? 1
|
|
54
|
+
: -1);
|
|
55
|
+
for (const event of events.slice(-25)) {
|
|
56
|
+
const meta = this.generateMeta(event);
|
|
57
|
+
this.$emit(event, meta);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
async activate() {
|
|
61
|
+
await this.makeWatchRequest();
|
|
62
|
+
},
|
|
63
|
+
async deactivate() {
|
|
64
|
+
await this.stopWatchRequest();
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
methods: {
|
|
68
|
+
setNextSyncToken(calendarId, nextSyncToken) {
|
|
69
|
+
this.db.set(`${calendarId}.nextSyncToken`, nextSyncToken);
|
|
70
|
+
},
|
|
71
|
+
getNextSyncToken(calendarId) {
|
|
72
|
+
return this.db.get(`${calendarId}.nextSyncToken`);
|
|
73
|
+
},
|
|
74
|
+
setChannelId(calendarId, channelId) {
|
|
75
|
+
this.db.set(`${calendarId}.channelId`, channelId);
|
|
76
|
+
},
|
|
77
|
+
getChannelId(calendarId) {
|
|
78
|
+
return this.db.get(`${calendarId}.channelId`);
|
|
79
|
+
},
|
|
80
|
+
setResourceId(calendarId, resourceId) {
|
|
81
|
+
this.db.set(`${calendarId}.resourceId`, resourceId);
|
|
82
|
+
},
|
|
83
|
+
getResourceId(calendarId) {
|
|
84
|
+
return this.db.get(`${calendarId}.resourceId`);
|
|
85
|
+
},
|
|
86
|
+
setExpiration(calendarId, expiration) {
|
|
87
|
+
this.db.set(`${calendarId}.expiration`, expiration);
|
|
88
|
+
},
|
|
89
|
+
getExpiration(calendarId) {
|
|
90
|
+
return this.db.get(`${calendarId}.expiration`);
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* A utility method to compute whether the provided event is newly created
|
|
94
|
+
* or not. Since the Google Calendar API does not provide a specific way to
|
|
95
|
+
* determine this, this method estimates the result based on the `created`
|
|
96
|
+
* and `updated` timestamps: if they are more than 2 seconds apart, then we
|
|
97
|
+
* assume that the event is not new.
|
|
98
|
+
*
|
|
99
|
+
* @param {Object} event - The calendar event being processed
|
|
100
|
+
* @returns {Boolean} True if the input event is a newly created event, or
|
|
101
|
+
* false otherwise
|
|
102
|
+
*/
|
|
103
|
+
_isNewEvent(event) {
|
|
104
|
+
const {
|
|
105
|
+
created,
|
|
106
|
+
updated,
|
|
107
|
+
} = event;
|
|
108
|
+
const createdTimestampMilliseconds = Date.parse(created);
|
|
109
|
+
const updatedTimestampMilliseconds = Date.parse(updated);
|
|
110
|
+
const diffMilliseconds = Math.abs(
|
|
111
|
+
updatedTimestampMilliseconds - createdTimestampMilliseconds,
|
|
112
|
+
);
|
|
113
|
+
const maxDiffMilliseconds = 2000;
|
|
114
|
+
return diffMilliseconds <= maxDiffMilliseconds;
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* A utility method to compute whether the provided event is relevant to the
|
|
118
|
+
* event source (and as a consequence must be processed) or not.
|
|
119
|
+
*
|
|
120
|
+
* @param {Object} event - The calendar event being processed
|
|
121
|
+
* @returns {Boolean} True if the input event must be processed, or false
|
|
122
|
+
* otherwise (i.e. if the event must be skipped)
|
|
123
|
+
*/
|
|
124
|
+
isEventRelevant(event) {
|
|
125
|
+
return !this.newOnly || this._isNewEvent(event);
|
|
126
|
+
},
|
|
127
|
+
generateMeta(event) {
|
|
128
|
+
const {
|
|
129
|
+
id,
|
|
130
|
+
summary,
|
|
131
|
+
updated: tsString,
|
|
132
|
+
} = event;
|
|
133
|
+
const ts = Date.parse(tsString);
|
|
134
|
+
return {
|
|
135
|
+
id: `${id}-${ts}`,
|
|
136
|
+
summary,
|
|
137
|
+
ts,
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
async makeWatchRequest() {
|
|
141
|
+
// Make watch request for this HTTP endpoint
|
|
142
|
+
for (const calendarId of this.calendarIds) {
|
|
143
|
+
const watchResp =
|
|
144
|
+
await this.googleCalendar.watchEvents({
|
|
145
|
+
calendarId,
|
|
146
|
+
requestBody: {
|
|
147
|
+
id: uuid(),
|
|
148
|
+
type: "web_hook",
|
|
149
|
+
address: this.http.endpoint,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Initial full sync. Get next sync token
|
|
154
|
+
const nextSyncToken = await this.googleCalendar.fullSync(calendarId);
|
|
155
|
+
|
|
156
|
+
this.setNextSyncToken(calendarId, nextSyncToken);
|
|
157
|
+
this.setChannelId(calendarId, watchResp.id);
|
|
158
|
+
this.setResourceId(calendarId, watchResp.resourceId);
|
|
159
|
+
this.setExpiration(calendarId, watchResp.expiration);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async stopWatchRequest() {
|
|
163
|
+
for (const calendarId of this.calendarIds) {
|
|
164
|
+
const id = this.getChannelId(calendarId);
|
|
165
|
+
const resourceId = this.getResourceId(calendarId);
|
|
166
|
+
if (id && resourceId) {
|
|
167
|
+
const { status } =
|
|
168
|
+
await this.googleCalendar.stopChannel({
|
|
169
|
+
returnOnlyData: false,
|
|
170
|
+
requestBody: {
|
|
171
|
+
id,
|
|
172
|
+
resourceId,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
if (status === 204) {
|
|
176
|
+
console.log("webhook deactivated");
|
|
177
|
+
this.setNextSyncToken(calendarId, null);
|
|
178
|
+
this.setChannelId(calendarId, null);
|
|
179
|
+
this.setResourceId(calendarId, null);
|
|
180
|
+
this.setExpiration(calendarId, null);
|
|
181
|
+
} else {
|
|
182
|
+
console.log("There was a problem deactivating the webhook");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
getSoonestExpirationDate() {
|
|
188
|
+
let min;
|
|
189
|
+
for (const calendarId of this.calendarIds) {
|
|
190
|
+
const expiration = parseInt(this.db.get(`${calendarId}.expiration`));
|
|
191
|
+
if (!min || expiration < min) {
|
|
192
|
+
min = expiration;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return new Date(min);
|
|
196
|
+
},
|
|
197
|
+
getChannelIds() {
|
|
198
|
+
const channelIds = [];
|
|
199
|
+
for (const calendarId of this.calendarIds) {
|
|
200
|
+
const channelId = this.db.get(`${calendarId}.channelId`);
|
|
201
|
+
channelIds.push(channelId);
|
|
202
|
+
}
|
|
203
|
+
return channelIds;
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
async run(event) {
|
|
207
|
+
// refresh watch
|
|
208
|
+
if (event.interval_seconds) {
|
|
209
|
+
// get time
|
|
210
|
+
const now = new Date();
|
|
211
|
+
const intervalMs = event.interval_seconds * 1000;
|
|
212
|
+
// get expiration
|
|
213
|
+
const expireDate = this.getSoonestExpirationDate();
|
|
214
|
+
|
|
215
|
+
// if now + interval > expiration, refresh watch
|
|
216
|
+
if (now.getTime() + intervalMs > expireDate.getTime()) {
|
|
217
|
+
await this.stopWatchRequest();
|
|
218
|
+
await this.makeWatchRequest();
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
// Verify channel ID
|
|
222
|
+
const channelIds = this.getChannelIds();
|
|
223
|
+
const incomingChannelId = event?.headers?.["x-goog-channel-id"];
|
|
224
|
+
if (!channelIds.includes(incomingChannelId)) {
|
|
225
|
+
console.log(
|
|
226
|
+
`Unexpected channel ID ${incomingChannelId}. This likely means there are multiple, older subscriptions active.`,
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check that resource state === exists
|
|
232
|
+
const state = event?.headers?.["x-goog-resource-state"];
|
|
233
|
+
switch (state) {
|
|
234
|
+
case "exists":
|
|
235
|
+
// there's something to emit, so keep going
|
|
236
|
+
break;
|
|
237
|
+
case "not_exists":
|
|
238
|
+
console.log("Resource does not exist. Exiting.");
|
|
239
|
+
return;
|
|
240
|
+
case "sync":
|
|
241
|
+
console.log("New channel created");
|
|
242
|
+
return;
|
|
243
|
+
default:
|
|
244
|
+
console.log(`Unknown state: ${state}`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Fetch and emit events
|
|
250
|
+
for (const calendarId of this.calendarIds) {
|
|
251
|
+
const syncToken = this.getNextSyncToken(calendarId);
|
|
252
|
+
let nextSyncToken = null;
|
|
253
|
+
let nextPageToken = null;
|
|
254
|
+
while (!nextSyncToken) {
|
|
255
|
+
const {
|
|
256
|
+
data: syncData = {},
|
|
257
|
+
status: syncStatus,
|
|
258
|
+
} = await this.googleCalendar.listEvents({
|
|
259
|
+
returnOnlyData: false,
|
|
260
|
+
calendarId,
|
|
261
|
+
syncToken,
|
|
262
|
+
pageToken: nextPageToken,
|
|
263
|
+
});
|
|
264
|
+
if (syncStatus === 410) {
|
|
265
|
+
console.log("Sync token invalid, resyncing");
|
|
266
|
+
nextSyncToken = await this.googleCalendar.fullSync(this.calendarId);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
nextPageToken = syncData.nextPageToken;
|
|
270
|
+
nextSyncToken = syncData.nextSyncToken;
|
|
271
|
+
|
|
272
|
+
const { items: events = [] } = syncData;
|
|
273
|
+
events
|
|
274
|
+
.filter(this.isEventRelevant, this)
|
|
275
|
+
.forEach((event) => {
|
|
276
|
+
const { status } = event;
|
|
277
|
+
if (status === "cancelled") {
|
|
278
|
+
console.log("Event cancelled. Exiting.");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const meta = this.generateMeta(event);
|
|
282
|
+
this.$emit(event, meta);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.setNextSyncToken(calendarId, nextSyncToken);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
};
|
package/google_calendar.app.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
const { google } = require("googleapis")
|
|
2
|
-
const get = require("lodash.get")
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
type: "app",
|
|
6
|
-
app: "google_calendar",
|
|
7
|
-
propDefinitions: {
|
|
8
|
-
calendarId: {
|
|
9
|
-
description: "Calendar identifier. To retrieve calendar IDs call the calendarList.list method. If you want to access the primary calendar of the currently logged in user, use the \"primary\" keyword.",
|
|
10
|
-
type: "string"
|
|
11
|
-
},
|
|
12
|
-
iCalUID : {
|
|
13
|
-
description: "Specifies event ID in the iCalendar format to be included in the response. Optional.",
|
|
14
|
-
optional: true,
|
|
15
|
-
type: "string"
|
|
16
|
-
},
|
|
17
|
-
maxAttendees: {
|
|
18
|
-
description: "The maximum number of attendees to include in the response. If there are more than the specified number of attendees, only the participant is returned. Optional.",
|
|
19
|
-
optional: true,
|
|
20
|
-
type: "integer"
|
|
21
|
-
},
|
|
22
|
-
maxResults: {
|
|
23
|
-
description: "Maximum number of events returned on one result page. The number of events in the resulting page may be less than this value, or none at all, even if there are more events matching the query. Incomplete pages can be detected by a non-empty nextPageToken field in the response. By default the value is 250 events. The page size can never be larger than 2500 events. Optional.",
|
|
24
|
-
optional: true,
|
|
25
|
-
type: "integer"
|
|
26
|
-
},
|
|
27
|
-
orderBy: {
|
|
28
|
-
description: "The order of the events returned in the result. Optional. The default is an unspecified, stable order.",
|
|
29
|
-
optional: true,
|
|
30
|
-
type: "string",
|
|
31
|
-
options() {
|
|
32
|
-
return [{label: "startTime", value: "startTime"}, {label: "updated", value: "updated"}]
|
|
33
|
-
},
|
|
34
|
-
// async options: ["startTime", "updated"],
|
|
35
|
-
default: "startTime"
|
|
36
|
-
},
|
|
37
|
-
pageToken: {
|
|
38
|
-
description: "Token specifying which result page to return. Optional.",
|
|
39
|
-
optional: true,
|
|
40
|
-
type: "string"
|
|
41
|
-
},
|
|
42
|
-
privateExtendedProperty: {
|
|
43
|
-
description: "Extended properties constraint specified as propertyName=value. Matches only private properties. This parameter might be repeated multiple times to return events that match all given constraints.",
|
|
44
|
-
optional: true,
|
|
45
|
-
type: "string"
|
|
46
|
-
},
|
|
47
|
-
q: {
|
|
48
|
-
description: "Free text search terms to find events that match these terms in any field, except for extended properties. Optional.",
|
|
49
|
-
optional: true,
|
|
50
|
-
type: "string"
|
|
51
|
-
},
|
|
52
|
-
sharedExtendedProperty: {
|
|
53
|
-
description: "Extended properties constraint specified as propertyName=value. Matches only shared properties. This parameter might be repeated multiple times to return events that match all given constraints.",
|
|
54
|
-
optional: true,
|
|
55
|
-
type: "string"
|
|
56
|
-
},
|
|
57
|
-
showDeleted: {
|
|
58
|
-
description: "Whether to include deleted events (with status equals \"cancelled\") in the result. Cancelled instances of recurring events (but not the underlying recurring event) will still be included if showDeleted and singleEvents are both False. If showDeleted and singleEvents are both True, only single instances of deleted events (but not the underlying recurring events) are returned. Optional. The default is False.",
|
|
59
|
-
optional: true,
|
|
60
|
-
type: "boolean"
|
|
61
|
-
},
|
|
62
|
-
showHiddenInvitations: {
|
|
63
|
-
description: "Whether to include hidden invitations in the result. Optional. The default is False.",
|
|
64
|
-
optional: true,
|
|
65
|
-
type: "boolean"
|
|
66
|
-
},
|
|
67
|
-
singleEvents: {
|
|
68
|
-
description: "Whether to expand recurring events into instances and only return single one-off events and instances of recurring events, but not the underlying recurring events themselves. Optional. The default is False.",
|
|
69
|
-
optional: true,
|
|
70
|
-
type: "boolean"
|
|
71
|
-
},
|
|
72
|
-
syncToken: {
|
|
73
|
-
description: "Token obtained from the nextSyncToken field returned on the last page of results from the previous list request. It makes the result of this list request contain only entries that have changed since then. All events deleted since the previous list request will always be in the result set and it is not allowed to set showDeleted to False.",
|
|
74
|
-
optional: true,
|
|
75
|
-
type: "string"
|
|
76
|
-
},
|
|
77
|
-
timeMax: {
|
|
78
|
-
description: "Upper bound (exclusive) for an event's start time to filter by. Optional. The default is not to filter by start time. Must be an RFC3339 timestamp with mandatory time zone offset, for example, 2011-06-03T10:00:00-07:00, 2011-06-03T10:00:00Z. Milliseconds may be provided but are ignored. If timeMin is set, timeMax must be greater than timeMin.",
|
|
79
|
-
optional: true,
|
|
80
|
-
type: "string"
|
|
81
|
-
},
|
|
82
|
-
timeMin: {
|
|
83
|
-
description: "Lower bound (exclusive) for an event's end time to filter by. Optional. The default is not to filter by end time. Must be an RFC3339 timestamp with mandatory time zone offset, for example, 2011-06-03T10:00:00-07:00, 2011-06-03T10:00:00Z. Milliseconds may be provided but are ignored. If timeMax is set, timeMin must be smaller than timeMax.",
|
|
84
|
-
optional: true,
|
|
85
|
-
type: "string"
|
|
86
|
-
},
|
|
87
|
-
timeZone: {
|
|
88
|
-
description: "Time zone used in the response. Optional. The default is the time zone of the calendar.",
|
|
89
|
-
optional: true,
|
|
90
|
-
type: "string"
|
|
91
|
-
},
|
|
92
|
-
updatedMin: { description: "Lower bound for an event's last modification time (as a RFC3339 timestamp) to filter by. When specified, entries deleted since this time will always be included regardless of showDeleted. Optional. The default is not to filter by last modification time.",
|
|
93
|
-
optional: true,
|
|
94
|
-
type: "string"
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
methods: {
|
|
98
|
-
_tokens() {
|
|
99
|
-
const access_token = get(this, "$auth.oauth_access_token")
|
|
100
|
-
const refresh_token = get(this, "$auth.oauth_refresh_token")
|
|
101
|
-
return {
|
|
102
|
-
access_token,
|
|
103
|
-
refresh_token,
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
// returns a calendar object you can do whatever you want with
|
|
107
|
-
calendar() {
|
|
108
|
-
const auth = new google.auth.OAuth2()
|
|
109
|
-
auth.setCredentials(this._tokens())
|
|
110
|
-
const calendar = google.calendar({version: "v3", auth})
|
|
111
|
-
return calendar
|
|
112
|
-
},
|
|
113
|
-
async calendarList() {
|
|
114
|
-
const calendar = this.calendar()
|
|
115
|
-
const resp = await calendar.calendarList.list()
|
|
116
|
-
return resp
|
|
117
|
-
},
|
|
118
|
-
async list(config) {
|
|
119
|
-
const calendar = this.calendar()
|
|
120
|
-
const resp = await calendar.events.list(config)
|
|
121
|
-
return resp
|
|
122
|
-
},
|
|
123
|
-
// for config key value pairs - https://developers.google.com/calendar/v3/reference/events/list
|
|
124
|
-
// deprecated
|
|
125
|
-
async getEvents(config) {
|
|
126
|
-
return await this.list(config)
|
|
127
|
-
},
|
|
128
|
-
async watch(config) {
|
|
129
|
-
const calendar = this.calendar()
|
|
130
|
-
const resp = await calendar.events.watch(config)
|
|
131
|
-
return resp
|
|
132
|
-
},
|
|
133
|
-
async stop(config) {
|
|
134
|
-
const calendar = this.calendar()
|
|
135
|
-
const resp = await calendar.channels.stop(config)
|
|
136
|
-
return resp
|
|
137
|
-
},
|
|
138
|
-
async fullSync(calendarId) {
|
|
139
|
-
let nextSyncToken = null
|
|
140
|
-
let nextPageToken = null
|
|
141
|
-
while(!nextSyncToken) {
|
|
142
|
-
const listConfig = {
|
|
143
|
-
calendarId,
|
|
144
|
-
pageToken: nextPageToken
|
|
145
|
-
}
|
|
146
|
-
const syncResp = await this.list(listConfig)
|
|
147
|
-
nextPageToken = get(syncResp, "data.nextPageToken")
|
|
148
|
-
nextSyncToken = get(syncResp, "data.nextSyncToken")
|
|
149
|
-
}
|
|
150
|
-
return nextSyncToken
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const _ = require("lodash");
|
|
2
|
-
const googleCalendar = require("../../google_calendar.app.js");
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
key: "google_calendar-event-cancelled",
|
|
6
|
-
name: "Event Cancelled",
|
|
7
|
-
description: "Emits when an event is cancelled or deleted",
|
|
8
|
-
version: "0.0.1",
|
|
9
|
-
dedupe: "unique", // Dedupe events based on the Google Calendar event ID
|
|
10
|
-
props: {
|
|
11
|
-
googleCalendar,
|
|
12
|
-
calendarId: {
|
|
13
|
-
type: "string",
|
|
14
|
-
async options() {
|
|
15
|
-
const calListResp = await this.googleCalendar.calendarList();
|
|
16
|
-
const calendars = _.get(calListResp, "data.items");
|
|
17
|
-
if (calendars) {
|
|
18
|
-
const calendarIds = calendars.map((item) => {
|
|
19
|
-
return { value: item.id, label: item.summary };
|
|
20
|
-
});
|
|
21
|
-
return calendarIds;
|
|
22
|
-
}
|
|
23
|
-
return [];
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
timer: {
|
|
27
|
-
type: "$.interface.timer",
|
|
28
|
-
default: {
|
|
29
|
-
intervalSeconds: 5 * 60,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
async run(event) {
|
|
34
|
-
const intervalMs = 1000 * (event.interval_seconds || 300); // fall through to default for manual testing
|
|
35
|
-
const now = new Date();
|
|
36
|
-
|
|
37
|
-
const updatedMin = new Date(now.getTime() - intervalMs).toISOString();
|
|
38
|
-
|
|
39
|
-
const config = {
|
|
40
|
-
calendarId: this.calendarId,
|
|
41
|
-
updatedMin,
|
|
42
|
-
showDeleted: true,
|
|
43
|
-
singleEvents: true,
|
|
44
|
-
orderBy: "startTime",
|
|
45
|
-
};
|
|
46
|
-
const resp = await this.googleCalendar.getEvents(config);
|
|
47
|
-
|
|
48
|
-
const events = _.get(resp.data, "items");
|
|
49
|
-
if (Array.isArray(events)) {
|
|
50
|
-
for (const event of events) {
|
|
51
|
-
// only emit if status is cancelled
|
|
52
|
-
if (event.status === "cancelled") {
|
|
53
|
-
const { summary, id } = event;
|
|
54
|
-
this.$emit(event, {
|
|
55
|
-
summary,
|
|
56
|
-
id,
|
|
57
|
-
ts: +new Date(event.start.dateTime),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
console.log("nothing to emit");
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
};
|