import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getAsanaApi } from "@automate.ax/integration-contracts/asana" import { ASANA_PAGE_INFO_SCHEMA, ASANA_PROJECT_SCHEMA, ASANA_PROVIDER_PAGE_SCHEMA, ASANA_PROVIDER_PROJECT_SCHEMA, toAsanaPageInfo, toAsanaProject, } from "@automate.ax/integration-contracts/asana" import { ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE } from "./scopes" const PROJECT_FIELDS = "gid,name,resource_type,archived,color,created_at,default_view,due_on,html_notes,modified_at,notes,permalink_url,public,start_on,team.gid,team.name,team.resource_type,workspace.gid,workspace.name,workspace.resource_type" const PROJECT_RESPONSE_SCHEMA = z.looseObject({ data: ASANA_PROVIDER_PROJECT_SCHEMA, }) const PROJECT_PAGE_RESPONSE_SCHEMA = ASANA_PROVIDER_PAGE_SCHEMA.extend({ data: ASANA_PROVIDER_PROJECT_SCHEMA.array(), }) const PROJECT_MUTATION_SCHEMA = z.object({ /** Whether the project is archived. */ archived: z.boolean().optional(), /** Asana project color or `none`. */ color: z.string().min(1).optional(), /** Project due date. */ dueOn: z.iso.date().nullable().optional(), /** Rich-text project description. Mutually exclusive with `notes`. */ htmlNotes: z.string().optional(), /** Plain-text project description. Mutually exclusive with `htmlNotes`. */ notes: z.string().optional(), /** Whether the project is public to its organization or team. */ public: z.boolean().optional(), /** Project start date. */ startOn: z.iso.date().nullable().optional(), }) /** Lists projects in an Asana workspace. */ export const listAsanaProjects = defineAction("List Asana projects") .describe("Lists projects visible in an Asana workspace or organization.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( z.object({ /** Include archived projects. */ archived: z.boolean().optional(), /** Maximum projects 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(), /** Workspace or organization GID. */ workspaceId: z.string().min(1), }), ) .output( z.object({ pageInfo: ASANA_PAGE_INFO_SCHEMA, projects: ASANA_PROJECT_SCHEMA.array(), }), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const result = await getAsanaApi(account.secret).call( `/workspaces/${input.workspaceId}/projects`, { query: { archived: input.archived, limit: input.limit, offset: input.offset, opt_fields: PROJECT_FIELDS, }, responseSchema: PROJECT_PAGE_RESPONSE_SCHEMA, }, ) return { pageInfo: toAsanaPageInfo(result), projects: result.data.map(toAsanaProject), } }) /** Gets complete metadata for an Asana project. */ export const getAsanaProject = defineAction("Get Asana project") .describe("Gets complete metadata for an Asana project.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input(z.object({ projectId: z.string().min(1) })) .output(ASANA_PROJECT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toAsanaProject( ( await getAsanaApi(account.secret).call(`/projects/${input.projectId}`, { query: { opt_fields: PROJECT_FIELDS }, responseSchema: PROJECT_RESPONSE_SCHEMA, }) ).data, ), ) /** Creates an Asana project. */ export const createAsanaProject = defineAction("Create Asana project") .describe("Creates a project in an Asana workspace or organization.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( PROJECT_MUTATION_SCHEMA.extend({ /** Project name. */ name: z.string().min(1), /** Team GID. Required when creating in an organization. */ teamId: z.string().min(1).optional(), /** Workspace or organization GID. */ workspaceId: z.string().min(1), }), ) .output(ASANA_PROJECT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toAsanaProject( ( await getAsanaApi(account.secret).call("/projects", { body: { ...(input.archived !== undefined && { archived: input.archived, }), ...(input.color !== undefined && { color: input.color }), ...(input.dueOn !== undefined && { due_on: input.dueOn }), ...(input.htmlNotes !== undefined && { html_notes: input.htmlNotes, }), name: input.name, ...(input.notes !== undefined && { notes: input.notes }), ...(input.public !== undefined && { public: input.public }), ...(input.startOn !== undefined && { start_on: input.startOn }), ...(input.teamId && { team: input.teamId }), workspace: input.workspaceId, }, query: { opt_fields: PROJECT_FIELDS }, responseSchema: PROJECT_RESPONSE_SCHEMA, }) ).data, ), ) /** Updates selected fields on an Asana project. */ export const updateAsanaProject = defineAction("Update Asana project") .describe("Updates selected fields on an Asana project.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input( PROJECT_MUTATION_SCHEMA.extend({ /** New project name. */ name: z.string().min(1).optional(), /** Project GID. */ projectId: z.string().min(1), }), ) .output(ASANA_PROJECT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toAsanaProject( ( await getAsanaApi(account.secret).call(`/projects/${input.projectId}`, { body: { ...(input.archived !== undefined && { archived: input.archived, }), ...(input.color !== undefined && { color: input.color }), ...(input.dueOn !== undefined && { due_on: input.dueOn }), ...(input.htmlNotes !== undefined && { html_notes: input.htmlNotes, }), ...(input.name !== undefined && { name: input.name }), ...(input.notes !== undefined && { notes: input.notes }), ...(input.public !== undefined && { public: input.public }), ...(input.startOn !== undefined && { start_on: input.startOn, }), }, httpMethod: "PUT", query: { opt_fields: PROJECT_FIELDS }, responseSchema: PROJECT_RESPONSE_SCHEMA, }) ).data, ), ) /** Permanently deletes an Asana project. */ export const deleteAsanaProject = defineAction("Delete Asana project") .describe("Permanently deletes an Asana project.") .account(ASANA_ACCOUNT, ASANA_DEFAULT_SCOPE) .input(z.object({ projectId: z.string().min(1) })) .output(z.void()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getAsanaApi(account.secret).call(`/projects/${input.projectId}`, { httpMethod: "DELETE", responseSchema: z.looseObject({ data: z.looseObject({}) }), }) })