Skip to content

fix: emit dynamic attribute for self-referential schemas to prevent stack overflow - #2

Draft
claude[bot] wants to merge 2 commits into
mainfrom
bb/fix-recursive-schema-dynamic
Draft

fix: emit dynamic attribute for self-referential schemas to prevent stack overflow#2
claude[bot] wants to merge 2 commits into
mainfrom
bb/fix-recursive-schema-dynamic

Conversation

@claude

@claude claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Requested by Brady Burke · Slack thread

Before / After

Before: Running the OpenAPI codegen (tfplugingen-openapi) on a genuinely self-referential schema crashed the whole process with an unrecoverable fatal error: stack overflow. The Galaxy API's IngestColumn / NewJsonIngestColumnInSchemaUpdate schema has a nestedColumns array whose items $ref back to the enclosing schema (it models arbitrarily-nested ingest columns). The attribute builder had no cycle or depth tracking, so it recursed into that same property forever and took down generation of the ingest_column resource entirely.

After: The builder detects the reference cycle and, at the point where the recursive edge would re-enter a type already open on the descent path, emits that one attribute as a Terraform dynamic attribute instead of recursing. Generation now succeeds, the ingest_column resource is produced, and the recursive structure is still authorable (as a dynamic value). Everything else in the schema stays strongly typed — only the self-referential edge degrades to dynamic. Terraform's plugin framework has no recursive nested type, so dynamic is the correct representation (preferred over erroring/skipping the resource, or flattening the field to a JSON string).

How

  • Added a VisitedRefs map[string]bool set to GlobalSchemaOpts (which already threads through the whole builder chain), with a WithVisitedRef helper that returns a copy with a ref added so sibling branches never share mutation, and an IsVisitedRef helper.
  • BuildSchema records a schema's $ref in the visited set as it descends through it, so deeper builders can tell when they are re-entering a type already open on the path (a cycle).
  • In BuildCollection{Resource,DataSource,Provider} (the array case that the real schema hits) and in the object-property loops in attribute.go (direct object self-refs), if the item/property references an already-visited schema, the attribute is built via the new BuildDynamic{Resource,DataSource,Provider} helpers rather than recursing.
  • New internal/mapper/oas/dynamic.go (builders) and internal/mapper/attrmapper/dynamic.go (Resource/DataSource/ProviderDynamicAttribute, mirroring the existing string attrmapper types and using the spec's Dynamic attribute type).
  • Regression test in collection_test.go builds an object whose nested_columns array items $ref back to the enclosing schema and asserts the result is a *attrmapper.ResourceDynamicAttribute and that the build returns instead of overflowing.

Cross-dependency

The downstream tool terraform-plugin-codegen-framework must be able to consume a spec that now contains a dynamic attribute. That fork did not previously support dynamic attributes; support is added in the companion PR starburstdata/terraform-plugin-codegen-framework#3. Both are needed for the end-to-end codegen pipeline to succeed on the recursive schema.

Base branch note

This branch is based on the exact commit the provider pins (a3f0d68, the resolved SHA of pseudo-version v0.0.0-20250916180948-a3f0d6803323) so the fix is directly consumable by the provider. main is one commit ahead (c3e7ba4, "sensitive override support"); that commit touches only the scalar attrmapper files and does not conflict with this change, so it merges cleanly into main.

Pre-existing test failures (also fixed in this PR)

Two test failures were present on the base commit, unrelated to the dynamic-attribute fix above. Brady asked for these to be repaired here as well, so the branch is fully green:

  • TestBuildSchema_Errors/too_many_allOf: stale test fixture. The earlier discriminator-support commit (0dffe9d) added composeAllOfSchemas, which intentionally merges properties across multiple allOf subschemas (needed for inheritance-style composition) instead of erroring. The test still expected the old "schema composition is currently not supported" error for len(allOf) > 1, which no longer occurs. Removed the obsolete error-path fixture and added a passing case to TestBuildSchema_AllOfSchemaComposition that verifies two allOf object subschemas correctly merge their properties.
  • internal/mapper/*_mapper_test.go build failure: the same discriminator-support commit added a *high.Document parameter to NewResourceMapper, NewDataSourceMapper, and NewProviderMapper (needed to resolve $refs in discriminator mappings), but the test call sites in resource_mapper_test.go, datasource_mapper_test.go, and provider_mapper_test.go were never updated, so the internal/mapper test package failed to compile. Updated the four call sites to pass nil for the document (none of those test cases exercise discriminator resolution).

go build ./... and go test ./... are both clean on this branch now.


Generated by Claude Code

…tack overflow

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011drBEhNrtGfnXQNsedmJLa
Update NewResourceMapper/NewDataSourceMapper/NewProviderMapper test
call sites to pass the *high.Document parameter added by the
discriminator support commit (0dffe9d) but never updated in the
mapper test files, which broke compilation of the internal/mapper
test package.

Also fix the stale TestBuildSchema_Errors/too_many_allOf case: that
same commit added composeAllOfSchemas to legitimately support
composing multiple allOf subschemas (for inheritance patterns), so
allOf with more than one subschema is no longer an error. Remove the
obsolete error-path fixture and add a passing coverage case to
TestBuildSchema_AllOfSchemaComposition verifying multi-schema property
merging.
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.

1 participant