Skip to content

Data interchange ​

Data interchange is how a facility gets its inventory into Ovahol. It is also how a facility gets its data out in a form another system can read.

There is exactly one import path: the interchange bundle. The older, library-specific importer and its facility-route import are gone, so a bug fixed here is fixed for every customer.

The bundle ​

A bundle is a JSON document conforming to the ovahol/interchange-schema v0.1.0 draft, vendored byte-identical at backend/internal/modules/interchange/schema/v0.1.0.json. Every bundle Ovahol emits validates against the unmodified draft.

Beyond the letter of the schema, Ovahol has defined behaviour for the cases the draft leaves open — unknown model names, unplaced assets, nomenclature fallbacks. Those are recorded in the implementation profile, which a second system should read alongside the schema.

The two route families ​

Tenant routesTargeted routes
Base path/api/v1/tenant/interchange/api/v1/core/admin/interchange/facilities/:facility_id
FacilityThe caller's ambient facilityChosen by the caller
RolesFacility roles and org adminsSUPER_ADMIN, ORG_ADMIN, FACILITY_ADMIN
ScopeContext middlewareservice.AuthorizeFacilityAccess

The targeted family exists because a super admin migrating data on behalf of a facility has no ambient facility context. It deliberately sits outside the tenant middleware chain and authorizes the target facility in the service layer: super admins may target any facility, org admins only facilities in their organization, facility admins only their own.

The operations are identical:

OperationPurpose
GET /exportEmit the facility as one bundle
GET /catalog/names?kind=device|model|brand|manufacturerScoped name list for match correction
POST /validateSemantic validation, no writes
POST /convertNormalize a third-party spreadsheet-shaped bundle; optionally recommend missing catalog entries
POST /importAll-or-nothing apply

Request bodies are capped at 32 MiB (413 beyond that).

The import flow ​

third-party data ──convert──> bundle ──validate──> import preview ──import──> facility
                                            │
                                            └── match report (create vs. reuse)
  1. Convert (optional) — a spreadsheet export becomes a bundle: column mapping, composite unknown-model naming, holding location for unplaced rows, UMDNS fallback. Conversion never touches the database; recommend_missing additionally proposes catalog entries for a human to approve.
  2. Validate — schema validation plus referential checks against the target facility: which devices already exist, which definitions can be reused, which rows would be rejected and why. Nothing is written.
  3. Import — the whole bundle is applied in a single transaction. A failure anywhere rolls back everything, so a half-migrated facility is not a state that can exist.

Import history is recorded so a re-run can be traced.

The engine ​

The engine lives in backend/internal/modules/interchange; its service package holds the moving parts:

FileResponsibility
convert.goColumn mapping and normalization into the bundle shape
validate.goSchema + semantic validation, match planning
matcher.goResolving names to existing catalog records
recommend.goSuggesting catalog entries for unmatched rows
import.goThe transactional apply
export.goReading a facility into a bundle
codemap.goVocabulary code translation
identity.goDevice identity/deduplication rules
target.goFacility targeting and authorization
catalog_names.goScoped name lookups

It owns no persistence. It composes the public facades of the devices, maintenance, lifecycle, training, facilities and counterparties modules — which is why the module sits above them rather than beside them.

Why one path ​

Two importers meant two normalization engines, two sets of edge-case fixes and two sets of tests for the same problem. The bundle is the standard; the spreadsheet converter is an adapter at the front of it, not a parallel implementation. Adding a source format means adding a converter, never a second import engine.

Ovahol developer documentation