Search code examples
javascriptnode.jsgoogle-calendar-api

Google calendar API updating all events instead of just selected event


My current problem is that I'm using google calendar API. When I attempt to patch an event the API updates all future events instead of just the selected event.

My project goes like this, User selects date on calendar and then request is sent to backend to retrieve all events for that day. Then the user can click on the event to confirm it and another request is sent to the server. During the request the calendar API should just update the event for the event clicked.

From the research I've done I think I must use recurrence and then the EXDATE but I tried to decipher the google calendar docs and didn't get anywhere...

This is the code I've tried so far.

    adjustEventForTeacher = async(userCalendarLocation, slotId) => {

        let getEventStartTime = await calendar.events.get({
           calendarId: 's',
            eventId: slotId
        })
        const getEventEndTime = getEventStartTime.data.end.dateTime
        getEventStartTime = getEventStartTime.data.start.dateTime
        console.log(getEventStartTime)
        const res = await calendar.events.patch({
            calendarId: 's',
            eventId: slotId,
            requestBody: {
                summary: 'Busy',
                name: 'Busy',
                status: 'confirmed',
                colorId: '0',
                // start: {
                //     dateTime: getEventStartTime
                // },
                // end: {
                //     dateTime: getEventEndTime
                // }
                // recurrence: [ `EXDATE;VALUE=DATE:${getEventStartTime}` ],
            }
            })

        console.log(res.data)
    }

Thank you.

enter image description here


Solution

  • It's not too clear in the documentation but I figured out how to change a single instance of a recurring event by analyzing how the events look like in the API after being changed in the UI.

    As you've already noticed, using update or patch changes all instances of a recurring event. It makes sense since recurring events still only have a single ID and their recurrence is defined by their recurrence[] rules. That way the calendar doesn't have to create a potentially infinite amount of new events when they're set to occur daily without limit.

    The trick is that instead you have to use the events.insert method to create a new event, while also specifying that it's a modification of an existing recurring event. This is done by adding the recurringEventId and originalStartTime fields in your request body. The recurringEventId should be the ID of the recurring event that you want to change, and the originalStartTime should have the startTime and timeZone of the specific instance that you want to change.

    Full explanation: Here's a sample of a recurring event in the API, which you can find by creating one in the UI and using events.list to retrieve it. The actual output is longer but I trimmed down some of the irrelevant fields:

      {
       "kind": "calendar#event",
       "id": "<original-recurrent-event>",
       "status": "confirmed",
       "created": "2022-10-28T02:58:32.000Z",
       "updated": "2022-10-28T02:58:32.287Z",
       "summary": "Recurrent event",
       "start": {
        "dateTime": "2022-10-27T21:30:00-06:00",
        "timeZone": "your-timezone"
       },
       "end": {
        "dateTime": "2022-10-27T22:30:00-06:00",
        "timeZone": "your-timezone"
       },
       "recurrence": [
        "RRULE:FREQ=DAILY"
       ],
       "eventType": "default"
      }
    

    The above shows a daily recurrent event. The only difference between it and other events is that it has a recurrence rule. I replaced the actual ID with the <original-recurrent-event> placeholder.

    When I made a change to this event in the UI for only a single day a new event was added which looked like this:

    {
       "kind": "calendar#event",
       "id": "<original-recurrent-event>_20221029T033000Z",
       "status": "confirmed",
       "created": "2022-10-28T02:58:32.000Z",
       "updated": "2022-10-28T02:59:30.474Z",
       "summary": "Modified event",
       "start": {
        "dateTime": "2022-10-28T21:00:00-06:00",
        "timeZone": "your-timezone"
       },
       "end": {
        "dateTime": "2022-10-28T22:00:00-06:00",
        "timeZone": "your-timezone"
       },
       "recurringEventId": "<original-recurrent-event>",
       "originalStartTime": {
        "dateTime": "2022-10-28T21:30:00-06:00",
        "timeZone": "your-timezone"
       },
       },
       "eventType": "default"
      }
    

    This looks pretty much like a regular new event with a few differences:

    • Its id is not randomly generated as usual. Instead it consists of the original event ID and adds a timestamp with the original start time in UTC.
    • It has the fields recurringEventId and originalStartTime which I mentioned earlier.

    With all that said, in practice you just need to insert a new event with those fields which would look something like this for you:

        const res = await calendar.events.insert({ // use insert method
            calendarId: 's',
            requestBody: {
                summary: 'Busy',
                name: 'Busy',
                start: {
                    dateTime: getEventStartTime
                },
                end: {
                    dateTime: getEventEndTime
                },
                recurringEventId:slotId,
                originalStartTime:{
                    startTime:getOriginalStartTime,
                    timeZone:getTimezone
                }
            }
            })
    

    I don't have a Node environment to run this myself but I hope it gives you an idea of how to do it. When I tried it in the APIs Explorer in the documentation pages it worked as described. Strangely the documentation doesn't explain these fields so I submitted some feedback in hope that they can clarify this for the future.

    Sources: