@pipedream/microsoft_outlook_calendar 0.3.4 → 0.5.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.
@@ -0,0 +1,59 @@
1
+ import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
2
+ import { ConfigurationError } from "@pipedream/platform";
3
+
4
+ export default {
5
+ type: "action",
6
+ key: "microsoft_outlook_calendar-delete-recurring-event-instance",
7
+ version: "0.0.1",
8
+ annotations: {
9
+ destructiveHint: true,
10
+ openWorldHint: true,
11
+ readOnlyHint: false,
12
+ },
13
+ name: "Delete Recurring Event Instance",
14
+ description: "Delete an individual instance of a recurring event in the user's default calendar. [See the documentation](https://learn.microsoft.com/en-us/graph/api/event-delete?view=graph-rest-1.0&tabs=http)",
15
+ props: {
16
+ microsoftOutlook,
17
+ recurringEventId: {
18
+ propDefinition: [
19
+ microsoftOutlook,
20
+ "recurringEventId",
21
+ ],
22
+ },
23
+ startDateTime: {
24
+ type: "string",
25
+ label: "Start Date Time",
26
+ description: "The start of the time range to find instances, in ISO 8601 format (e.g., `2024-01-01T00:00:00Z`)",
27
+ },
28
+ endDateTime: {
29
+ type: "string",
30
+ label: "End Date Time",
31
+ description: "The end of the time range to find instances, in ISO 8601 format (e.g., `2024-12-31T23:59:59Z`)",
32
+ },
33
+ instanceId: {
34
+ propDefinition: [
35
+ microsoftOutlook,
36
+ "instanceId",
37
+ (c) => ({
38
+ recurringEventId: c.recurringEventId,
39
+ startDateTime: c.startDateTime,
40
+ endDateTime: c.endDateTime,
41
+ }),
42
+ ],
43
+ },
44
+ },
45
+ async run({ $ }) {
46
+ if (new Date(this.startDateTime) >= new Date(this.endDateTime)) {
47
+ throw new ConfigurationError("`Start Date Time` must be before `End Date Time`");
48
+ }
49
+
50
+ const response = await this.microsoftOutlook.deleteCalendarEvent({
51
+ $,
52
+ eventId: this.instanceId,
53
+ });
54
+
55
+ $.export("$summary", `Successfully deleted recurring event instance with ID ${this.instanceId}`);
56
+
57
+ return response;
58
+ },
59
+ };
@@ -0,0 +1,186 @@
1
+ import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
2
+ import { ConfigurationError } from "@pipedream/platform";
3
+
4
+ export default {
5
+ type: "action",
6
+ key: "microsoft_outlook_calendar-update-recurring-event-instance",
7
+ version: "0.0.1",
8
+ annotations: {
9
+ destructiveHint: false,
10
+ openWorldHint: true,
11
+ readOnlyHint: false,
12
+ },
13
+ name: "Update Recurring Event Instance",
14
+ description: "Update an individual instance of a recurring event in the user's default calendar. [See the documentation](https://learn.microsoft.com/en-us/graph/api/event-update?view=graph-rest-1.0&tabs=http)",
15
+ props: {
16
+ microsoftOutlook,
17
+ recurringEventId: {
18
+ propDefinition: [
19
+ microsoftOutlook,
20
+ "recurringEventId",
21
+ ],
22
+ },
23
+ startDateTime: {
24
+ type: "string",
25
+ label: "Start Date Time",
26
+ description: "The start of the time range to find instances, in ISO 8601 format (e.g., `2024-01-01T00:00:00Z`)",
27
+ },
28
+ endDateTime: {
29
+ type: "string",
30
+ label: "End Date Time",
31
+ description: "The end of the time range to find instances, in ISO 8601 format (e.g., `2024-12-31T23:59:59Z`)",
32
+ },
33
+ instanceId: {
34
+ propDefinition: [
35
+ microsoftOutlook,
36
+ "instanceId",
37
+ (c) => ({
38
+ recurringEventId: c.recurringEventId,
39
+ startDateTime: c.startDateTime,
40
+ endDateTime: c.endDateTime,
41
+ }),
42
+ ],
43
+ },
44
+ subject: {
45
+ label: "Subject",
46
+ description: "Subject of the event instance",
47
+ type: "string",
48
+ optional: true,
49
+ },
50
+ contentType: {
51
+ propDefinition: [
52
+ microsoftOutlook,
53
+ "contentType",
54
+ ],
55
+ optional: true,
56
+ },
57
+ content: {
58
+ propDefinition: [
59
+ microsoftOutlook,
60
+ "content",
61
+ ],
62
+ description: "Content",
63
+ optional: true,
64
+ },
65
+ timeZone: {
66
+ propDefinition: [
67
+ microsoftOutlook,
68
+ "timeZone",
69
+ ],
70
+ optional: true,
71
+ },
72
+ start: {
73
+ propDefinition: [
74
+ microsoftOutlook,
75
+ "start",
76
+ ],
77
+ description: "New start date-time for this instance (yyyy-MM-ddThh:mm:ss). Requires **Time Zone** to be set.",
78
+ optional: true,
79
+ },
80
+ end: {
81
+ propDefinition: [
82
+ microsoftOutlook,
83
+ "end",
84
+ ],
85
+ description: "New end date-time for this instance (yyyy-MM-ddThh:mm:ss). Requires **Time Zone** to be set.",
86
+ optional: true,
87
+ },
88
+ attendees: {
89
+ propDefinition: [
90
+ microsoftOutlook,
91
+ "attendees",
92
+ ],
93
+ optional: true,
94
+ },
95
+ location: {
96
+ propDefinition: [
97
+ microsoftOutlook,
98
+ "location",
99
+ ],
100
+ optional: true,
101
+ },
102
+ isOnlineMeeting: {
103
+ propDefinition: [
104
+ microsoftOutlook,
105
+ "isOnlineMeeting",
106
+ ],
107
+ optional: true,
108
+ },
109
+ expand: {
110
+ propDefinition: [
111
+ microsoftOutlook,
112
+ "expand",
113
+ ],
114
+ description: "Additional event details. [See object definition](https://docs.microsoft.com/en-us/graph/api/resources/event)",
115
+ optional: true,
116
+ },
117
+ },
118
+ async run({ $ }) {
119
+ if (new Date(this.startDateTime) >= new Date(this.endDateTime)) {
120
+ throw new ConfigurationError("`Start Date Time` must be before `End Date Time`");
121
+ }
122
+
123
+ const data = {
124
+ ...this.expand,
125
+ };
126
+
127
+ if (this.subject) {
128
+ data.subject = this.subject;
129
+ }
130
+
131
+ if (this.isOnlineMeeting !== undefined) {
132
+ data.isOnlineMeeting = this.isOnlineMeeting;
133
+ }
134
+
135
+ if (this.location) {
136
+ data.location = {
137
+ displayName: this.location,
138
+ };
139
+ }
140
+
141
+ if (this.content) {
142
+ data.body = {
143
+ contentType: this.contentType ?? "text",
144
+ content: this.content,
145
+ };
146
+ }
147
+
148
+ if ((this.start || this.end) && !this.timeZone) {
149
+ throw new ConfigurationError("Time Zone is required when updating start or end times");
150
+ }
151
+ if (this.start && this.timeZone) {
152
+ data.start = {
153
+ dateTime: this.start,
154
+ timeZone: this.timeZone,
155
+ };
156
+ }
157
+ if (this.end && this.timeZone) {
158
+ data.end = {
159
+ dateTime: this.end,
160
+ timeZone: this.timeZone,
161
+ };
162
+ }
163
+
164
+ if (this.attendees) {
165
+ data.attendees = this.attendees.map((at) => ({
166
+ emailAddress: {
167
+ address: at,
168
+ },
169
+ }));
170
+ }
171
+
172
+ if (!Object.keys(data).length) {
173
+ throw new ConfigurationError("At least one field must be provided to update the event instance");
174
+ }
175
+
176
+ const response = await this.microsoftOutlook.updateCalendarEvent({
177
+ $,
178
+ eventId: this.instanceId,
179
+ data,
180
+ });
181
+
182
+ $.export("$summary", `Successfully updated recurring event instance with ID ${response?.id ?? this.instanceId}`);
183
+
184
+ return response;
185
+ },
186
+ };
@@ -17,6 +17,45 @@ export default {
17
17
  }));
