feat(asana): add Asana project management API emulator#4
Conversation
Port the Asana emulator from the amondnet/minsu-lee/asana branch, adapted to this repo's conventions: package @pleaseai/emulate-asana, bun test, tsgo type-check, @emulators/core ^0.6.0 (Hono/Context imported from core instead of a direct hono dependency), and the repo lint style. Covers users, workspaces, teams, projects, sections, tasks, tags, stories, and webhooks over the Asana REST API v1.0. Registered in the emulate service registry (default port 4005) with seed config support.
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| BestPractice | 38 high |
| ErrorProne | 41 high |
| Security | 1 high |
🟢 Metrics 561 complexity · 108 duplication
Metric Results Complexity 561 Duplication 108
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
|
@cubic-dev-ai review |
There was a problem hiding this comment.
Pull Request Overview
The Asana API emulator successfully implements the core stateful CRUD operations and seeding capabilities required by the specification. However, the current implementation is not up to repository standards according to Codacy, primarily due to 74 new issues and 108 code clones.
Critical logic gaps exist in packages/asana/src/routes/tasks.ts and projects.ts, where mandatory API filters are not enforced, and the 'me' keyword for assignees is not resolved to a valid GID. Additionally, the taskRoutes function has reached a complexity level (172) that makes it a high-risk file for future maintenance. These structural and validation issues should be addressed before merging to ensure the emulator behaves consistently with the real Asana API.
About this PR
- There is a systemic pattern of boilerplate duplication across all route handlers, particularly regarding fetching entities by GID and returning 404 responses. This has resulted in over 100 detected clones. Implementing a middleware or a higher-order helper function for entity resolution would significantly improve maintainability.
Test suggestions
- Found recommended test scenario: GET /users/me returns authenticated user details and workspace list
- Found recommended test scenario: POST /projects creates a project with required workspace and name validation
- Found recommended test scenario: GET /projects/:gid/task_counts returns computed task metrics
- Found recommended test scenario: POST /tasks creates a task and supports project/membership inference
- Found recommended test scenario: Task relationship management: add/remove projects, tags, and dependencies
- Found recommended test scenario: Subtasks: creating and listing children of a parent task
- Found recommended test scenario: Stories: adding comments to tasks and verifying editable flags
- Found recommended test scenario: Webhooks: standard CRUD operations (Create, Read, Update, Delete)
- Found recommended test scenario: Seeder: Verify all resource types (workspaces, projects, teams, tasks, etc.) populate from config
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
| } from '../helpers.js' | ||
| import { getAsanaStore } from '../store.js' | ||
|
|
||
| export function taskRoutes({ app, store, baseUrl }: RouteContext): void { |
There was a problem hiding this comment.
🔴 HIGH RISK
Suggestion: The taskRoutes function is overly complex. Refactor this by extracting the logic for each route (GET, POST, PUT, DELETE, etc.) into separate handler functions or a dedicated controller class. This will reduce the file's cognitive load and improve testability.
Try running the following prompt in your IDE agent:
Refactor the
taskRoutesfunction in packages/asana/src/routes/tasks.ts by extracting every anonymous route handler into its own named function. Ensure the store and context are passed correctly to these handlers.
There was a problem hiding this comment.
[claude] @codacy-production ⏭️ Skipped for now — every route file in this repo (projects, sections, tags, teams, …) uses the same inline-handler structure, so refactoring only tasks.ts would break that consistency. Worth revisiting repo-wide as a separate change rather than in this PR.
| it('GET /users/me returns authenticated user', async () => { | ||
| const res = await app.request(`${base}/api/1.0/users/me`, { headers: authHeaders() }) | ||
| expect(res.status).toBe(200) | ||
| const body = await res.json() as any |
There was a problem hiding this comment.
🟡 MEDIUM RISK
Suggestion: The use of as any when parsing JSON responses hides the structure of the emulated API data. Use unknown with a type guard or define a generic interface for Asana API responses to ensure the tests remain robust against internal entity changes.
There was a problem hiding this comment.
[claude] @codacy-production ⏭️ Skipped — as any for parsed JSON is used pervasively across this test file; introducing a typed response helper is a reasonable follow-up but out of scope for this PR.
@amondnet I have started the AI code review. It will take a few minutes to complete. |
- GET /projects: require workspace or team, 404 on unknown workspace/team - GET /tasks: require a workspace/project/section filter, 404 on unknown project - POST /tasks: validate referenced projects/sections/parent (404 if missing) - POST /tasks: resolve the 'me' assignee keyword to the authenticated user - POST /tasks: set completed_at when a task is created already completed - tasks list: use optional chaining instead of a non-null assertion - projects: cast html_notes to a nullable type before the ?? default
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
A task membership could reference a section belonging to a different project, producing inconsistent project/section links. Validate that the section's project matches the membership project, returning 400 otherwise.
Add tests for the request-validation and resource-existence checks introduced during review (GET/POST 400/404 paths, cross-project section rejection, 'me' assignee resolution, completed_at on create) plus the previously untested addFollowers/removeFollowers handlers, project PUT field updates, and milestone task_counts. Raises new-code coverage above the 80% quality gate.
# Conflicts: # README.md # packages/emulate/package.json # packages/emulate/src/registry.ts
|



Summary
Ports the Asana emulator from the
amondnet/minsu-lee/asanabranch, adapted to this repo's conventions. Provides stateful emulation of the core Asana REST API v1.0 resources.@pleaseai/emulate-asana(packages/asana)emulateservice registry (default port 4005) with seed config supportConvention alignment (changes from the original)
The original lived under
packages/@emulators/asanaand used pnpm + vitest. It was adapted to fit this repo's structure.packages/asana,@pleaseai/emulate-asanabun:test(API-compatible, import-only change)Hono/Context/ContentfulStatusCodere-exported from@emulators/coreinstead of a directhonodependency (matches the firebase package)tsconfig.json,tsup.config.ts,package.json(bun test, tsgo)ifblockswebhooksparam intasks.tsVerification
emulateintegration tests passtype-check/build/lintclean (13 tasks)--service asana --port 4005→ seeded workspace returned correctly, auth middleware works as expected