← Google Calendar + Go integrations

Run Go Code with Go API on New or Updated Event (Instant) from Google Calendar API

Pipedream makes it easy to connect APIs for Go, Google Calendar and 1000+ other apps remarkably fast.

Trigger workflow on
New or Updated Event (Instant) from the Google Calendar API
Next, do this
Run Go Code with the Go API
No credit card required
Into to Pipedream
Watch us build a workflow
Watch us build a workflow
7 min
Watch now ➜
Trusted by 250,000+ developers from startups to Fortune 500 companies:
Trusted by 250,000+ developers from startups to Fortune 500 companies

Developers Pipedream

Getting Started

This integration creates a workflow with a Google Calendar trigger and Go action. When you configure and deploy the workflow, it will run on Pipedream's servers 24x7 for free.

  1. Select this integration
  2. Configure the New or Updated Event (Instant) trigger
    1. Connect your Google Calendar account
    2. Select one or more Calendars
    3. Optional- Configure New events only?
    4. Configure Push notification renewal schedule
  3. Configure the Run Go Code action
    1. Connect your Go account
  4. Deploy the workflow
  5. Send a test event to validate your setup
  6. Turn on the trigger

Details

This integration uses pre-built, open source components from Pipedream's GitHub repo. These components are developed by Pipedream and the community, and verified and maintained by Pipedream.

To contribute an update to an existing component or create a new component, create a PR on GitHub. If you're new to Pipedream component development, you can start with quickstarts for trigger span and action development, and then review the component API reference.

Trigger

Description:Emit new calendar events when an event is created or updated (does not emit cancelled events)
Version:0.1.5
Key:google_calendar-new-or-updated-event-instant

Google Calendar Overview

The Google Calendar API gives developers access to Google Calendar data,
allowing them to create their own applications that can read, write, and update
calendar data. With the API, users can integrate their own calendar
applications with Google Calendar, giving them the ability to manage their
calendars in one place. The API also provides the ability to search for events
and create new events.

Possible applications that could be built using the Google Calendar API
include:

  • A calendar application that integrates with Google Calendar, allowing users
    to manage their calendars in one place.
  • A calendar application that allows users to search for events and create new
    events.
  • A to-do list application that integrates with Google Calendar, allowing users
    to see their tasks and events in one place.
  • A reminder application that uses Google Calendar data to remind users of
    upcoming events.

Trigger Code

import { v4 as uuid } from "uuid";
import googleCalendar from "../../google_calendar.app.mjs";
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";

