Skip to content

openapi: a subtype named by two mapping keys takes whichever is declared first #410

Description

@OmarAlJarrah

A discriminator mapping may spell two wire values for one subtype — an alias tag. The compiler
keeps both on the base and picks one for the subtype, and which one it picks depends on the
order the keys are written in.

Reproduction

openapi: 3.1.0
info: {title: T, version: "1"}
paths: {}
components:
  schemas:
    Pet:
      type: object
      required: [k]
      properties: {k: {type: string}}
      discriminator:
        propertyName: k
        mapping:
          alpha: '#/components/schemas/Dog'
          zulu: '#/components/schemas/Dog'
    Dog:
      allOf: [{$ref: '#/components/schemas/Pet'}]
      properties: {bark: {type: boolean}}
$ morphic compile dd.yaml -skip-validate | jq -r '.types["t/openapi/components/schemas/Dog"].discriminatorValue'
alpha

Swap the two mapping lines and nothing else, and the same document lowers to zulu. No
diagnostic is emitted in either case.

Why it is a defect rather than a preference

A JSON or YAML mapping is unordered, so those two files are the same document. Lowering them
differently is the property the order-invariance oracle exists to reject — it would reject this
one, except that no committed spec declares two keys for one target, so the oracle never meets
the shape.

The scope is narrower than it first looks, and worth stating: nothing is lost. Discriminator
on the base keeps the whole mapping, both keys included, so the routing table an emitter needs
is intact. What varies is Model.DiscriminatorValue, the per-subtype convenience field, which
structurally holds one value and so cannot represent an alias pair at all.

Expected

Two documents differing only in mapping key order lower identically. Whether that means
choosing the tag by a rule that does not depend on declaration order, or reporting the pair as
a degradation because the field cannot hold both, is the decision to take here — the silent
order-dependence is what should not survive it.

Pre-existing: the same behaviour is on main before and after #368, which only changed how far
up the chain the mapping is searched.

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