Files
2026-05-27 23:29:53 +01:00

127 lines
10 KiB
Markdown

# @suaveplan/api-plugin-contacts - Contacts + groups HTTP plugin for @suaveplan/api. Mounts /contacts and /groups (with /groups/:id/members and /groups/:id/resolve) on a Hono OpenAPI registry, backed by SurrealDB-implementations of the @suaveplan/contacts ContactStore / GroupStore / MembershipStore interfaces. Bulk-import with per-row error reporting, repository-level maxGroupDepth enforcement, six OTel instruments, typed Hono ContextVariableMap + ServiceRegistry augmentations, and the 12-code CONTACTS_API.* error namespace.
Contacts and Groups HTTP plugin for [`@suaveplan/api`](../../api/api).
Mounts the `/contacts` and `/groups` route trees on a Hono
`OpenAPIHono`, backed by SurrealDB implementations of the
[`@suaveplan/contacts`](../contacts) store interfaces.
<!-- begin:auto-docs -->
## Documentation tree
- [context](./src/context/context.md) — Module augmentation declaring four typed slots on Hono's `ContextVariableMap`: `contactRepository`, `groupRepository`,…
- [context](./src/context/context.md) — Module augmentation declaring four typed slots on Hono's `ContextVariableMap`: `contactRepository`, `groupRepository`,…
- [error-codes](./src/error-codes/error-codes.md) — Single source of truth for every failure path inside `@suaveplan/api-plugin-contacts`. Built on the `@suaveplan…
- [error-codes](./src/error-codes/error-codes.md) — Single source of truth for every failure path inside `@suaveplan/api-plugin-contacts`. Built on the `@suaveplan…
- [otel](./src/otel/instruments.md) — Exposes the six OpenTelemetry instruments emitted by `@suaveplan/api-plugin-contacts` in production: four counters cove…
- [instruments](./src/otel/instruments.md) — Exposes the six OpenTelemetry instruments emitted by `@suaveplan/api-plugin-contacts` in production: four counters cove…
- [plugin](./src/plugin/plugin.md) — Returns the `PluginDescriptor<ContactsPluginConfig>` consumed by the `@suaveplan/api` plugin host. Runs the 5-step `onI…
- [plugin](./src/plugin/plugin.md) — Returns the `PluginDescriptor<ContactsPluginConfig>` consumed by the `@suaveplan/api` plugin host. Runs the 5-step `onI…
- [schema](./src/plugin/schema.md) — Inlined SurrealQL DDL for the three tables the contacts plugin owns: `contact`, `contact_group`, and `group_membership`…
- [contact-repository](./src/repositories/contact-repository.md) — Concrete implementation of the universal `ContactStore` interface from `@suaveplan/contacts`, backed by a connecte…
- [group-repository](./src/repositories/group-repository.md) — Concrete implementation of the `GroupStore` interface from `@suaveplan/contacts`, backed by `SurrealAdapter`. I…
- [membership-repository](./src/repositories/membership-repository.md) — Concrete implementation of the `MembershipStore` interface from `@suaveplan/contacts`, backed by `SurrealAdapte…
- [build-routes](./src/routes/contacts/build-routes.md) — Returns the seven `PluginRouteEntry` values that compose the `/contacts` route tree. Mounted by the plugin's `onInit` v…
- [contacts-bulk-routes](./src/routes/contacts/contacts-bulk-routes.md) — Two `PluginRouteEntry` builders for the bulk-operation contact endpoints: `POST /contacts/import` and `POST /contacts/m…
- [contacts-crud-routes](./src/routes/contacts/contacts-crud-routes.md) — Five `PluginRouteEntry` builders for the contact CRUD endpoints: `GET /contacts`, `POST /contacts`, `GET /contacts/:id`…
- [build-routes](./src/routes/groups/build-routes.md) — Returns the 11 `PluginRouteEntry` values that compose the `/groups` route tree: 5 CRUD (`list`, `create`, `get`, `updat…
- [groups-crud-routes](./src/routes/groups/groups-crud-routes.md) — Five `PluginRouteEntry` builders for the group CRUD endpoints: `GET /groups`, `POST /groups`, `GET /groups/:id`, `PATCH…
- [groups-members-routes](./src/routes/groups/groups-members-routes.md) — Four `PluginRouteEntry` builders for the group-membership endpoints: `GET /groups/:id/members`, `POST /groups/:id/membe…
- [groups-tree-routes](./src/routes/groups/groups-tree-routes.md) — Two `PluginRouteEntry` builders for the hierarchical-group endpoints: `GET /groups/:id/tree` materialises a typed `Grou…
- [require-tenant](./src/routes/shared/require-tenant.md) — Centralised guard that extracts and validates the request `Principal` on every `/contacts` and `/groups` handler. Retur…
- [strip-undefined](./src/routes/shared/strip-undefined.md) — Bridges Zod's optional-field semantics (`.optional() → string | undefined`) with the `@suaveplan/contacts` inpu…
- [contacts-schemas](./src/schemas/contacts-schemas.md) — Zod schemas mounted on every `/contacts` route via `@hono/zod-openapi`. They validate inbound bodies / query strings, s…
- [groups-schemas](./src/schemas/groups-schemas.md) — Zod schemas for the seven `/groups` routes (list, create, get, update, delete, tree, move) plus the three `/groups/:id/…
- [options](./src/schemas/options.md) — Zod schema that validates the optional bag of options passed to `createContactsPlugin`. Every field is optional with a…
- [services](./src/services/registry-augmentation.md) — Adds typed `contactRepository`, `groupRepository`, and `membershipRepository` slots to the shared `@suaveplan/api-plugi…
- [registry-augmentation](./src/services/registry-augmentation.md) — Adds typed `contactRepository`, `groupRepository`, and `membershipRepository` slots to the shared `@suaveplan/api-plugi…
- [tree](./src/tree/build-tree.md) — Constructs a typed `GroupTreeNode` rooted at the supplied `Group`, given a flat list of descendants in arbitrary order.…
- [build-tree](./src/tree/build-tree.md) — Constructs a typed `GroupTreeNode` rooted at the supplied `Group`, given a flat list of descendants in arbitrary order.…
- [types](./src/types/types.md) — Pure type declarations that downstream consumers of `@suaveplan/api-plugin-contacts` depend on at compile time. Keeping…
- [types](./src/types/types.md) — Pure type declarations that downstream consumers of `@suaveplan/api-plugin-contacts` depend on at compile time. Keeping…
<!-- end:auto-docs -->
## Status
- Version: `0.1.0`
- Tier: server / universal
- suaveplan-policy: 100% test coverage, 0 mocks, real SurrealDB via
`@suaveplan/testing-integration-surrealdb`.
## Quick start
```ts
import { createContactsPlugin } from "@suaveplan/api-plugin-contacts";
const plugin = createContactsPlugin({
routePrefix: "/api",
maxImportBatchSize: 500,
maxGroupDepth: 10,
enforceGroupDepth: true,
});
// host.use(plugin);
```
The plugin is suaveplan-compatible with the `@suaveplan/api-plugin`
descriptor contract; load it after the database and auth plugins so
the ServiceRegistry `db` and `authService` slots resolve.
## Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | `/contacts` | List contacts (search / status / channelType / tags / paging) |
| POST | `/contacts` | Create a contact |
| GET | `/contacts/:id` | Read a contact (tenant-scoped) |
| PATCH | `/contacts/:id` | Update a contact |
| DELETE | `/contacts/:id` | Delete a contact |
| POST | `/contacts/import` | Bulk import (Zod-capped) with per-row errors |
| POST | `/contacts/merge` | Channel + tag union into a primary contact |
| GET | `/groups` | List groups |
| POST | `/groups` | Create a group (depth-enforced) |
| GET | `/groups/:id` | Read a group |
| PATCH | `/groups/:id` | Update a group |
| DELETE | `/groups/:id` | Delete a group (cascade members) |
| GET | `/groups/:id/tree` | Typed `GroupTreeNode` subtree |
| POST | `/groups/:id/move` | Reparent (depth-enforced) |
| GET | `/groups/:id/members` | List members |
| POST | `/groups/:id/members` | Idempotent add |
| DELETE | `/groups/:id/members` | Idempotent remove |
| GET | `/groups/:id/resolve` | Flatten group + descendants → unique contacts |
## Modules
- [`error-codes/error-codes`](./src/error-codes/error-codes.md) — 12 `CONTACTS_API.*` codes + `ContactsApiError`.
- [`otel/instruments`](./src/otel/instruments.md) — six OTel instruments.
- [`schemas/options`](./src/schemas/options.md) — plugin options Zod schema.
- [`schemas/contacts-schemas`](./src/schemas/contacts-schemas.md) — `/contacts` request/response shapes.
- [`schemas/groups-schemas`](./src/schemas/groups-schemas.md) — `/groups` request/response shapes.
- [`repositories/contact-repository`](./src/repositories/contact-repository.md) — `SurrealContactRepository`.
- [`repositories/group-repository`](./src/repositories/group-repository.md) — `SurrealGroupRepository`.
- [`repositories/membership-repository`](./src/repositories/membership-repository.md) — `SurrealMembershipRepository`.
- [`routes/shared/require-tenant`](./src/routes/shared/require-tenant.md) — Principal → tenantId guard.
- [`routes/contacts/build-routes`](./src/routes/contacts/build-routes.md) — `/contacts` aggregator.
- [`routes/contacts/contacts-crud-routes`](./src/routes/contacts/contacts-crud-routes.md) — CRUD builders.
- [`routes/contacts/contacts-bulk-routes`](./src/routes/contacts/contacts-bulk-routes.md) — import + merge builders.
- [`routes/groups/build-routes`](./src/routes/groups/build-routes.md) — `/groups` aggregator.
- [`routes/groups/groups-crud-routes`](./src/routes/groups/groups-crud-routes.md) — CRUD builders.
- [`routes/groups/groups-tree-routes`](./src/routes/groups/groups-tree-routes.md) — tree + move.
- [`routes/groups/groups-members-routes`](./src/routes/groups/groups-members-routes.md) — membership + resolve.
- [`tree/build-tree`](./src/tree/build-tree.md) — typed subtree builder.
- [`plugin/schema`](./src/plugin/schema.md) — bundled SurrealQL DDL + apply helper.
- [`plugin/plugin`](./src/plugin/plugin.md) — `createContactsPlugin` factory.
- [`types/types`](./src/types/types.md) — public configuration types.
- [`context/context`](./src/context/context.md) — Hono `ContextVariableMap` augmentation.
- [`services/registry-augmentation`](./src/services/registry-augmentation.md) — `ServiceRegistry` augmentation.
## Tests
```sh
bunx turbo test --filter=@suaveplan/api-plugin-contacts
bunx turbo test:coverage --filter=@suaveplan/api-plugin-contacts
```
Integration tests under `tests/integration/` boot a SurrealDB
testcontainer (Docker required) and exercise the full plugin
lifecycle end-to-end.