export default {
  key: "google_calendar-new-or-updated-event-instant",
  type: "source",
  name: "New or Updated Event (Instant)",
  description: "Emit new calendar events when an event is created or updated (does not emit cancelled events)",
  version: "0.1.5",
  dedupe: "unique",
  props: {
    googleCalendar,
    db: "$.service.db",
    calendarIds: {
      propDefinition: [
        googleCalendar,
        "calendarId",
      ],
      type: "string[]",
      label: "Calendars",
      description: "Select one or more calendars to watch",
    },
    newOnly: {
      label: "New events only?",
      type: "boolean",
      description: "Emit new events only, and not updates to existing events",
      optional: true,
      default: false,
    },
    http: "$.interface.http",
    timer: {
      label: "Push notification renewal schedule",
      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**.",
      type: "$.interface.timer",
      static: {
        intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
      },
    },
  },
  hooks: {
    async deploy() {
      const events = [];
      const params = {
        maxResults: 25,
        orderBy: "updated",
      };
      for (const calendarId of this.calendarIds) {
        params.calendarId = calendarId;
        const { items } = await this.googleCalendar.listEvents(params);
        events.push(...items);
      }
      events.sort((a, b) => (Date.parse(a.updated) > Date.parse(b.updated))
        ? 1
        : -1);
      for (const event of events.slice(-25)) {
        const meta = this.generateMeta(event);
        this.$emit(event, meta);
      }
    },
    async activate() {
      await this.makeWatchRequest();
    },
    async deactivate() {
      await this.stopWatchRequest();
    },
  },
  methods: {
    setNextSyncToken(calendarId, nextSyncToken) {
      this.db.set(`${calendarId}.nextSyncToken`, nextSyncToken);
    },
    getNextSyncToken(calendarId) {
      return this.db.get(`${calendarId}.nextSyncToken`);
    },
    setChannelId(calendarId, channelId) {
      this.db.set(`${calendarId}.channelId`, channelId);
    },
    getChannelId(calendarId) {
      return this.db.get(`${calendarId}.channelId`);
    },
    setResourceId(calendarId, resourceId) {
      this.db.set(`${calendarId}.resourceId`, resourceId);
    },
    getResourceId(calendarId) {
      return this.db.get(`${calendarId}.resourceId`);
    },
    setExpiration(calendarId, expiration) {
      this.db.set(`${calendarId}.expiration`, expiration);
    },
    getExpiration(calendarId) {
      return this.db.get(`${calendarId}.expiration`);
    },
    /**
     * A utility method to compute whether the provided event is newly created
     * or not. Since the Google Calendar API does not provide a specific way to
     * determine this, this method estimates the result based on the `created`
     * and `updated` timestamps: if they are more than 2 seconds apart, then we
     * assume that the event is not new.
     *
     * @param {Object} event - The calendar event being processed
     * @returns {Boolean} True if the input event is a newly created event, or
     * false otherwise
     */
    _isNewEvent(event) {
      const {
        created,
        updated,
      } = event;
      const createdTimestampMilliseconds = Date.parse(created);
      const updatedTimestampMilliseconds = Date.parse(updated);
      const diffMilliseconds = Math.abs(
        updatedTimestampMilliseconds - createdTimestampMilliseconds,
      );
      const maxDiffMilliseconds = 2000;
      return diffMilliseconds <= maxDiffMilliseconds;
    },
    /**
     * A utility method to compute whether the provided event is relevant to the
     * event source (and as a consequence must be processed) or not.
     *
     * @param {Object} event - The calendar event being processed
     * @returns {Boolean} True if the input event must be processed, or false
     * otherwise (i.e. if the event must be skipped)
     */
    isEventRelevant(event) {
      return !this.newOnly || this._isNewEvent(event);
    },
    generateMeta(event) {
      const {
        id,
        summary,
        updated: tsString,
      } = event;
      const ts = Date.parse(tsString);
      return {
        id: `${id}-${ts}`,
        summary,
        ts,
      };
    },
    async makeWatchRequest() {
      // Make watch request for this HTTP endpoint
      for (const calendarId of this.calendarIds) {
        const watchResp =
          await this.googleCalendar.watchEvents({
            calendarId,
            requestBody: {
              id: uuid(),
              type: "web_hook",
              address: this.http.endpoint,
            },
          });

        // Initial full sync. Get next sync token
        const nextSyncToken = await this.googleCalendar.fullSync(calendarId);

        this.setNextSyncToken(calendarId, nextSyncToken);
        this.setChannelId(calendarId, watchResp.id);
        this.setResourceId(calendarId, watchResp.resourceId);
        this.setExpiration(calendarId, watchResp.expiration);
      }
    },
    async stopWatchRequest() {
      for (const calendarId of this.calendarIds) {
        const id = this.getChannelId(calendarId);
        const resourceId = this.getResourceId(calendarId);
        if (id && resourceId) {
          const { status } =
            await this.googleCalendar.stopChannel({
              returnOnlyData: false,
              requestBody: {
                id,
                resourceId,
              },
            });
          if (status === 204) {
            console.log("webhook deactivated");
            this.setNextSyncToken(calendarId, null);
            this.setChannelId(calendarId, null);
            this.setResourceId(calendarId, null);
            this.setExpiration(calendarId, null);
          } else {
            console.log("There was a problem deactivating the webhook");
          }
        }
      }
    },
    getSoonestExpirationDate() {
      let min;
      for (const calendarId of this.calendarIds) {
        const expiration = parseInt(this.db.get(`${calendarId}.expiration`));
        if (!min || expiration < min) {
          min = expiration;
        }
      }
      return new Date(min);
    },
    getChannelIds() {
      const channelIds = [];
      for (const calendarId of this.calendarIds) {
        const channelId = this.db.get(`${calendarId}.channelId`);
        channelIds.push(channelId);
      }
      return channelIds;
    },
  },
  async run(event) {
    // refresh watch
    if (event.interval_seconds) {
      // get time
      const now = new Date();
      const intervalMs = event.interval_seconds * 1000;
      // get expiration
      const expireDate = this.getSoonestExpirationDate();

      // if now + interval > expiration, refresh watch
      if (now.getTime() + intervalMs > expireDate.getTime()) {
        await this.stopWatchRequest();
        await this.makeWatchRequest();
      }
    } else {
      // Verify channel ID
      const channelIds = this.getChannelIds();
      const incomingChannelId = event?.headers?.["x-goog-channel-id"];
      if (!channelIds.includes(incomingChannelId)) {
        console.log(
          `Unexpected channel ID ${incomingChannelId}. This likely means there are multiple, older subscriptions active.`,
        );
        return;
      }

      // Check that resource state === exists
      const state = event?.headers?.["x-goog-resource-state"];
      switch (state) {
      case "exists":
        // there's something to emit, so keep going
        break;
      case "not_exists":
        console.log("Resource does not exist. Exiting.");
        return;
      case "sync":
        console.log("New channel created");
        return;
      default:
        console.log(`Unknown state: ${state}`);
        return;
      }
    }

    // Fetch and emit events
    for (const calendarId of this.calendarIds) {
      const syncToken = this.getNextSyncToken(calendarId);
      let nextSyncToken = null;
      let nextPageToken = null;
      while (!nextSyncToken) {
        const {
          data: syncData = {},
          status: syncStatus,
        } = await this.googleCalendar.listEvents({
          returnOnlyData: false,
          calendarId,
          syncToken,
          pageToken: nextPageToken,
        });
        if (syncStatus === 410) {
          console.log("Sync token invalid, resyncing");
          nextSyncToken = await this.googleCalendar.fullSync(this.calendarId);
          break;
        }
        nextPageToken = syncData.nextPageToken;
        nextSyncToken = syncData.nextSyncToken;

        const { items: events = [] } = syncData;
        events
          .filter(this.isEventRelevant, this)
          .forEach((event) => {
            const { status } = event;
            if (status === "cancelled") {
              console.log("Event cancelled. Exiting.");
              return;
            }
            const meta = this.generateMeta(event);
            this.$emit(event, meta);
          });
      }

      this.setNextSyncToken(calendarId, nextSyncToken);
    }
  },
};

