import * as z from "zod" import { defineAction } from "../../../automation/actions" import { EMAIL_ORIGIN_TOKEN } from "../../../lib/email-origin" import { buildRawMessage, getGmailApi, getGmailMessage, toDraftIdentifier, } from "@automate.ax/integration-contracts/gmail" import { GMAIL_COMPOSE_AND_READ_REQUIREMENT } from "../lib/scopes" import { COMPOSE_OPTIONS_SCHEMA, DRAFT_IDENTIFIER_SCHEMA, } from "@automate.ax/integration-contracts/gmail" /** * Updates a Gmail draft while preserving fields omitted by the caller. * * Supplying either body representation replaces the existing body pair; missing * plain text is then derived from replacement HTML. */ export const updateGmailDraft = defineAction("Update Gmail draft") .describe("Updates selected fields on an existing Gmail draft.") .account("google", GMAIL_COMPOSE_AND_READ_REQUIREMENT) .input( COMPOSE_OPTIONS_SCHEMA.extend({ /** Immutable Gmail draft ID to replace. */ draftId: z.string().min(1), /** Optional HTML draft body. */ html: z.string().optional(), /** Optional plain-text draft body. */ text: z.string().optional(), }), ) .output(DRAFT_IDENTIFIER_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input, runtime }) => { const gmailApi = getGmailApi(account.secret) const { data: existingDraft } = await gmailApi.users.drafts.get({ format: "raw", id: input.draftId, userId: "me", }) if (!existingDraft.message) { throw new Error("Gmail returned an incomplete draft.") } const existing = await getGmailMessage(existingDraft.message) const bodyChanged = input.html !== undefined || input.text !== undefined const { data: updatedDraft } = await gmailApi.users.drafts.update({ id: input.draftId, requestBody: { message: { raw: await buildRawMessage({ automationOriginToken: runtime[EMAIL_ORIGIN_TOKEN], attachments: input.attachments ?? existing.attachments.map( ({ contentId, disposition, file, filename }) => ({ contentId, disposition, file, filename, }), ), bcc: input.bcc ?? existing.bcc, cc: input.cc ?? existing.cc, from: input.from ?? existing.from, headers: input.headers, html: bodyChanged ? input.html : existing.html, inReplyTo: existing.inReplyTo, priority: input.priority, references: existing.references, replyTo: input.replyTo ?? existing.replyTo, subject: input.subject ?? existing.subject, text: bodyChanged ? input.text : existing.text, to: input.to ?? existing.to, }), threadId: existing.threadId, }, }, userId: "me", }) return toDraftIdentifier(updatedDraft, "updated draft") })