Skip to content

feat(analyzer): allOf is honored wherever it appears, not only on named schemas - #117

Merged
giraffesyo merged 2 commits into
canaryfrom
allof-properties
Aug 21, 2026
Merged

feat(analyzer): allOf is honored wherever it appears, not only on named schemas#117
giraffesyo merged 2 commits into
canaryfrom
allof-properties

Conversation

@giraffesyo

@giraffesyo giraffesyo commented Aug 21, 2026

Copy link
Copy Markdown
Member

Closes #116.

allOf is JSON Schema 2020-12 composition, which 3.1 uses, and it is valid anywhere a schema is. convertSchema composes it for a named component schema. resolveGoType, which types every inline property schema, never looked at it, so a property that composed anything resolved to any.

Measured on a 3.1 spec, before this change:

property canary correct
allOf: [$ref, {inline object}] any no
allOf: [$ref] any no
$ref with sibling keywords *User yes
anyOf: [$ref, {type: "null"}] *User yes

The first two are the inconsistency: the same composition written as a named schema builds a struct.

What it does

  • A lone $ref inside an allOf says the value must match that schema, so the property is of that type.
  • Anything more composes a shape of its own and is named like other inline schemas, so allOf: [{$ref: User}, {properties: {verdict}}] gives a struct embedding User and adding Verdict rather than any.

On the framing of my first version

I justified this by counting any fields in GitHub's client, 53 down to 38, and that was the wrong argument: GitHub is 3.0, where wrapping a $ref in allOf is a workaround for sibling keywords being ignored. Of the four specs I have, only that one writes it, and Mealie and ACTIVATE, both 3.1, write it nowhere.

The change stands on 3.1 grounds instead: allOf is part of the schema language 3.1 uses, the generator already honors it for named schemas, and honoring it in one place and not the other is a hole a valid 3.1 spec falls into. The GitHub numbers are incidental corroboration, not the reason.

Tests

All specs in them are 3.1.

  • internal/analyzer/schemas_allof_property_test.go: a lone $ref resolves to it with and without sibling keywords, a two-entry composition is named and keeps both halves, a single inline entry composes just as much as several, and a separate test pins the idioms 3.1 offers instead, a $ref with siblings and an anyOf with a null member, which already worked and now cannot regress.
  • internal/generator/e2e_allof_property_test.go: compiles and runs, reading a composed $ref without an assertion and checking a JSON null leaves the pointer nil.

gofmt, golangci-lint, go vet ./..., and go test ./... pass.

…ed schemas

allOf is JSON Schema 2020-12 composition, which 3.1 uses, and it is valid
anywhere a schema is. convertSchema composes it for a named component schema
and resolveGoType, which types every inline property, never looked at it, so
a property that composed anything resolved to any.

In a 3.1 spec today:

	allOf: [$ref, {inline object}]   ->  any
	allOf: [$ref]                    ->  any
	$ref with sibling keywords       ->  the referenced type
	anyOf: [$ref, {type: null}]      ->  the referenced type

The first two are the inconsistency: the same composition on a named schema
builds a struct. A lone $ref inside an allOf says the value must match that
schema, so it resolves to it; anything more composes a shape of its own and
is named like other inline schemas, keeping both what it embeds and what it
adds.

The two idioms 3.1 offers instead already worked, and now have a test that
keeps them working.
@giraffesyo giraffesyo changed the title feat(analyzer): a property that wraps a $ref in allOf keeps its type feat(analyzer): allOf is honored wherever it appears, not only on named schemas Aug 21, 2026
…parts

The composition path this PR adds converted its schema as if it were sent as
JSON, so a multipart body written as allOf typed its binary properties as
byte slices and the encoder sent them as base64 text in ordinary fields, the
same failure #90 fixed for a body written as an object.

Multipart is a property of where a schema is used, so the composition path
consults it the same way the object path does.
@giraffesyo
giraffesyo merged commit 41e0737 into canary Aug 21, 2026
7 checks passed
@giraffesyo
giraffesyo deleted the allof-properties branch August 21, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A property that wraps a $ref in allOf resolves to any

1 participant