feat: ConnectWise PSA integration (#106)

PSA abstraction layer with provider pattern, ConnectWise integration (connection management, ticket linking, note posting, status updates, member mapping), Integrations page UI, Fernet credential encryption, in-memory TTL cache, 6 DB migrations, ConnectWise API reference docs.
This commit was merged in pull request #106.
This commit is contained in:
chihlasm
2026-03-15 01:45:35 -04:00
committed by GitHub
parent 80e094215f
commit 46865882c6
60 changed files with 726716 additions and 11 deletions

View File

@@ -53,18 +53,19 @@ When adding new pages/components: use "ResolutionFlow" branding, ice-cyan gradie
## Current State
- **Phase:** Phase 2.5 - Step Library Foundation (In Progress)
- **Phase:** Phase 3 - PSA Integration (In Progress)
- **Backend:** Complete (35+ API endpoints, 100+ integration tests)
- **Frontend:** Core features complete, Tree Editor functional
- **Database:** PostgreSQL with Docker, 49 migrations
- **Database:** PostgreSQL with Docker, 75 migrations
- **Detailed status:** [CURRENT-STATE.md](CURRENT-STATE.md)
### What's In Progress
- Step Library Frontend UI
- ConnectWise PSA Integration (ticket linking, note posting, member mapping, status updates)
### Recently Completed
- Step Library Foundation
- AI chat session conclusion: outcome tracking, AI-generated ticket summaries, resume flow
- Survey completion: email-to-self, thank-you page, admin read/unread/archive/delete management
- Survey system: public survey page, admin invite tracking, response viewer with CSV export
@@ -105,12 +106,13 @@ patherly/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI entry point
│ │ ├── api/endpoints/ # Route handlers (auth, trees, sessions, admin, steps, survey, copilot, assistant_chat)
│ │ ├── api/endpoints/ # Route handlers (auth, trees, sessions, admin, steps, survey, copilot, assistant_chat, psa_connections)
│ │ ├── api/deps.py # Auth dependencies
│ │ ├── api/router.py # Route registration
│ │ ├── core/ # config, database, permissions, security, audit, rate_limit
│ │ ├── models/ # SQLAlchemy models
│ │ ── schemas/ # Pydantic schemas
│ │ ── schemas/ # Pydantic schemas
│ │ └── services/psa/ # PSA provider abstraction (base, connectwise/, cache, encryption, registry, types)
│ ├── alembic/ # Database migrations (001-029+)
│ ├── scripts/ # seed_data.py, seed_trees.py
│ └── tests/ # pytest integration tests
@@ -189,10 +191,14 @@ Official ConnectWise developer guides live in `docs/connectwise/best-practices/`
### Key Implementation Rules
- Auth: API Key auth (Base64 of `companyId+publicKey:privateKey`) + `clientId` header on every request
- All ConnectWise integration code belongs in a dedicated service layer (e.g., `services/connectwise/`) — do NOT scatter CW API calls across the codebase
- `clientId` is server-side config (`CW_CLIENT_ID` in `config.py`) — identifies the ResolutionFlow app, NOT per-tenant. Per-connection credentials: `company_id`, `public_key`, `private_key`, `server_url`
- All PSA integration code in `services/psa/` — provider pattern with `BasePsaProvider` abstract class, `ConnectWiseProvider` implementation, `PsaProviderRegistry` for multi-PSA dispatch
- PSA endpoints in `api/endpoints/psa_connections.py` — connection CRUD, ticket ops, member mapping
- Credentials encrypted at rest via `services/psa/encryption.py` (Fernet)
- Each MSP tenant provides their own CW credentials — ResolutionFlow stores these per-team, never per-user
- Design for the Autotask integration following the same service layer pattern (future PSA)
- Respect CW API: cache board/status/priority lookups, paginate with max 1000 per page, handle retries gracefully
- In-memory TTL cache in `services/psa/cache.py` for board/status/priority lookups
- Respect CW API: paginate with max 1000 per page, handle retries gracefully
---
@@ -410,6 +416,8 @@ navigate(`/trees/${newTree.id}/edit`)
**58. `scriptGeneratorStore.generate()` has an optional `sessionId` param:** `generate(sessionId?: string)` — do NOT pass it as a bare `onClick={generate}` handler (TypeScript error: MouseEvent not assignable to string). Always wrap: `onClick={() => generate()}`.
**59. ConnectWise `clientId` is server-side config, not per-connection:** `clientId` is set in backend `config.py` as `CW_CLIENT_ID` — it identifies the ResolutionFlow integration app, not the MSP tenant. Per-connection credentials are only `company_id`, `public_key`, `private_key`, and `server_url`.
---
## RBAC & Permissions
@@ -505,8 +513,8 @@ When a feature, fix, or significant piece of work is finished and merged/committ
## Future Roadmap
- **Phase 3:** File attachments, offline mode, client context, analytics
- **Phase 4:** PSA integrations (ConnectWise, Kaseya), PowerShell automation, enterprise SSO
- **Phase 3:** PSA integrations (ConnectWise in progress), file attachments, client context, analytics
- **Phase 4:** Additional PSA integrations (Autotask/Kaseya), PowerShell automation, enterprise SSO
---