@pipedream/google_calendar 0.8.4 → 0.8.5

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.
@@ -6,7 +6,7 @@ export default {
6
6
  key: "google_calendar-update-following-instances",
7
7
  name: "Update Following Event Instances",
8
8
  description: "Update all instances of a recurring event following a specific instance. This creates a new recurring event starting from the selected instance. [See the documentation](https://developers.google.com/calendar/api/guides/recurringevents#modifying_all_following_instances)",
9
- version: "0.0.3",
9
+ version: "0.0.4",
10
10
  type: "action",
11
11
  annotations: {
12
12
  destructiveHint: true,
@@ -191,69 +191,116 @@ export default {
191
191
  const instanceStartDate = targetInstance.start.dateTime || targetInstance.start.date;
192
192
  const untilDate = this.calculateUntilDate(instanceStartDate);
193
193
 
194
- // Step 3: Trim the original recurring event
195
- const trimmedRecurrence = this.modifyRecurrenceRule(
196
- originalEvent.recurrence,
197
- untilDate,
198
- );
199
-
200
- await this.googleCalendar.updateEvent({
194
+ // Step 3: Delete the target instance while it is still a valid part of the
195
+ // series. This must happen before trimming the RRULE — once UNTIL excludes the
196
+ // instance, Google Calendar rejects the delete with a 400 Bad Request.
197
+ // Explicit deletion prevents the instance from appearing as an orphaned
198
+ // duplicate after the series is split.
199
+ await this.googleCalendar.deleteEvent({
201
200
  calendarId: this.calendarId,
202
- eventId: this.recurringEventId,
201
+ eventId: this.instanceId,
203
202
  sendUpdates: this.sendUpdates,
204
- requestBody: {
205
- ...originalEvent,
206
- recurrence: trimmedRecurrence,
207
- },
208
203
  });
209
204
 
210
- // Step 4: Create new recurring event with changes
211
- const timeZone = await this.getTimeZone(this.timeZone || targetInstance.start.timeZone);
212
- const attendees = this.formatAttendees(this.attendees, originalEvent.attendees);
205
+ // Steps 4 & 5 are wrapped in a try/catch: the delete above is irreversible,
206
+ // so if either subsequent call fails we attempt to restore the deleted instance
207
+ // (by patching its status back to "confirmed") before re-throwing. If the
208
+ // restore itself fails we emit a distinct partial-failure summary so the caller
209
+ // knows the instance is gone and manual intervention is required.
210
+ let step4TrimApplied = false;
211
+ try {
212
+ // Step 4: Trim the original recurring event
213
+ const trimmedRecurrence = this.modifyRecurrenceRule(
214
+ originalEvent.recurrence,
215
+ untilDate,
216
+ );
213
217
 
214
- // Determine recurrence for new event
215
- let newRecurrence;
216
- if (this.repeatFrequency) {
217
- newRecurrence = this.formatRecurrence({
218
- repeatFrequency: this.repeatFrequency,
219
- repeatInterval: this.repeatInterval,
220
- repeatTimes: this.repeatTimes,
221
- repeatUntil: this.repeatUntil,
218
+ await this.googleCalendar.updateEvent({
219
+ calendarId: this.calendarId,
220
+ eventId: this.recurringEventId,
221
+ sendUpdates: this.sendUpdates,
222
+ requestBody: {
223
+ ...originalEvent,
224
+ recurrence: trimmedRecurrence,
225
+ },
222
226
  });
223
- } else {
224
- // Use original recurrence rules
225
- newRecurrence = originalEvent.recurrence;
226
- }
227
+ step4TrimApplied = true;
227
228
 
228
- const newEvent = await this.googleCalendar.createEvent({
229
- calendarId: this.calendarId,
230
- sendUpdates: this.sendUpdates,
231
- resource: {
232
- summary: this.summary || originalEvent.summary,
233
- location: this.location || originalEvent.location,
234
- description: this.description || originalEvent.description,
235
- start: this.getDateParam({
236
- date: this.eventStartDate || instanceStartDate,
237
- timeZone: timeZone || targetInstance.start.timeZone,
238
- }),
239
- end: this.getDateParam({
240
- date: this.eventEndDate || targetInstance.end.dateTime || targetInstance.end.date,
241
- timeZone: timeZone || targetInstance.end.timeZone,
242
- }),
243
- recurrence: newRecurrence,
244
- attendees,
245
- colorId: this.colorId || originalEvent.colorId,
246
- },
247
- });
229
+ // Step 5: Create new recurring event with changes
230
+ const timeZone = await this.getTimeZone(this.timeZone || targetInstance.start.timeZone);
231
+ const attendees = this.formatAttendees(this.attendees, originalEvent.attendees);
232
+
233
+ // Determine recurrence for new event
234
+ let newRecurrence;
235
+ if (this.repeatFrequency) {
236
+ newRecurrence = this.formatRecurrence({
237
+ repeatFrequency: this.repeatFrequency,
238
+ repeatInterval: this.repeatInterval,
239
+ repeatTimes: this.repeatTimes,
240
+ repeatUntil: this.repeatUntil,
241
+ });
242
+ } else {
243
+ // Inherit original recurrence rules, but strip any COUNT so the tail
244
+ // series does not reapply the original total-count from the beginning.
245
+ newRecurrence = originalEvent.recurrence?.map((rule) => {
246
+ if (!rule.startsWith("RRULE:")) {
247
+ return rule;
248
+ }
249
+ return rule.replace(/;COUNT=[^;]+/g, "");
250
+ });
251
+ }
252
+
253
+ const newEvent = await this.googleCalendar.createEvent({
254
+ calendarId: this.calendarId,
255
+ sendUpdates: this.sendUpdates,
256
+ resource: {
257
+ summary: this.summary || originalEvent.summary,
258
+ location: this.location || originalEvent.location,
259
+ description: this.description || originalEvent.description,
260
+ start: this.getDateParam({
261
+ date: this.eventStartDate || instanceStartDate,
262
+ timeZone: timeZone || targetInstance.start.timeZone,
263
+ }),
264
+ end: this.getDateParam({
265
+ date: this.eventEndDate || targetInstance.end.dateTime || targetInstance.end.date,
266
+ timeZone: timeZone || targetInstance.end.timeZone,
267
+ }),
268
+ recurrence: newRecurrence,
269
+ attendees,
270
+ colorId: this.colorId || originalEvent.colorId,
271
+ },
272
+ });
248
273
 
249
- $.export("$summary", `Successfully split recurring event. Original trimmed, new event created with ID: \`${newEvent.id}\``);
274
+ $.export("$summary", `Successfully split recurring event. Original trimmed, new event created with ID: \`${newEvent.id}\``);
250
275
 
251
- return {
252
- originalEvent: {
253
- id: this.recurringEventId,
254
- trimmedRecurrence,
255
- },
256
- newEvent,
257
- };
276
+ return {
277
+ originalEvent: {
278
+ id: this.recurringEventId,
279
+ trimmedRecurrence,
280
+ },
281
+ newEvent,
282
+ };
283
+ } catch (splitError) {
284
+ // Attempt to restore the deleted instance by patching its status back to
285
+ // "confirmed". The instance ID is still valid as a cancelled exception.
286
+ try {
287
+ await this.googleCalendar.updateEvent({
288
+ calendarId: this.calendarId,
289
+ eventId: this.instanceId,
290
+ sendUpdates: this.sendUpdates,
291
+ requestBody: {
292
+ ...targetInstance,
293
+ status: "confirmed",
294
+ },
295
+ });
296
+ if (step4TrimApplied) {
297
+ $.export("$summary", `Partial failure: the original instance (${this.instanceId}) was restored but the series RRULE was already trimmed and the new event was not created. The series may require manual repair.`);
298
+ }
299
+ } catch (_restoreError) {
300
+ $.export("$summary", `Partial failure: the original instance (${this.instanceId}) was deleted but the split could not be completed and automatic restoration failed. Manual intervention required.`);
301
+ throw splitError;
302
+ }
303
+ throw splitError;
304
+ }
258
305
  },
259
306
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/google_calendar",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Pipedream Google_calendar Components",
5
5
  "main": "google_calendar.app.mjs",
6
6
  "keywords": [