import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getGoogleCalendarApi, normalizeCalendar, normalizeCalendarPage, resolveCalendarId, } from "@automate.ax/integration-contracts/google-calendar" /** Calendar-list controls constrained during incremental synchronization. */ type CalendarListSyncInput = { includeDeleted?: boolean includeHidden?: boolean minAccessRole?: string showOwnOrganizationOnly?: boolean syncToken?: string } import { GOOGLE_CALENDAR_CLEAR_REQUIREMENT, GOOGLE_CALENDAR_CREATE_REQUIREMENT, GOOGLE_CALENDAR_LIST_READ_REQUIREMENT, GOOGLE_CALENDAR_LIST_WRITE_REQUIREMENT, GOOGLE_CALENDAR_REFERENCE_LIST_ENTRY_READ_REQUIREMENT, GOOGLE_CALENDAR_REFERENCE_LIST_ENTRY_WRITE_REQUIREMENT, GOOGLE_CALENDAR_REFERENCE_MANAGE_REQUIREMENT, GOOGLE_CALENDAR_REFERENCE_METADATA_REQUIREMENT, } from "../lib/scopes" import { CALENDAR_ACCESS_ROLE_SCHEMA, CALENDAR_PAGE_SCHEMA, CALENDAR_NOTIFICATION_SCHEMA, CALENDAR_REFERENCE_SCHEMA, CALENDAR_SCHEMA, REMINDER_SCHEMA, } from "@automate.ax/integration-contracts/google-calendar" /** Gets metadata for any accessible calendar. */ export const getGoogleCalendar = defineAction("Get Google calendar") .describe("Gets normalized metadata for an accessible calendar.") .account("google", GOOGLE_CALENDAR_REFERENCE_METADATA_REQUIREMENT) .input( z.object({ /** Exact calendar title or ID. Defaults to the primary calendar. */ calendar: CALENDAR_REFERENCE_SCHEMA.optional(), }), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const calendarId = await resolveCalendarId(calendarApi, input.calendar) const { data } = await calendarApi.calendars.get({ calendarId, }) return normalizeCalendar(data, calendarId) }) /** Creates a new secondary calendar owned by the authenticated user. */ export const createGoogleCalendar = defineAction("Create Google calendar") .describe("Creates a secondary calendar owned by the authenticated user.") .account("google", GOOGLE_CALENDAR_CREATE_REQUIREMENT) .input( z.object({ /** Human-readable calendar description. */ description: z.string().optional(), /** Free-form geographic location. */ location: z.string().optional(), /** Human-readable calendar title. */ summary: z.string().trim().min(1), /** IANA calendar timezone. */ timeZone: z.string().min(1).optional(), }), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { data } = await getGoogleCalendarApi( account.secret, ).calendars.insert({ requestBody: input, }) return normalizeCalendar(data) }) /** Updates selected metadata on an owned calendar. */ export const updateGoogleCalendar = defineAction("Update Google calendar") .describe("Updates a calendar's title, description, location, or timezone.") .account("google", GOOGLE_CALENDAR_REFERENCE_MANAGE_REQUIREMENT) .input( z .object({ /** Exact calendar title or ID. Defaults to the primary calendar. */ calendar: CALENDAR_REFERENCE_SCHEMA.optional(), /** Replacement calendar description. */ description: z.string().optional(), /** Replacement geographic location. */ location: z.string().optional(), /** Replacement calendar title. */ summary: z.string().trim().min(1).optional(), /** Replacement IANA calendar timezone. */ timeZone: z.string().min(1).optional(), }) .refine( (input) => [ input.description, input.location, input.summary, input.timeZone, ].some((value) => value !== undefined), "At least one calendar property must be updated.", ), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const calendarId = await resolveCalendarId(calendarApi, input.calendar) const { data } = await calendarApi.calendars.patch({ calendarId, requestBody: { description: input.description, location: input.location, summary: input.summary, timeZone: input.timeZone, }, }) return normalizeCalendar(data, calendarId) }) /** Clears all events from the authenticated user's primary calendar. */ export const clearPrimaryGoogleCalendar = defineAction( "Clear primary Google calendar", ) .describe("Permanently removes every event from the primary calendar.") .account("google", GOOGLE_CALENDAR_CLEAR_REQUIREMENT) .input(z.object({})) .output(z.literal("primary")) .retry({ replaySafety: "unsafe" }) .handler(async ({ account }) => { await getGoogleCalendarApi(account.secret).calendars.clear({ calendarId: "primary", }) return "primary" as const }) /** Deletes an owned secondary calendar and returns its ID. */ export const deleteGoogleCalendar = defineAction("Delete Google calendar") .describe("Permanently deletes an owned secondary calendar.") .account("google", GOOGLE_CALENDAR_REFERENCE_MANAGE_REQUIREMENT) .input(z.object({ calendar: CALENDAR_REFERENCE_SCHEMA })) .output(z.string()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const calendarId = await resolveCalendarId(calendarApi, input.calendar) await calendarApi.calendars.delete({ calendarId, }) return calendarId }) /** Lists one page of calendars visible to the authenticated user. */ export const listGoogleCalendars = defineAction("List Google calendars") .describe("Lists visible calendars with pagination and sync tokens.") .account("google", GOOGLE_CALENDAR_LIST_READ_REQUIREMENT) .input( z .object({ /** Include calendars removed from the user's list. */ includeDeleted: z.boolean().optional(), /** Include hidden calendar-list entries. */ includeHidden: z.boolean().optional(), /** Minimum access role required for returned calendars. */ minAccessRole: CALENDAR_ACCESS_ROLE_SCHEMA.optional(), /** Maximum calendars returned in this page. */ maxResults: z.number().int().min(1).max(250).optional(), /** Pagination token returned by a previous request. */ pageToken: z.string().min(1).optional(), /** Return only calendars owned by the connected Workspace organization. */ showOwnOrganizationOnly: z.boolean().optional(), /** Incremental synchronization token from a previous completed listing. */ syncToken: z.string().min(1).optional(), }) .refine( calendarListSyncInputIsValid, "syncToken cannot be combined with access or organization filters, includeDeleted: false, or includeHidden: false.", ), ) .output(CALENDAR_PAGE_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { data } = await getGoogleCalendarApi( account.secret, ).calendarList.list({ maxResults: input.maxResults, minAccessRole: input.minAccessRole, pageToken: input.pageToken, showDeleted: input.includeDeleted, showHidden: input.includeHidden, showOwnOrganizationOnly: input.showOwnOrganizationOnly, syncToken: input.syncToken, }) return normalizeCalendarPage(data) }) /** Gets one user-specific calendar-list entry. */ export const getGoogleCalendarListEntry = defineAction( "Get Google calendar-list entry", ) .describe("Gets display and subscription settings for one calendar.") .account("google", GOOGLE_CALENDAR_REFERENCE_LIST_ENTRY_READ_REQUIREMENT) .input( z.object({ /** Exact calendar title or ID. */ calendar: CALENDAR_REFERENCE_SCHEMA, }), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const { data } = await calendarApi.calendarList.get({ calendarId: await resolveCalendarId(calendarApi, input.calendar), }) return normalizeCalendar(data) }) /** Adds an existing accessible calendar to the user's calendar list. */ export const addGoogleCalendarToCalendarList = defineAction( "Add Google calendar to calendar list", ) .describe("Subscribes the authenticated user to an accessible calendar.") .account("google", GOOGLE_CALENDAR_LIST_WRITE_REQUIREMENT) .input( z.object({ /** Calendar background color in `#RRGGBB` form. */ backgroundColor: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), /** Accessible calendar ID. The calendar need not be subscribed yet. */ calendar: CALENDAR_REFERENCE_SCHEMA, /** Calendar foreground color in `#RRGGBB` form. */ foregroundColor: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), /** Whether the calendar should be hidden from the list. */ hidden: z.boolean().optional(), /** Whether calendar events should appear in the UI. */ selected: z.boolean().optional(), /** Email notifications for calendar changes and agendas. */ notifications: CALENDAR_NOTIFICATION_SCHEMA.array().optional(), /** User-specific title override. */ summaryOverride: z.string().optional(), }), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { data } = await getGoogleCalendarApi( account.secret, ).calendarList.insert({ colorRgbFormat: Boolean(input.backgroundColor || input.foregroundColor), requestBody: { backgroundColor: input.backgroundColor, foregroundColor: input.foregroundColor, hidden: input.hidden, id: input.calendar, notificationSettings: input.notifications ? { notifications: input.notifications.map(({ type }) => ({ method: "email", type, })), } : undefined, selected: input.selected, summaryOverride: input.summaryOverride, }, }) return normalizeCalendar(data) }) /** Updates user-specific display and reminder settings for a calendar. */ export const updateGoogleCalendarListEntry = defineAction( "Update Google calendar-list entry", ) .describe("Updates a calendar's user-specific list settings.") .account("google", GOOGLE_CALENDAR_REFERENCE_LIST_ENTRY_WRITE_REQUIREMENT) .input( z .object({ /** Calendar background color in `#RRGGBB` form. */ backgroundColor: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), /** Exact calendar title or ID. */ calendar: CALENDAR_REFERENCE_SCHEMA, /** User-specific default event reminders. */ defaultReminders: REMINDER_SCHEMA.array().max(5).optional(), /** Calendar foreground color in `#RRGGBB` form. */ foregroundColor: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), /** Whether the calendar should be hidden from the list. */ hidden: z.boolean().optional(), /** Whether calendar events should appear in the UI. */ selected: z.boolean().optional(), /** Replacement email notification configuration. */ notifications: CALENDAR_NOTIFICATION_SCHEMA.array().optional(), /** User-specific title override. */ summaryOverride: z.string().optional(), }) .refine( (input) => [ input.backgroundColor, input.defaultReminders, input.foregroundColor, input.hidden, input.notifications, input.selected, input.summaryOverride, ].some((value) => value !== undefined), "At least one calendar-list property must be updated.", ), ) .output(CALENDAR_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const { data } = await calendarApi.calendarList.patch({ calendarId: await resolveCalendarId(calendarApi, input.calendar), colorRgbFormat: Boolean(input.backgroundColor || input.foregroundColor), requestBody: { backgroundColor: input.backgroundColor, defaultReminders: input.defaultReminders, foregroundColor: input.foregroundColor, hidden: input.hidden, notificationSettings: input.notifications ? { notifications: input.notifications.map(({ type }) => ({ method: "email", type, })), } : undefined, selected: input.selected, summaryOverride: input.summaryOverride, }, }) return normalizeCalendar(data) }) /** Removes a calendar from the authenticated user's calendar list. */ export const removeGoogleCalendarFromCalendarList = defineAction( "Remove Google calendar from calendar list", ) .describe("Unsubscribes from a calendar without deleting the calendar.") .account("google", GOOGLE_CALENDAR_REFERENCE_LIST_ENTRY_WRITE_REQUIREMENT) .input( z.object({ /** Exact calendar title or ID. */ calendar: CALENDAR_REFERENCE_SCHEMA, }), ) .output(z.string()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const calendarApi = getGoogleCalendarApi(account.secret) const calendarId = await resolveCalendarId(calendarApi, input.calendar) await calendarApi.calendarList.delete({ calendarId, }) return calendarId }) /** * Checks whether filters are compatible with incremental calendar-list sync. * * @param input - Calendar-list controls. */ function calendarListSyncInputIsValid(input: CalendarListSyncInput) { return ( !input.syncToken || (input.includeDeleted !== false && input.includeHidden !== false && input.minAccessRole === undefined && input.showOwnOrganizationOnly === undefined) ) }