PSA/ee/docs/plans/2026-03-14-n8n-contact-crud-design.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

196 lines
8.2 KiB
Markdown

# N8n Contact CRUD Design
- Date: `2026-03-14`
- Status: `Approved`
- Scope: `packages/n8n-nodes-alga-psa`
## Summary
Expand the `Alga PSA` n8n community node to support a first-class `Contact` resource with ticket-style ergonomics. The first pass should add contact `Create`, `Get`, `List`, `Update`, and `Delete` operations, align the editor experience with the existing ticket resource, and keep scope limited to core contact fields already exercised by the API and product UI.
This is an n8n package expansion, not a backend API invention. The server already exposes `GET/POST /api/v1/contacts` and `GET/PUT/DELETE /api/v1/contacts/{id}`.
## Goals
- Add `Contact` as a first-class n8n node resource.
- Support contact CRUD with the same operational feel as ticket CRUD.
- Reuse existing node conventions for validation, success normalization, and continue-on-fail behavior.
- Keep first-pass field coverage focused on core contact fields.
- Update package docs, examples, and tests so the new surface is shippable.
## Non-goals
- No contact `Search` operation in the first pass.
- No attempt to expose the full contact schema on day one.
- No backend API changes unless gaps are discovered during implementation.
- No generic registry rewrite for all n8n resources in this pass.
## Approaches Considered
### Recommended: Add contact support with a small internal refactor
- Extend the current node with a `Contact` resource and contact-specific CRUD operations.
- Extract or add shared helper logic where needed so ticket and contact behavior follow the same internal shape.
Why this approach:
- Keeps the public node surface simple.
- Avoids overloading the already-large node file with ad hoc one-off contact logic.
- Delivers contact parity without taking on a full framework rewrite.
### Alternative: Minimal inline extension in the existing node file
- Add contact fields and execute branches directly into the current resource switch with minimal cleanup.
Why not recommended:
- Fastest to start, but increases maintenance cost in an already dense file.
- Makes future additions like contact search or more resources harder to reason about.
### Alternative: Full resource-registry abstraction
- Convert ticket, contact, and helper resources into a generic registry-driven architecture.
Why not recommended:
- Too much surface area for a first contact pass.
- Raises regression risk for existing ticket behavior.
- Slows delivery of the actual user-facing capability.
## User Experience
`Contact` should appear beside `Ticket`, `Client`, `Board`, `Status`, and `Priority` in the node resource selector.
When `Contact` is selected:
- The node should expose a dedicated `contactOperation` selector.
- Required create fields should be separated from optional additional fields, mirroring ticket create/update behavior.
- `client_id` should use the same lookup-plus-manual-ID pattern already used by ticket references.
- List output should preserve pagination metadata.
- Success and error handling should match current ticket behavior, including continue-on-fail item wrapping.
## Contact Scope
### Operations
- `Create`
- `Get`
- `List`
- `Update`
- `Delete`
### Fields
Create required fields:
- `full_name`
Create and update optional fields:
- `email`
- `client_id`
- `role`
- `notes`
- `is_inactive`
- `phone_numbers`
### Field Shape Notes
- `client_id` should be a `resourceLocator` with client lookup support and manual UUID fallback.
- `phone_numbers` should be exposed as a JSON-authored field in the first pass rather than a complex nested n8n collection UI.
- `List` should support practical filters that align with the API and current lookup usage:
- `page`
- `limit`
- `client_id`
- `search_term`
- `is_inactive` if the API behavior is confirmed during implementation
## Architecture
### Node Description
Update [packages/n8n-nodes-alga-psa/nodes/AlgaPsa/AlgaPsa.node.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/nodes/AlgaPsa/AlgaPsa.node.ts) to:
- add `contact` to the resource options
- add `contactOperation`
- define contact CRUD fields and additional-field collections
- reuse existing client lookup load options for `client_id`
- update the node subtitle so contact operations render cleanly
### Payload and Query Helpers
Update [packages/n8n-nodes-alga-psa/nodes/AlgaPsa/helpers.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/nodes/AlgaPsa/helpers.ts) to add:
- contact create payload builder
- contact update payload builder
- contact list query builder
- `phone_numbers` parsing and validation helpers
The helper shape should stay close to the ticket helper conventions:
- strip empty values
- validate required strings and UUIDs before sending requests
- normalize JSON-authored fields into the API payload shape
### Execute Flow
Extend the execute logic in [packages/n8n-nodes-alga-psa/nodes/AlgaPsa/AlgaPsa.node.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/nodes/AlgaPsa/AlgaPsa.node.ts) to route:
- `POST /api/v1/contacts`
- `GET /api/v1/contacts/{id}`
- `GET /api/v1/contacts`
- `PUT /api/v1/contacts/{id}`
- `DELETE /api/v1/contacts/{id}`
Use the same output conventions already established for tickets:
- unwrap `{ data: ... }` objects
- preserve paginated list metadata
- return non-empty normalized delete success objects
- convert API errors into the current n8n error shape
## Testing
Update the existing node tests rather than introducing a new testing style.
### Description and Load Option Tests
Extend [packages/n8n-nodes-alga-psa/__tests__/node-description-loadoptions.test.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/__tests__/node-description-loadoptions.test.ts) to cover:
- `Contact` appearing in the resource selector
- valid contact operation options
- client lookup reuse in contact fields
- separation of required create fields from optional update/create collections
### Execute Tests
Extend [packages/n8n-nodes-alga-psa/__tests__/node-execute.test.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/__tests__/node-execute.test.ts) to cover:
- create request payload mapping
- get by ID
- list query serialization and pagination output
- update payload mapping
- delete request shape and normalized success output
- continue-on-fail behavior for contact operations
### Helper Tests
Extend [packages/n8n-nodes-alga-psa/__tests__/helpers.test.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/__tests__/helpers.test.ts) for:
- `phone_numbers` parsing
- malformed JSON handling
- compact payload/query behavior for contact fields
### Documentation Tests
Extend [packages/n8n-nodes-alga-psa/__tests__/docs.test.ts](/Users/roberisaacs/alga-psa.worktrees/feature/n8n-contact-crud/packages/n8n-nodes-alga-psa/__tests__/docs.test.ts) so README examples and operation matrix stay in sync.
## General Plan
1. Extend the public node surface.
Add `Contact` to the resource matrix, define contact CRUD operations, and expose ticket-style contact fields in the node description.
2. Add contact helper builders and validation.
Introduce contact payload/query helpers and validate JSON-authored `phone_numbers` before requests are sent.
3. Implement execute paths.
Wire the contact operations to the existing `/api/v1/contacts` endpoints using the same normalization and error-handling conventions as tickets.
4. Expand package tests.
Cover node description, request shapes, list behavior, validation failures, delete normalization, and continue-on-fail behavior.
5. Update package documentation and examples.
Refresh the README operation matrix and add at least one contact example workflow plus a release-note entry.
## Risks and Open Questions
- `phone_numbers` is the main shape risk because the API expects structured entries; the first pass should validate this aggressively and keep the authoring model simple.
- The exact `List` filter set should be finalized against current contact API behavior during implementation, especially `is_inactive`.
- If contact-specific label fields or response shapes differ from expectations, the node should follow actual API responses rather than forcing ticket-style naming.