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
110 lines
5.4 KiB
Markdown
110 lines
5.4 KiB
Markdown
# Hudu Integration — Phase 2.1: Attributes, Layout Exclusion, Multi-Source Guard
|
|
|
|
- Status: Approved (scope picked by Natallia 2026-06-12)
|
|
- Phase: 2.1 (pull-only, Hudu → AlgaPSA)
|
|
- Predecessors: Phase 1 (`2026-06-08-hudu-integration`), Phase 2 (`2026-06-11-hudu-integration-phase2`)
|
|
|
|
## Overview
|
|
|
|
Three gaps surfaced while reviewing Phase 2:
|
|
|
|
1. **Hudu field data (incl. Notes) isn't imported** — Hudu assets carry their
|
|
real documentation in per-layout custom `fields[]`; Phase 2 imported only
|
|
name/serial/type.
|
|
2. **Every Hudu layout is importable** — but most layouts (API Secrets,
|
|
Contracts & SLAs, Cloud Accounts…) aren't devices and shouldn't become
|
|
AlgaPSA assets at all.
|
|
3. **Multi-source sync is unguarded** — NinjaOne's sync engine already
|
|
creates/updates Alga assets; Hudu sync writing name/serial to the same
|
|
asset means last-writer-wins ping-pong, and import can create duplicates
|
|
when serials are absent or unmatched.
|
|
|
|
## Verified foundations
|
|
|
|
- Live `assets` columns include `attributes jsonb` (NinjaOne precedent for
|
|
integration extras), `rmm_provider/rmm_device_id/agent_status/last_seen_at`
|
|
(direct RMM-ownership signal), and `notes_document_id` (document-backed
|
|
notes — NOT used here; creating documents from Hudu content would break the
|
|
link-only philosophy and invite drift).
|
|
- Hudu `fields[]` shape (live): `{ id, label, value, position }`, ordered by
|
|
position; values are strings/dates/numbers/null.
|
|
|
|
## Goals
|
|
|
|
- G1. Import and sync copy a Hudu asset's `fields[]` into the Alga asset's
|
|
`attributes` under a Hudu namespace; the asset detail page renders them.
|
|
- G2. The layout map supports **Don't import** per layout; import paths skip
|
|
excluded layouts.
|
|
- G3. Hudu sync never fights an RMM for device facts; import never silently
|
|
creates a serial-duplicate of an existing asset.
|
|
|
|
## Non-goals
|
|
|
|
- Per-field mapping UI (Hudu field → specific Alga column) — the namespace
|
|
copy is wholesale and read-only.
|
|
- Writing Hudu data into `notes_document_id` / creating Alga documents.
|
|
- Per-tenant configurable field-precedence matrix (the fixed RMM-wins rule is
|
|
this phase; config comes if a real need appears).
|
|
- Touching NinjaOne's sync engine.
|
|
|
|
## Functional Requirements
|
|
|
|
Hudu attributes (group `hudu-attributes`):
|
|
- FR1. `HuduAsset` contract gains `asset_layout_id` and `fields[]` (lifting
|
|
the Phase 2 local typing into contracts.ts).
|
|
- FR2. Import writes `attributes.hudu_fields = [{label, value}]` (position
|
|
order preserved) and `attributes.hudu_synced_at`; other attributes keys
|
|
untouched.
|
|
- FR3. Sync refreshes `attributes.hudu_fields` on every mapped, live Hudu
|
|
asset (the Hudu namespace is always Hudu-won, independent of the RMM rule);
|
|
a row counts as `updated` if name/serial OR hudu_fields changed.
|
|
- FR4. Asset detail page renders a read-only "Hudu Documentation" card
|
|
listing label/value pairs whenever `attributes.hudu_fields` is non-empty.
|
|
Pure data-presence gate — no EE import, no flag check (the data only exists
|
|
if the EE integration wrote it), so the card lives in packages/assets (CE)
|
|
next to its siblings.
|
|
- FR5. New UI strings i18n'd in all 8 locales in whatever namespace the
|
|
asset detail components already use.
|
|
|
|
Layout exclusion (group `layout-exclude`):
|
|
- FR6. The layout-type map accepts `'excluded'` alongside the six asset
|
|
types; normalization/validation updated; `resolveAssetTypeForLayout`
|
|
exposes exclusion distinctly (not as 'unknown').
|
|
- FR7. Settings UI type select gains a "Don't import" option.
|
|
- FR8. Single import of an excluded-layout asset returns a typed
|
|
`layout_excluded` failure; bulk import skips excluded-layout assets and
|
|
reports a `skipped` count in the summary.
|
|
- FR9. The client-tab mapping manager hides the Import affordance (and
|
|
excludes the row from "Import all unmatched" counts) for excluded-layout
|
|
rows; mapping an excluded-layout Hudu asset to an EXISTING Alga asset
|
|
remains allowed (context-linking is harmless).
|
|
|
|
Multi-source guard (group `multi-source-guard`):
|
|
- FR10. Sync skips `name`/`serial_number` writes for assets with
|
|
`rmm_provider` set (RMM owns device facts); it still refreshes
|
|
`attributes.hudu_fields` and stale flags for them. Summary gains an
|
|
`rmmSkipped` count surfaced in the UI alongside updated/unchanged/stale.
|
|
- FR11. Import pre-checks tenant-wide for an existing asset with the same
|
|
non-blank serial_number; hit → typed `serial_conflict` failure naming the
|
|
existing asset (id + name); bulk records these per-row under `failed` with
|
|
the code.
|
|
- FR12. Mapping manager renders bulk-import summaries distinguishing
|
|
created / skipped (excluded) / failed (incl. serial conflicts).
|
|
|
|
Cross-cutting:
|
|
- FR13. Permissions sweep and i18n static scan stay green (no new action
|
|
modules; new strings added to scans where components are Hudu-owned).
|
|
|
|
## Acceptance Criteria
|
|
|
|
- AC1. Importing EC-WS-001 lands its Hudu fields on the Alga asset and they
|
|
render on the asset page; re-sync after editing a field in Hudu updates it.
|
|
- AC2. "API Secrets" marked Don't import → invisible to Import-all, single
|
|
import fails typed, row not importable in UI but still mappable.
|
|
- AC3. An asset with rmm_provider='ninjaone' mapped to a Hudu asset keeps its
|
|
RMM name/serial through a Hudu sync, while its hudu_fields refresh; the
|
|
summary shows it under rmmSkipped.
|
|
- AC4. Importing a Hudu asset whose serial matches any existing tenant asset
|
|
fails with serial_conflict naming the existing asset.
|
|
- AC5. Full hudu suites green; locale files updated in 8 languages.
|