import * as z from "zod" import { defineAction } from "../../../automation/actions" import { TRELLO_READ_SCOPE, TRELLO_WRITE_SCOPE } from "./scopes" import { getTrelloApi, normalizeTrelloBoardId, } from "@automate.ax/integration-contracts/trello" import { TRELLO_BOARD_SCHEMA, TRELLO_LABEL_SCHEMA, TRELLO_LIST_SCHEMA, TRELLO_MEMBER_SCHEMA, TRELLO_PROVIDER_BOARD_SCHEMA, TRELLO_PROVIDER_LABEL_SCHEMA, TRELLO_PROVIDER_LIST_SCHEMA, TRELLO_PROVIDER_MEMBER_SCHEMA, toTrelloBoard, toTrelloLabel, toTrelloList, toTrelloMember, } from "@automate.ax/integration-contracts/trello" const TRELLO_ACCOUNT = "trello" const BOARD_FIELDS = "id,name,desc,closed,idOrganization,url,shortUrl,dateLastActivity,starred,subscribed" const BOARD_FILTER_SCHEMA = z.enum(["open", "closed", "all"]) const POSITION_SCHEMA = z.union([ z.enum(["top", "bottom"]), z.number().nonnegative(), ]) /** Retrieves the Trello member connected to this account. */ export const getCurrentTrelloMember = defineAction("Get current Trello member") .describe("Retrieves the profile for the connected Trello member.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input(z.object({})) .output(TRELLO_MEMBER_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account }) => toTrelloMember( await getTrelloApi(account.secret).request("members/me", { query: { fields: "id,fullName,username,initials,avatarUrl,url" }, responseSchema: TRELLO_PROVIDER_MEMBER_SCHEMA, }), ), ) /** Lists boards visible to the connected Trello member. */ export const listTrelloBoards = defineAction("List Trello boards") .describe("Lists boards visible to the connected Trello member.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input( z.object({ /** Whether to return open, archived, or all boards. */ filter: BOARD_FILTER_SCHEMA.prefault("open"), /** Workspace ID used to limit returned boards. */ organizationId: z.string().trim().min(1).optional(), }), ) .output(TRELLO_BOARD_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getTrelloApi(account.secret).request( input.organizationId ? `organizations/${encodeURIComponent(input.organizationId)}/boards` : "members/me/boards", { query: { fields: BOARD_FIELDS, filter: input.filter, lists: "none", }, responseSchema: TRELLO_PROVIDER_BOARD_SCHEMA.array(), }, ) ).map(toTrelloBoard), ) /** Retrieves one Trello board by ID, short link, or URL. */ export const getTrelloBoard = defineAction("Get Trello board") .describe("Retrieves one board by ID, short link, or Trello URL.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), }), ) .output(TRELLO_BOARD_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toTrelloBoard( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}`, { query: { fields: BOARD_FIELDS }, responseSchema: TRELLO_PROVIDER_BOARD_SCHEMA, }, ), ), ) /** Creates a Trello board with code-first preference controls. */ export const createTrelloBoard = defineAction("Create Trello board") .describe("Creates a board, optionally in a workspace or from a template.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z.object({ /** Board description. */ description: z.string().optional(), /** Whether Trello should create its default lists. */ defaultLists: z.boolean().prefault(true), /** Source board ID or URL to copy. */ sourceBoard: z.string().trim().min(1).optional(), /** Source-board content to retain. */ keepFromSource: z.enum(["cards", "none"]).optional(), /** Human-readable board name. */ name: z.string().trim().min(1), /** Board visibility. */ permissionLevel: z.enum(["org", "private", "public"]).prefault("private"), /** Whether board members may join without an invitation. */ selfJoin: z.boolean().optional(), /** Workspace ID that should own the board. */ organizationId: z.string().trim().min(1).optional(), }), ) .output(TRELLO_BOARD_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toTrelloBoard( await getTrelloApi(account.secret).request("boards", { method: "POST", query: { defaultLists: input.defaultLists, desc: input.description, idBoardSource: input.sourceBoard ? normalizeTrelloBoardId(input.sourceBoard) : undefined, idOrganization: input.organizationId, keepFromSource: input.keepFromSource, name: input.name, prefs_permissionLevel: input.permissionLevel, prefs_selfJoin: input.selfJoin, }, responseSchema: TRELLO_PROVIDER_BOARD_SCHEMA, }), ), ) /** Updates mutable Trello board fields and preferences. */ export const updateTrelloBoard = defineAction("Update Trello board") .describe("Updates board metadata, visibility, workspace, or watch state.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z .object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), /** Archive or reopen the board. */ closed: z.boolean().optional(), /** Board description. */ description: z.string().optional(), /** New board name. */ name: z.string().trim().min(1).optional(), /** Move the board to this workspace. */ organizationId: z.string().trim().min(1).optional(), /** Board visibility. */ permissionLevel: z.enum(["org", "private", "public"]).optional(), /** Whether board members may join without an invitation. */ selfJoin: z.boolean().optional(), /** Whether the connected member watches the board. */ subscribed: z.boolean().optional(), }) .refine( (input) => input.closed !== undefined || input.description !== undefined || input.name !== undefined || input.organizationId !== undefined || input.permissionLevel !== undefined || input.selfJoin !== undefined || input.subscribed !== undefined, "Provide at least one board field to update.", ), ) .output(TRELLO_BOARD_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toTrelloBoard( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}`, { method: "PUT", query: { closed: input.closed, desc: input.description, idOrganization: input.organizationId, name: input.name, "prefs/permissionLevel": input.permissionLevel, "prefs/selfJoin": input.selfJoin, subscribed: input.subscribed, }, responseSchema: TRELLO_PROVIDER_BOARD_SCHEMA, }, ), ), ) /** Permanently deletes a Trello board. */ export const deleteTrelloBoard = defineAction("Delete Trello board") .describe("Permanently deletes a Trello board.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), }), ) .output(z.object({ boardId: z.string(), deleted: z.literal(true) })) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const boardId = normalizeTrelloBoardId(input.board) await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(boardId)}`, { method: "DELETE", responseSchema: z.unknown() }, ) return { boardId, deleted: true as const } }) /** Lists lists on a Trello board. */ export const listTrelloBoardLists = defineAction("List Trello board lists") .describe("Lists open, archived, or all lists on a board.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), /** Whether to return open, archived, or all lists. */ filter: BOARD_FILTER_SCHEMA.prefault("open"), }), ) .output(TRELLO_LIST_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}/lists`, { query: { fields: "id,name,closed,idBoard,pos,subscribed", filter: input.filter, }, responseSchema: TRELLO_PROVIDER_LIST_SCHEMA.array(), }, ) ).map(toTrelloList), ) /** Lists members of a Trello board. */ export const listTrelloBoardMembers = defineAction("List Trello board members") .describe("Lists members who belong to a Trello board.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input(z.object({ board: z.string().trim().min(1) })) .output(TRELLO_MEMBER_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}/members`, { query: { fields: "id,fullName,username,initials,avatarUrl,url" }, responseSchema: TRELLO_PROVIDER_MEMBER_SCHEMA.array(), }, ) ).map(toTrelloMember), ) /** Lists labels defined on a Trello board. */ export const listTrelloBoardLabels = defineAction("List Trello board labels") .describe("Lists labels defined on a Trello board.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), /** Maximum number of labels to return. */ limit: z.number().int().min(1).max(1000).prefault(100), }), ) .output(TRELLO_LABEL_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}/labels`, { query: { fields: "id,idBoard,name,color", limit: input.limit }, responseSchema: TRELLO_PROVIDER_LABEL_SCHEMA.array(), }, ) ).map(toTrelloLabel), ) /** Creates a reusable label on a Trello board. */ export const createTrelloBoardLabel = defineAction("Create Trello board label") .describe("Creates a reusable label on a Trello board.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), /** Trello label color, or null for a colorless label. */ color: z .enum([ "black", "blue", "green", "lime", "orange", "pink", "purple", "red", "sky", "yellow", ]) .nullable(), /** Label name. */ name: z.string().trim().min(1), }), ) .output(TRELLO_LABEL_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toTrelloLabel( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}/labels`, { method: "POST", query: { color: input.color ?? "null", name: input.name }, responseSchema: TRELLO_PROVIDER_LABEL_SCHEMA, }, ), ), ) /** Creates a list on a Trello board. */ export const createTrelloList = defineAction("Create Trello list") .describe("Creates a list at a chosen position on a Trello board.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z.object({ /** Board ID, short link, or standard Trello board URL. */ board: z.string().trim().min(1), /** List name. */ name: z.string().trim().min(1), /** Initial list position. */ position: POSITION_SCHEMA.prefault("bottom"), }), ) .output(TRELLO_LIST_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toTrelloList( await getTrelloApi(account.secret).request( `boards/${encodeURIComponent(normalizeTrelloBoardId(input.board))}/lists`, { method: "POST", query: { name: input.name, pos: input.position }, responseSchema: TRELLO_PROVIDER_LIST_SCHEMA, }, ), ), ) /** Retrieves one Trello list. */ export const getTrelloList = defineAction("Get Trello list") .describe("Retrieves one Trello list by ID.") .account(TRELLO_ACCOUNT, TRELLO_READ_SCOPE) .input(z.object({ listId: z.string().trim().min(1) })) .output(TRELLO_LIST_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toTrelloList( await getTrelloApi(account.secret).request( `lists/${encodeURIComponent(input.listId)}`, { query: { fields: "id,name,closed,idBoard,pos,subscribed" }, responseSchema: TRELLO_PROVIDER_LIST_SCHEMA, }, ), ), ) /** Updates, archives, moves, or reorders a Trello list. */ export const updateTrelloList = defineAction("Update Trello list") .describe("Updates, archives, moves, or reorders a Trello list.") .account(TRELLO_ACCOUNT, TRELLO_WRITE_SCOPE) .input( z .object({ /** Move the list to this board. */ boardId: z.string().trim().min(1).optional(), /** Archive or reopen the list. */ closed: z.boolean().optional(), /** Stable Trello list ID. */ listId: z.string().trim().min(1), /** New list name. */ name: z.string().trim().min(1).optional(), /** New list position. */ position: POSITION_SCHEMA.optional(), /** Whether the connected member watches the list. */ subscribed: z.boolean().optional(), }) .refine( (input) => input.boardId !== undefined || input.closed !== undefined || input.name !== undefined || input.position !== undefined || input.subscribed !== undefined, "Provide at least one list field to update.", ), ) .output(TRELLO_LIST_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toTrelloList( await getTrelloApi(account.secret).request( `lists/${encodeURIComponent(input.listId)}`, { method: "PUT", query: { closed: input.closed, idBoard: input.boardId, name: input.name, pos: input.position, subscribed: input.subscribed, }, responseSchema: TRELLO_PROVIDER_LIST_SCHEMA, }, ), ), )