Skip to content

spec(api): mappingName is accepted by both import routes but undeclared on ImportRequestSchema — the typed SDK cannot express it #10330

Description

@os-elon

Found while writing the Data Modeling import-mappings page (epic #10206, card #10213). Filed unassigned for domain:spec triage; not fixed there, per that card's non-goals.

Enforced but undeclared

mappingName is the request parameter the mapping metadata kind exists for — it is the consumer that satisfied the ADR-0088 admission test (#2611). Both import routes read it:

  • POST /api/v1/data/:object/import
  • POST /api/v1/data/:object/import/jobs

Both go through prepareImportRequest in packages/rest/src/import-prepare.ts, which reads body.mappingName off the raw body, resolves the artifact, and rejects mappingName plus an inline mapping with 400 CONFLICTING_MAPPING.

ImportRequestSchema in packages/spec/src/api/export.zod.ts declares fifteen keys and none of them is mappingName. CreateImportJobRequestSchemaisImportRequestSchema (one object, two names), so the async route is undeclared in the same edit.

This is the mirror of declared-but-unenforced — the shape #6245 closed for webhook/connector/sharing_rule, in the opposite direction: the wire accepts something the contract does not describe.

Measured

Zero occurrences in the built declarations:

$ grep -rn "mappingName" --include="*.d.ts" packages/spec/dist/ # exit 1, 0 lines

And the SDK call is a type error. packages/client/src/index.ts types both data.import and the scoped data.import as (object: string, request: ImportRequest), with ImportRequest imported from @objectstack/spec/api:

importtype{ImportRequest}from'@objectstack/spec/api';constreq: ImportRequest={format: 'csv',csv: 'Full Name,E-mail\nAda,ada@example.com\n',mappingName: 'showcase_inquiry_feed',};
error TS2353: Object literal may only specify known properties, and 'mappingName'
does not exist in type '{ format?: "csv" | "json" | "xlsx" | undefined; csv?: string
| undefined; rows?: Record<string, unknown>[] | undefined; ... 9 more ...;
skipBlankMatchKey?: boolean | undefined; }'.

(tsc 6.0.3, --strict, against packages/spec/dist built at this branch's base.)

Why it matters beyond a missing key

The kind was promoted because a consumer landed. As things stand that consumer is reachable only by hand-rolled HTTP: a TypeScript caller using the published client cannot name a mapping without casting the request. Anything else derived from the spec — the OpenAPI document, any generated client — is missing it for the same reason.

Suggested shape

Add to ImportRequestSchema, next to mapping:

mappingName: z.string().optional().describe('Name of a registered `mapping` artifact to apply. Mutually exclusive with `mapping`.'),

Worth deciding at the same time whether the mutual exclusion should be a schema refinement rather than only a route check — the route's 400 CONFLICTING_MAPPING stays either way, but a .refine() would reject the conflicting pair at authoring time for anyone building the body through the schema.

Note ImportRequestSchema is a plain z.object, so it strips unknown keys rather than rejecting them; adding the key is additive and cannot break an existing caller.

Impact on docs

content/docs/data-modeling/import-mappings.mdx (card #10213) documents the HTTP request, which works, and carries an explicit warning that the SDK call does not type-check, pointing at this issue. That warning should be deleted when this lands.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions