import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getAsanaApi } from "@automate.ax/integration-contracts/asana" import { ASANA_PAGE_INFO_SCHEMA, ASANA_PROVIDER_PAGE_SCHEMA, ASANA_PROVIDER_SECTION_SCHEMA, ASANA_SECTION_SCHEMA, toAsanaPageInfo, toAsanaSection, } from "@automate.ax/integration-contracts/asana" import { ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE } from "./scopes" const SECTION_FIELDS = "gid,name,resource_type,created_at,project.gid,project.name,project.resource_type" const SECTION_RESPONSE_SCHEMA = z.looseObject({ data: ASANA_PROVIDER_SECTION_SCHEMA, }) const SECTION_PAGE_RESPONSE_SCHEMA = ASANA_PROVIDER_PAGE_SCHEMA.extend({ data: ASANA_PROVIDER_SECTION_SCHEMA.array(), }) const EMPTY_RESPONSE_SCHEMA = z.looseObject({ data: z.looseObject({}) }) /** Lists sections in an Asana project. */ export const listAsanaSections = defineAction("List Asana sections") .describe("Lists sections in an Asana project in display order.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( z.object({ /** Maximum sections to return. */ limit: z.number().int().min(1).max(100).prefault(100), /** Opaque token returned by the previous page. */ offset: z.string().min(1).optional(), /** Project GID. */ projectId: z.string().min(1), }), ) .output( z.object({ pageInfo: ASANA_PAGE_INFO_SCHEMA, sections: ASANA_SECTION_SCHEMA.array(), }), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const result = await getAsanaApi(account.secret).call( `/projects/${input.projectId}/sections`, { query: { limit: input.limit, offset: input.offset, opt_fields: SECTION_FIELDS, }, responseSchema: SECTION_PAGE_RESPONSE_SCHEMA, }, ) return { pageInfo: toAsanaPageInfo(result), sections: result.data.map(toAsanaSection), } }) /** Creates a section in an Asana project. */ export const createAsanaSection = defineAction("Create Asana section") .describe("Creates a section in an Asana project.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( z.object({ /** Existing section GID after which the new section should be inserted. */ insertAfterSectionId: z.string().min(1).optional(), /** Existing section GID before which the new section should be inserted. */ insertBeforeSectionId: z.string().min(1).optional(), /** Section name. */ name: z.string().min(1), /** Project GID. */ projectId: z.string().min(1), }), ) .output(ASANA_SECTION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toAsanaSection( ( await getAsanaApi(account.secret).call( `/projects/${input.projectId}/sections`, { body: { ...(input.insertAfterSectionId && { insert_after: input.insertAfterSectionId, }), ...(input.insertBeforeSectionId && { insert_before: input.insertBeforeSectionId, }), name: input.name, }, query: { opt_fields: SECTION_FIELDS }, responseSchema: SECTION_RESPONSE_SCHEMA, }, ) ).data, ), ) /** Renames an Asana section. */ export const updateAsanaSection = defineAction("Update Asana section") .describe("Renames an Asana section.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( z.object({ /** New section name. */ name: z.string().min(1), /** Section GID. */ sectionId: z.string().min(1), }), ) .output(ASANA_SECTION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toAsanaSection( ( await getAsanaApi(account.secret).call(`/sections/${input.sectionId}`, { body: { name: input.name }, httpMethod: "PUT", query: { opt_fields: SECTION_FIELDS }, responseSchema: SECTION_RESPONSE_SCHEMA, }) ).data, ), ) /** Moves a task into a section and optionally positions it. */ export const moveAsanaTaskToSection = defineAction("Move Asana task to section") .describe("Moves a task into a project section and optionally positions it.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( z.object({ /** Task GID after which to insert the moved task. */ insertAfterTaskId: z.string().min(1).optional(), /** Task GID before which to insert the moved task. */ insertBeforeTaskId: z.string().min(1).optional(), /** Destination section GID. */ sectionId: z.string().min(1), /** Task GID to move. */ taskId: z.string().min(1), }), ) .output(z.void()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getAsanaApi(account.secret).call( `/sections/${input.sectionId}/addTask`, { body: { ...(input.insertAfterTaskId && { insert_after: input.insertAfterTaskId, }), ...(input.insertBeforeTaskId && { insert_before: input.insertBeforeTaskId, }), task: input.taskId, }, responseSchema: EMPTY_RESPONSE_SCHEMA, }, ) }) /** Permanently deletes an Asana section. */ export const deleteAsanaSection = defineAction("Delete Asana section") .describe("Permanently deletes an Asana section without deleting its tasks.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input(z.object({ sectionId: z.string().min(1) })) .output(z.void()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getAsanaApi(account.secret).call(`/sections/${input.sectionId}`, { httpMethod: "DELETE", responseSchema: EMPTY_RESPONSE_SCHEMA, }) })