|
1 | | -# Recurring Events |
| 1 | +# Recurring Events - Overview |
2 | 2 |
|
3 | 3 | ## Core Concepts |
4 | 4 |
|
@@ -176,186 +176,3 @@ Google Calendar uses specific ID patterns to identify events: |
176 | 176 | - Helps maintain history |
177 | 177 | - Allows for potential restoration |
178 | 178 | - Maintains relationships between events |
179 | | - |
180 | | -## Synchronization Workflow |
181 | | - |
182 | | -### Overview |
183 | | - |
184 | | -Synchronizing recurring events with Google Calendar involves: |
185 | | - |
186 | | -1. Initial setup and watching for changes |
187 | | -2. Receiving notifications when changes occur |
188 | | -3. Fetching the changed events |
189 | | -4. Analyzing the changes |
190 | | -5. Updating your database accordingly |
191 | | - |
192 | | -### Initial Setup |
193 | | - |
194 | | -1. **Watch for Changes** |
195 | | - |
196 | | - - Set up a webhook to receive notifications from Google Calendar |
197 | | - - Store the `nextSyncToken` from the initial sync |
198 | | - - This token is crucial for incremental updates |
199 | | - |
200 | | -2. **Initial Sync** |
201 | | - - Fetch all events for the calendar |
202 | | - - Store the final `nextSyncToken` for future use |
203 | | - - This establishes your baseline state |
204 | | - |
205 | | -### Change Detection |
206 | | - |
207 | | -When changes occur in Google Calendar: |
208 | | - |
209 | | -1. **Notification Received** |
210 | | - |
211 | | - - Google sends a notification to your webhook |
212 | | - - The notification indicates which calendar changed |
213 | | - - No event details are included in the notification |
214 | | - |
215 | | -2. **Fetch Changes** |
216 | | - - Use the stored `nextSyncToken` to fetch only changed events |
217 | | - - Google returns: |
218 | | - - Modified events |
219 | | - - New events |
220 | | - - Deleted events (marked as "cancelled") |
221 | | - - A new `nextSyncToken` for the next sync |
222 | | - |
223 | | -```mermaid |
224 | | -sequenceDiagram |
225 | | - participant User |
226 | | - participant Google |
227 | | - participant Webhook |
228 | | - participant App |
229 | | - participant DB |
230 | | -
|
231 | | - Note over User,DB: Initial Setup |
232 | | - User->>Google: Create Recurring Event |
233 | | - Google->>App: Send Notification |
234 | | - App->>Google: Fetch Changes |
235 | | - Google-->>App: Return Events + nextSyncToken |
236 | | - App->>DB: Store Events & Token |
237 | | -
|
238 | | - Note over User,DB: Sync Changes |
239 | | - User->>Google: Modify Event |
240 | | - Google->>Webhook: Send Notification |
241 | | - Webhook->>App: Forward Notification |
242 | | - App->>Google: Fetch Changes (with token) |
243 | | - Google-->>App: Return Changes + new token |
244 | | - App->>App: Analyze Changes |
245 | | - App->>DB: Update Database |
246 | | - App->>DB: Store new token |
247 | | -``` |
248 | | - |
249 | | -## Splitting Series |
250 | | - |
251 | | -### Problem Summary |
252 | | - |
253 | | -When users modify recurring events in Google Calendar using "this and following" operations, we need to handle the resulting series splits in our sync system. These splits can occur in two scenarios: |
254 | | - |
255 | | -1. Delete "this and following" - splits the series and deletes future instances |
256 | | - |
257 | | -2. Edit "this and following" - splits the series and creates a new series with modified properties |
258 | | - |
259 | | -The challenge is that Google Calendar's API sends these changes in potentially multiple payloads, and we can't rely on receiving all related changes in a single payload or in a specific order. |
260 | | - |
261 | | -### Solution Overview |
262 | | - |
263 | | -Treat each change as an independent operation based on the event's properties, not on payload combinations or ID patterns. This approach is more reliable as it: |
264 | | - |
265 | | -1. Uses documented API properties |
266 | | - |
267 | | -2. Doesn't rely on payload ordering |
268 | | - |
269 | | -3. Handles each change atomically |
270 | | - |
271 | | -4. Works consistently across all operation types |
272 | | - |
273 | | -### Approach |
274 | | - |
275 | | -Whenever a series is split, delete the following instances |
276 | | - |
277 | | -- Don't worry about what caused the split -- differentiating between editing 'this and following' vs deleting 'this and following' |
278 | | - |
279 | | -Has recurrence with UNTIL? → Original series being split |
280 | | -Has recurringEventId? → Instance or new series |
281 | | -Has status: "cancelled"? → Cancelled instance |
282 | | -Has originalStartTime? → Modified instance |
283 | | - |
284 | | -### Design Flow: Detecting Series Splits |
285 | | - |
286 | | -```mermaid |
287 | | -
|
288 | | -graph TD |
289 | | -
|
290 | | - A[Receive Google Calendar Event] --> B{Has recurringEventId?} |
291 | | -
|
292 | | - B -->|Yes| C{Status = cancelled?} |
293 | | -
|
294 | | - C -->|Yes| D[Delete Instance] |
295 | | -
|
296 | | - C -->|No| E[Update Instance] |
297 | | -
|
298 | | - B -->|No| F[Fetch Current State] |
299 | | -
|
300 | | - F --> G{Is Series Split?} |
301 | | -
|
302 | | - G -->|Yes| H[Update Original Series] |
303 | | -
|
304 | | - G -->|No| I[Create/Update Series] |
305 | | -
|
306 | | -
|
307 | | -
|
308 | | - H --> J[Delete Future Instances] |
309 | | -
|
310 | | - I --> K[Generate New Instances] |
311 | | -
|
312 | | -
|
313 | | -
|
314 | | - subgraph "Series Split Detection" |
315 | | -
|
316 | | - G -->|Compare| L[UNTIL Dates] |
317 | | -
|
318 | | - L -->|Earlier| G |
319 | | -
|
320 | | - end |
321 | | -
|
322 | | -``` |
323 | | - |
324 | | -### Anti-Pattern Design Flow |
325 | | - |
326 | | -We're sharing this flawed approach because it seems like a logical way to approach the problem, but it doesn't work. Do not do this. |
327 | | - |
328 | | -```mermaid |
329 | | -graph TD |
330 | | - subgraph "Sync Process" |
331 | | - Watch[Watch for Changes] -->|1. Notification| Notify[Receive Notification] |
332 | | - Notify -->|2. Fetch| Fetch[Fetch Changes] |
333 | | - Fetch -->|3. Analyze| Analyze[Analyze Changes] |
334 | | - Analyze -->|4. Update| Update[Update Database] |
335 | | - Update -->|5. Store Token| Token[Store nextSyncToken] |
336 | | - end |
337 | | -
|
338 | | - subgraph "Change Types" |
339 | | - Create[Create Series] |
340 | | - EditOne[Edit One Instance] |
341 | | - EditFuture[Edit This & Future] |
342 | | - EditAll[Edit All Instances] |
343 | | - DeleteOne[Delete Instance] |
344 | | - DeleteAll[Delete Series] |
345 | | - end |
346 | | -
|
347 | | - Analyze -->|determines| Create |
348 | | - Analyze -->|determines| EditOne |
349 | | - Analyze -->|determines| EditFuture |
350 | | - Analyze -->|determines| EditAll |
351 | | - Analyze -->|determines| DeleteOne |
352 | | - Analyze -->|determines| DeleteAll |
353 | | -``` |
354 | | - |
355 | | -The flawed assumption is that you will be able to determine the user's action based on the payloads |
356 | | -that you receive from Google Calendar after a notification is received. Unfortunately, this is not the case. |
357 | | -Google Calendar API batches changes, and does not return them in a way that corresponds to what |
358 | | -caused the change. |
359 | | - |
360 | | -Because of this, you are better off treating each change as an independent operation, without regard |
361 | | -for what caused the change. |
0 commit comments