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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
126 lines
7.2 KiB
Markdown
126 lines
7.2 KiB
Markdown
# Scratchpad — Hudu Phase 2 (Assets & Documents)
|
||
|
||
## Decisions
|
||
|
||
- 2026-06-11 — Scope settled with Natallia via three options questions:
|
||
assets = **import + pull sync** (not mapping-only, not one-time import);
|
||
articles = **link-only** section in client Documents tab; global view =
|
||
**Documents page "Hudu" tab** (not global-search-only).
|
||
- Sync is **manual-only** this phase (button per client); scheduled refresh
|
||
stays a future enhancement (was already a Phase 1 non-goal).
|
||
- Sync conflict policy: **Hudu wins on synced fields only** (`name`,
|
||
`serial_number`); everything else in Alga untouched; `asset_type` never
|
||
retro-changed after import/mapping.
|
||
- Hudu disappearance → mapping metadata `stale: true` + badge. Never delete.
|
||
- Layout→type map lives in `hudu_integrations.settings.asset_layout_type_map`
|
||
(no new table); UI block inside HuduIntegrationSettings.
|
||
- Asset mapping UI reuses the **staged Save/Discard confirmation pattern**
|
||
built for company mappings on 2026-06-11 (picker stages; Save commits;
|
||
dismissible suggestions; row revert/unmap actions).
|
||
|
||
## Discoveries (verified in code)
|
||
|
||
- `tenant_external_entity_mappings` unique indexes
|
||
(`server/migrations/20250502173321_…`) are scoped by
|
||
`(tenant…, integration_type, alga_entity_type, …)` → asset mappings
|
||
(`alga_entity_type='asset'`) coexist with Phase 1 client mappings. The
|
||
original migration named the column `tenant_id` but Phase 1's
|
||
companyMapping.ts queries `tenant` and its real-DB tests pass — a later
|
||
rename superseded it. **Mirror companyMapping.ts, not the old migration.**
|
||
- `CreateAssetRequest` (`packages/types/src/interfaces/asset.interfaces.ts:378`):
|
||
fixed enum `asset_type: 'workstation'|'network_device'|'server'|
|
||
'mobile_device'|'printer'|'unknown'`; **required** `asset_tag`, `name`,
|
||
`status`, `client_id`; optional `serial_number`; per-type extension blobs.
|
||
- `createAsset` = `packages/assets/src/actions/assetActions.ts:755`
|
||
(withAuth wrapper → asset RBAC).
|
||
- Client Documents tab: `packages/clients/src/components/clients/
|
||
ClientDetails.tsx` tab id `documents` (~line 1715). Phase 1 EE-injection
|
||
precedent: `useHuduClientTab(client.client_id)` at line 283 + spread tabs
|
||
at ~1844.
|
||
- Main documents page: `server/src/app/msp/documents/page.tsx` →
|
||
`packages/documents/src/components/DocumentsPage.tsx`.
|
||
- Hudu API (plan dir `hudu-api-reference.md`): assets by company
|
||
`GET /api/v1/assets?company_id=&page=&archived=false`; articles
|
||
`GET /api/v1/articles?company_id=&page=`; global articles = omit
|
||
company_id. Asset layouts: `GET /api/v1/asset_layouts` (verify response
|
||
shape on local instance). 25/page, 300 req/min.
|
||
- Hudu asset payload carries `primary_serial`, `asset_layout_id`, `fields[]`
|
||
(layout custom fields), `archived`.
|
||
|
||
## Environment / testing
|
||
|
||
- Local Hudu: `http://hudu.localtest.me` (stack in `~/hudu2`), API key
|
||
unscoped, password access on. Companies: ExampleCo (1), Emerald City Ltd
|
||
(2). Alga dev: bigmac worktree, `localhost:3001`, tenant
|
||
`c7c99e9f-19dd-46b0-80be-0a2a59f5ab7a` (has `enterprise` add-on row).
|
||
Flags forced via `server/.env.local` (`NEXT_PUBLIC_FORCE_FEATURE_FLAGS`
|
||
+ `DISABLE_FEATURE_FLAGS=true`).
|
||
- Unit tests: `cd ee/server && npx vitest run src/__tests__/unit/hudu` —
|
||
273 passing pre-Phase-2 (incl. staged-save mapping manager rewrite).
|
||
|
||
## Implementation findings
|
||
|
||
- **FR1 correction**: `idx_unique_external_mapping` is `(tenant,
|
||
integration_type, external_entity_id, COALESCE(external_realm_id,''))` —
|
||
NOT scoped by alga_entity_type (only the alga-direction index is). Hudu
|
||
company and asset ids overlap numerically → asset mapping rows carry
|
||
`external_realm_id = String(hudu company id)` (client rows use null).
|
||
T215 proves coexistence with identical external ids.
|
||
- `getHuduAssetMappings` reuses the Phase 1 `getHuduCompanyAssets` action
|
||
for fetch+cache → effectively requires system_settings read in addition
|
||
to asset read (technicians already have it for the client tab). Acceptable.
|
||
- regression-phase2 DONE (2026-06-11): huduPermissions.test.ts now enumerates
|
||
all 8 action modules (Phase 1 three + layoutMap/assetMapping/assetImport/
|
||
assetSync/globalDocs) — 97 tests. Layout-map actions joined the
|
||
system_settings manage/view lists; the Technician flows got a
|
||
resource+permission matrix (asset read/update/create, client read) plus an
|
||
unauthenticated sweep over every entry (withAuth mock now drops the session
|
||
user). T093 exhaustiveness still fails on any future unlisted export.
|
||
T250 = information_schema column-list check in
|
||
hudu-regression.integration.test.ts (real-DB harness): exactly the 12
|
||
Phase 1 columns, no pull-only additions (NFR4). T110 expected-file list
|
||
extended with the 5 new action modules.
|
||
- Found while running tsc for regression-phase2: 6 hudu type errors from
|
||
earlier groups, all fixed — HuduAssetMappingManager.tsx relied on
|
||
`!result.success` narrowing, which the non-strict EE tsconfig doesn't do
|
||
(added isImportFailure/isBulkImportFailure guards, the file's existing
|
||
isMappingFailure idiom); two TS2347s (type args on untyped vi helpers) in
|
||
huduSettingsPageCategory.test.tsx / huduDocumentsTabGate.test.tsx became
|
||
post-call casts. Remaining tsc errors are the known pre-existing chat
|
||
registry / mcp agent-tooling / msp-composition ones (14).
|
||
- `HuduAsset` in contracts.ts lacks `asset_layout_id` (runtime has it);
|
||
typed locally in actions as HuduAssetListItem — consider lifting to
|
||
contracts in a later group.
|
||
|
||
## OQ resolutions (2026-06-11, verified on local instance)
|
||
|
||
- **OQ1 RESOLVED**: `GET /api/v1/articles?search=` does case-insensitive
|
||
partial matching ("vpn" → "VPN Setup Guide"); `?name=` is exact-only.
|
||
Use `?search=`. Global list = omit `company_id`; response carries
|
||
`company_id` per article.
|
||
- **OQ2 RESOLVED**: `assets.asset_tag` is notNullable but has NO unique
|
||
constraint (migration `20241112031330_create_asset_management_tables.cjs`).
|
||
`primary_serial` as tag is safe; `hudu-<id>` fallback only for missing
|
||
serials. NOTE: assets table column is `company_id` (legacy name for
|
||
client) + `type_id` uuid — the `asset_type` enum in CreateAssetRequest is
|
||
the action-level API; verify createAsset's mapping when implementing.
|
||
- Hudu asset payload verified: `{id, name, company_id, asset_layout_id,
|
||
primary_serial, archived, fields[]}`. Default layouts on fresh instance
|
||
incl. "Computer Assets" (7), "Databases" (9), "Applications" (3).
|
||
- Seeded local data: articles (2× ExampleCo + 3 pre-existing, 1× Emerald),
|
||
assets EC-WS-001 (SN-EC-1001), EC-SRV-01 (SN-EC-2001) in Computer Assets.
|
||
- Git: commits require Natallia's explicit request (auto-mode enforces) —
|
||
implement all groups, flip flags, let her commit per group at the end.
|
||
|
||
## Gotchas
|
||
|
||
- `asset_tag` uniqueness semantics unverified (R2) — check before using
|
||
`primary_serial` as tag; fallback `hudu-<id>`.
|
||
- Hudu article search param name unverified (OQ1): try `?search=`, fall back
|
||
to `?name=`.
|
||
- i18n: every new string needs en + de/es/fr/it/nl/pl/pt keys, and the
|
||
Phase 1 static i18n test (`huduI18n.test.ts`) must be extended to scan the
|
||
new components (it whitelists scanned sources explicitly).
|
||
- DocumentsPage is a CE package — EE tab must inject via gate hook, never a
|
||
hard import of EE components (R4).
|