Trigger Configuration

This component may be configured based on the props defined in the component code. Pipedream automatically prompts for input values in the UI and CLI.
LabelPropTypeDescription
Google CalendargoogleCalendarappThis component uses the Google Calendar app.
N/Adb$.service.dbThis component uses $.service.db to maintain state between component invocations.
CalendarscalendarIdsstring[]Select a value from the drop down menu.
New events only?newOnlyboolean

Emit new events only, and not updates to existing events

N/Ahttp$.interface.httpThis component uses $.interface.http to generate a unique URL when the component is first instantiated. Each request to the URL will trigger the run() method of the component.
Push notification renewal scheduletimer$.interface.timer

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.

Trigger Authentication

Google Calendar uses OAuth authentication. When you connect your Google Calendar account, Pipedream will open a popup window where you can sign into Google Calendar and grant Pipedream permission to connect to your account. Pipedream securely stores and automatically refreshes the OAuth tokens so you can easily authenticate any Google Calendar API.

Pipedream requests the following authorization scopes when you connect your account:

emailprofilehttps://www.googleapis.com/auth/calendar.eventshttps://www.googleapis.com/auth/calendar.readonlyhttps://www.googleapis.com/auth/calendar.settings.readonly

About Google Calendar

Scheduling calendar service developed by Google

Action

Description:Run any Go code and use any Go package available with a simple import. Refer to the [Pipedream Go docs](https://pipedream.com/docs/code/go) to learn more.
Version:0.0.2
Key:go-run-go-code

Action Code

import go from "../../go.app.mjs";

export default {
  name: "Run Go Code",
  version: "0.0.2",
  key: "go-run-go-code",
  description: "Run any Go code and use any Go package available with a simple import. Refer to the [Pipedream Go docs](https://pipedream.com/docs/code/go) to learn more.",
  props: {
    go,
  },
  type: "action",
  methods: {},
  async run({ $ }) {
    // Placeholder action for Go code app
  },
};

Action Configuration

This component may be configured based on the props defined in the component code. Pipedream automatically prompts for input values in the UI.

LabelPropTypeDescription
GogoappThis component uses the Go app.

Action Authentication

The Go API does not require authentication.

About Go

Anything you can do in Go, you can do in a Pipedream Workflow. You can use any of Go packages available with a simple import.

More Ways to Connect Go + Google Calendar

Run Go Code with Go API on Event Cancelled from Google Calendar API
Google Calendar + Go
 
Try it
Run Go Code with Go API on Event Ended from Google Calendar API
Google Calendar + Go
 
Try it
Run Go Code with Go API on Event Search from Google Calendar API
Google Calendar + Go
 
Try it
Run Go Code with Go API on Event Start from Google Calendar API
Google Calendar + Go
 
Try it
Run Go Code with Go API on New Calendar from Google Calendar API
Google Calendar + Go
 
Try it
Event Start from the Google Calendar API

Emits a specified time before an event starts

 
Try it
Upcoming Event Alert from the Google Calendar API

Triggers based on a time interval before an upcoming event in the calendar. This source uses Pipedream's Task Scheduler. See here for more information and instructions for connecting your Pipedream account.

 
Try it
New or Updated Event (Instant) from the Google Calendar API

Emit new calendar events when an event is created or updated (does not emit cancelled events)

 
Try it
New Event from the Google Calendar API

Emits when an event is created

 
Try it
Event Search from the Google Calendar API

Emit when an event is created that matches a search

 
Try it
Add Quick Event with the Google Calendar API

Create an event to the Google Calendar. See the docs here

 
Try it
Create Event with the Google Calendar API

Create an event to the Google Calendar. See the docs here

 
Try it
Delete an Event with the Google Calendar API

Delete an event to the Google Calendar. See the docs here

 
Try it
List calendars from user account with the Google Calendar API

Retrieve calendars from the user account. See the docs here

 
Try it
List Events with the Google Calendar API

Retrieve a list of event from the Google Calendar. See the docs here

 
Try it