18
18
  },
19
19
  },
20
+ recurringEventId: {
21
+ label: "Recurring Event ID",
22
+ description: "The ID of the recurring event series",
23
+ type: "string",
24
+ async options() {
25
+ const { value: events } = await this.listCalendarEvents();
26
+
27
+ return events
28
+ .filter((event) => event.recurrence)
29
+ .map((event) => ({
30
+ label: event.subject,
31
+ value: event.id,
32
+ }));
33
+ },
34
+ },
35
+ instanceId: {
36
+ label: "Instance ID",
37
+ description: "The ID of the specific occurrence of the recurring event",
38
+ type: "string",
39
+ async options({
40
+ recurringEventId, startDateTime, endDateTime,
41
+ }) {
42
+ if (!recurringEventId || !startDateTime || !endDateTime) {
43
+ return [];
44
+ }
45
+ const { value: instances } = await this.listEventInstances({
46
+ eventId: recurringEventId,
47
+ params: {
48
+ startDateTime,
49
+ endDateTime,
50
+ },
51
+ });
52
+
53
+ return instances.map((instance) => ({
54
+ label: `${instance.subject} - ${instance.start?.dateTime}`,
55
+ value: instance.id,
56
+ }));
57
+ },
58
+ },
20
59
  contentType: {
21
60
  label: "Content Type",
22
61
  description: "Content type (default `text`)",
@@ -188,5 +227,14 @@ export default {
188
227
  ...args,
189
228
  });
190
229
  },
230
+ async listEventInstances({
231
+ eventId, ...args
232
+ }) {
233
+ return this._makeRequest({
234
+ method: "GET",
235
+ path: `/me/events/${eventId}/instances`,
236
+ ...args,
237
+ });
238
+ },
191
239
  },
192
240
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/microsoft_outlook_calendar",
3
- "version": "0.3.4",
3
+ "version": "0.5.0",
4
4
  "description": "Pipedream Microsoft Outlook Calendar Components",
5
5
  "main": "microsoft_outlook_calendar.app.mjs",
6
6
  "keywords": [
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "@pipedream/microsoft_outlook": "^1.7.3",
19
19
  "@pipedream/pipedream": "^0.4.2",
20
- "@pipedream/platform": "^3.1.0",
20
+ "@pipedream/platform": "^3.1.1",
21
21
  "luxon": "^3.7.2",
22
22
  "windows-iana": "^5.1.0"
23
23
  }
@@ -0,0 +1,137 @@
1
+ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
2
+ import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
3
+
4
+ export default {
5
+ key: "microsoft_outlook_calendar-new-upcoming-event-polling",
6
+ name: "New Upcoming Calendar Event (Polling)",
7
+ description: "Emit new event based on a time interval before an upcoming calendar event. [See the documentation](https://docs.microsoft.com/en-us/graph/api/user-list-events)",
8
+ version: "0.0.1",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ props: {
12
+ microsoftOutlook,
13
+ db: "$.service.db",
14
+ timer: {
15
+ type: "$.interface.timer",
16
+ default: {
17
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
18
+ },
19
+ },
20
+ pollingInfo: {
21
+ type: "alert",
22
+ alertType: "info",
23
+ content: "Since this source executes based on a timer, event emission may be slightly delayed. For example, if the source runs every 5 minutes, the delay may be up to 5 minutes. You can use the `new-upcoming-event` source for instant event emission.",
24
+ },
25
+ minutesBefore: {
26
+ type: "integer",
27
+ label: "Minutes Before",
28
+ description: "Number of minutes to trigger before the start of the calendar event.",
29
+ min: 0,
30
+ default: 30,
31
+ },
32
+ },
33
+ methods: {
34
+ _getEmittedEvents() {
35
+ return this.db.get("emittedEvents") || {};
36
+ },
37
+ _setEmittedEvents(emittedEvents) {
38
+ this.db.set("emittedEvents", emittedEvents);
39
+ },
40
+ _cleanupEmittedEvents(now) {
41
+ const emittedEvents = this._getEmittedEvents();
42
+ const cleanedEvents = {};
43
+ let cleanedCount = 0;
44
+
45
+ // Keep only events that haven't passed yet
46
+ for (const [
47
+ eventId,
48
+ startTime,
49
+ ] of Object.entries(emittedEvents)) {
50
+ if (startTime > now.getTime()) {
51
+ cleanedEvents[eventId] = startTime;
52
+ } else {
53
+ cleanedCount++;
54
+ }
55
+ }
56
+
57
+ if (cleanedCount > 0) {
58
+ console.log(`Cleaned up ${cleanedCount} past event(s) from emitted events tracker`);
59
+ this._setEmittedEvents(cleanedEvents);
60
+ }
61
+
62
+ return cleanedEvents;
63
+ },
64
+ generateMeta(event) {
65
+ const ts = event.start?.dateTime
66
+ ? Date.parse(event.start.dateTime)
67
+ : Date.now();
68
+ return {
69
+ id: `${event.uid}-${ts}`,
70
+ summary: `Upcoming: ${event.subject || "(untitled)"}`,
71
+ ts,
72
+ };
73
+ },
74
+ },
75
+ hooks: {
76
+ async deploy() {
77
+ // On initial deploy, don't emit historical events
78
+ // Just initialize the emitted events tracker
79
+ this._setEmittedEvents({});
80
+ },
81
+ },
82
+ async run() {
83
+ const now = new Date();
84
+ const alertWindowMs = this.minutesBefore * 60 * 1000;
85
+ const timeMax = new Date(now.getTime() + alertWindowMs).toISOString();
86
+
87
+ // Clean up old emitted events
88
+ const emittedEvents = this._cleanupEmittedEvents(now);
89
+
90
+ // Fetch events within the alert window
91
+ const { value: events } = await this.microsoftOutlook.listCalendarView({
92
+ params: {
93
+ startDateTime: now.toISOString(),
94
+ endDateTime: timeMax,
95
+ $orderby: "start/dateTime",
96
+ },
97
+ });
98
+
99
+ if (!events || events.length === 0) {
100
+ console.log("No upcoming events found in the alert window");
101
+ return;
102
+ }
103
+
104
+ for (const event of events) {
105
+ // Skip if already emitted
106
+ if (emittedEvents[event.uid]) {
107
+ continue;
108
+ }
109
+
110
+ const startTime = event.start
111
+ ? new Date(event.start.dateTime)
112
+ : null;
113
+
114
+ if (!startTime) {
115
+ continue;
116
+ }
117
+
118
+ const timeRemaining = startTime.getTime() - now.getTime();
119
+ if (timeRemaining < 0) {
120
+ continue;
121
+ }
122
+
123
+ const alertThresholdMs = this.minutesBefore * 60 * 1000;
124
+
125
+ // Emit if time remaining is less than or equal to the alert threshold
126
+ if (timeRemaining <= alertThresholdMs) {
127
+ emittedEvents[event.uid] = startTime.getTime();
128
+
129
+ const meta = this.generateMeta(event);
130
+ this.$emit(event, meta);
131
+ }
132
+ }
133
+
134
+ this._setEmittedEvents(emittedEvents);
135
+ },
136
+ };
137
+