Skip to content

Commit 2409c98

Browse files
feat(asana): expand provider to 101 core REST actions (#215)
## Summary - expand the Asana provider from its original small action set to 101 core collaboration REST actions across workspaces, users, teams, projects, sections, tasks, stories/comments, tags, custom fields/settings, and attachments - add a shared typed Asana runtime with consistent pagination, error mapping, request validation, and injected SSRF-guarded egress - complete advanced project/task search filters, asynchronous duplicate job responses, date and placement constraints, and official provider scopes - support safe external and transit-file attachments with public URL validation, multipart uploads, encoded filenames, and the 100 MiB limit - keep provider loading lazy and split definitions/runtime by resource responsibility Reference: https://developers.asana.com/reference/rest-api-reference ## Verification - `npm run fix-check` - `npx vitest run src/providers/asana/runtime.test.ts` — 259 passed - `npm test` — 59 files, 825 tests passed - `npm run build` - catalog generation — 1129 providers, 12254 actions - verified 101 unique Asana definitions match 101 executors - built and smoke-tested the Docker image locally; health passed and the live catalog exposed all 101 Asana actions ## Notes - specialized and enterprise-oriented Asana resource families remain out of scope - the provider continues to use Personal Access Token authentication; OAuth-only attachment app linking is intentionally not exposed --------- Co-authored-by: Kevin Cui <bh@bugs.cc>
1 parent 97ca60a commit 2409c98

21 files changed

Lines changed: 8796 additions & 783 deletions
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import type { ActionDefinition, JsonSchema } from "../../core/types.ts";
2+
3+
import { s } from "../../core/json-schema.ts";
4+
import { defineProviderAction } from "../../core/provider-definition.ts";
5+
import {
6+
attachmentSchema,
7+
gidField,
8+
includeFieldsSchema,
9+
nextCursorSchema,
10+
paginationFields,
11+
successOutputSchema,
12+
} from "./schemas.ts";
13+
14+
const service = "asana";
15+
16+
const attachmentOutputSchema = s.object("The output payload for this action.", {
17+
attachment: attachmentSchema,
18+
});
19+
20+
const attachmentsOutputSchema = s.object("The output payload for this action.", {
21+
attachments: s.array("The Asana attachments.", attachmentSchema),
22+
nextCursor: nextCursorSchema,
23+
});
24+
25+
function resourceInput(idField: string, description: string): JsonSchema {
26+
return s.object(
27+
"The input payload for this action.",
28+
{
29+
[idField]: gidField(description),
30+
includeFields: includeFieldsSchema,
31+
},
32+
{ required: [idField] },
33+
);
34+
}
35+
36+
const createAttachmentInputSchema = s.object(
37+
"The input payload for this action.",
38+
{
39+
parentId: gidField("The parent task, project, or project brief gid."),
40+
fileId: s.nonEmptyString("The local transit file identifier to upload."),
41+
externalUrl: s.url("The public URL of an external resource to attach."),
42+
name: s.nonEmptyString("The attachment name required for an external resource."),
43+
includeFields: includeFieldsSchema,
44+
},
45+
{ required: ["parentId"] },
46+
);
47+
createAttachmentInputSchema.oneOf = [
48+
{
49+
required: ["fileId"],
50+
not: { anyOf: [{ required: ["externalUrl"] }, { required: ["name"] }] },
51+
},
52+
{
53+
required: ["externalUrl", "name"],
54+
not: { required: ["fileId"] },
55+
},
56+
];
57+
58+
export const asanaAttachmentActions: ActionDefinition[] = [
59+
defineProviderAction(service, {
60+
name: "get_attachment",
61+
description: "Get an Asana attachment by gid.",
62+
requiredScopes: ["attachments:read"],
63+
inputSchema: resourceInput("attachmentId", "The attachment gid."),
64+
outputSchema: attachmentOutputSchema,
65+
}),
66+
defineProviderAction(service, {
67+
name: "delete_attachment",
68+
description: "Delete an Asana attachment.",
69+
requiredScopes: ["attachments:delete"],
70+
inputSchema: s.object(
71+
"The input payload for this action.",
72+
{ attachmentId: gidField("The attachment gid.") },
73+
{ required: ["attachmentId"] },
74+
),
75+
outputSchema: successOutputSchema,
76+
}),
77+
defineProviderAction(service, {
78+
name: "list_attachments",
79+
description: "List attachments on an Asana task, project, or project brief.",
80+
requiredScopes: ["attachments:read"],
81+
inputSchema: s.object(
82+
"The input payload for this action.",
83+
{
84+
parentId: gidField("The parent task, project, or project brief gid."),
85+
...paginationFields,
86+
includeFields: includeFieldsSchema,
87+
},
88+
{ required: ["parentId"] },
89+
),
90+
outputSchema: attachmentsOutputSchema,
91+
}),
92+
defineProviderAction(service, {
93+
name: "create_attachment",
94+
description: "Attach a public external URL or local transit file to an Asana object.",
95+
requiredScopes: ["attachments:write"],
96+
inputSchema: createAttachmentInputSchema,
97+
outputSchema: attachmentOutputSchema,
98+
}),
99+
];

0 commit comments

Comments
 (0)