Skip to content

fix(examples): flatten SchemaTypeObject blocks in generated example manifests#657

Draft
Duologic wants to merge 1 commit into
crossplane:mainfrom
grafana:duologic/fix-schema-type-object-example-conversion
Draft

fix(examples): flatten SchemaTypeObject blocks in generated example manifests#657
Duologic wants to merge 1 commit into
crossplane:mainfrom
grafana:duologic/fix-schema-type-object-example-conversion

Conversation

@Duologic
Copy link
Copy Markdown
Contributor

Summary

  • Adds a post-processing step to ApplyAPIConverters that flattens single-element arrays at SchemaTypeObject (NestingModeSingle) field paths in generated example YAML manifests
  • Uses upjet's existing schema traverser to collect CRD paths for SchemaTypeObject fields per resource
  • Adds tests for both the path collection and the flattening logic

Background

The HCL-to-JSON scraper wraps all Terraform blocks in arrays per HCL spec. For NestingModeSingle blocks, upjet generates CRD types as embedded objects (pointer-to-struct), not slices. However, the generated example manifests retained the array syntax from the scraper, producing invalid YAML that fails with "unknown field" errors when applied.

ApplyAPIConverters (added in #538) only handles TypeList/TypeSet with MaxItems=1 via SingletonListEmbedder. SchemaTypeObject fields were not covered because:

  1. SingletonListEmbedder.VisitResource filters on Type == TypeList || TypeSet, skipping SchemaTypeObject (ValueType 9)
  2. The traverser doesn't add [*] wildcards for SchemaTypeObject paths, so they can't be passed through conversion.Convert the same way

Implementation

A separate lenient flattening function is used instead of reusing conversion.Convert, because:

  • Convert errors on non-slice values (correct for runtime), but example manifests may already have objects at some paths
  • SchemaTypeObject paths require lexical ordering (parents before children), opposite to Convert's reverse-lexical ordering for ToEmbeddedObject, because without [*] wildcards child paths are unresolvable while the parent is still an array

Who is affected

Any upjet-based provider whose Terraform provider uses the plugin framework NestingModeSingle pattern. The first provider to hit this is grafana/crossplane-provider-grafana (grafana/crossplane-provider-grafana#549).

Fixes #656

…anifests

The HCL-to-JSON scraper wraps all Terraform blocks in arrays per HCL
spec. For NestingModeSingle (SchemaTypeObject) fields, upjet generates
CRD types as embedded objects (pointer-to-struct), but the generated
example manifests retained array syntax, causing unknown field errors.

ApplyAPIConverters only handled TypeList/TypeSet with MaxItems=1 via
SingletonListEmbedder. SchemaTypeObject fields were not covered because
they use a different schema type (ValueType 9) and their paths lack [*]
wildcards.

Add a post-processing step that traverses each resource schema, collects
SchemaTypeObject CRD paths, and flattens single-element arrays at those
paths. Uses lexical ordering (parents before children) because without
wildcards, child paths are not resolvable while the parent is still an
array.

Fixes crossplane#656

Signed-off-by: Duologic <jeroen@simplistic.be>
@Duologic Duologic force-pushed the duologic/fix-schema-type-object-example-conversion branch from 53da950 to 5433818 Compare May 15, 2026 06:29
@Duologic Duologic marked this pull request as ready for review May 15, 2026 09:21
@Duologic Duologic marked this pull request as draft May 15, 2026 09:21
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds SchemaTypeObject flattening to ApplyAPIConverters, extending existing logic that flattens single-element arrays for plugin framework NestingModeSingle blocks in generated example manifests. It introduces schema traversal utilities to discover and flatten these fields, integrates them into the conversion pipeline, and includes comprehensive table-driven tests covering various nesting patterns and edge cases.

Changes

SchemaTypeObject Flattening Feature

Layer / File(s) Summary
Schema collection and flattening helpers
pkg/examples/conversion/example_conversions.go
Imports sort and fieldpath. Adds schemaTypeObjectCollector to traverse Terraform schemas, collectSchemaTypeObjectCRDPaths to gather paths for SchemaTypeObject fields, and flattenSchemaTypeObjectExamples / flattenSingleElementArray to flatten single-element arrays at those paths in example manifests, skipping missing or non-array values.
ApplyAPIConverters integration
pkg/examples/conversion/example_conversions.go
Modifies ApplyAPIConverters control flow to scope embedded-object conversions to version-matched examples. After conversions, collects SchemaTypeObject CRD paths, rewrites them under spec.forProvider., and flattens matching fields in each example's unstructured object.
Comprehensive test coverage
pkg/examples/conversion/example_conversions_test.go
Introduces test infrastructure and two test suites. TestCollectSchemaTypeObjectCRDPaths validates path collection across root-level, list-nested, sibling, and nested-object contexts. TestFlattenSchemaTypeObjectExamples verifies flattening behavior for root-level singleton arrays, nested paths with wildcard segments, parent-child scenarios, and no-op cases. Includes sortStrings and roundTrip test helpers for deterministic comparison and JSON normalization.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning Title is descriptive about the fix but exceeds the 72-character limit at 77 characters. Shorten the title to 72 characters or fewer, e.g., 'fix(examples): flatten SchemaTypeObject blocks in examples' (64 chars).
✅ Passed checks (6 passed)
Check name Status Explanation
Description check ✅ Passed Description comprehensively explains the problem, background, implementation, and impact, directly relating to the changeset.
Linked Issues check ✅ Passed The PR implements the expected behavior from issue #656: flattening single-element arrays at SchemaTypeObject field paths in generated examples using schema traversal and lenient flattening logic.
Out of Scope Changes check ✅ Passed All changes align with the stated objective: adding path collection and flattening logic for SchemaTypeObject fields, with corresponding tests, no extraneous modifications.
Configuration Api Breaking Changes ✅ Passed PR only adds new files to pkg/examples/conversion/. No pkg/config/** files are modified, deleted, or renamed. No Configuration API changes.
Generated Code Manual Edits ✅ Passed No files matching the zz_*.go pattern (generated code) were modified in this PR. Changes are limited to example_conversions.go and example_conversions_test.go, which are hand-written utility files.
Template Breaking Changes ✅ Passed No changes to pkg/controller/external*.go templates. PR only modifies example conversion logic in pkg/examples/conversion/. Check is not applicable.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/examples/conversion/example_conversions_test.go (1)

383-397: 💤 Low value

Consider using stdlib sort.Strings for consistency with Go idioms.

The bubble sort implementation works correctly, but Go's sort.Strings is more idiomatic and efficient (O(n log n) vs O(n²)). Since the function needs to return a new slice for use in cmp.Transformer, you could copy first then sort:

func sortStrings(s []string) []string {
	if s == nil {
		return nil
	}
	result := make([]string, len(s))
	copy(result, s)
	sort.Strings(result)
	return result
}

That said, the current implementation is perfectly clear and works fine for test helper purposes—this is purely a style suggestion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/examples/conversion/example_conversions_test.go` around lines 383 - 397,
Replace the manual O(n²) bubble-sort inside the helper function sortStrings with
the stdlib sort.Strings for idiomatic and faster sorting: retain the nil check
and the copy into result (used by cmp.Transformer), then call
sort.Strings(result) before returning; ensure you import "sort" in the test file
and update any references to sortStrings as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/examples/conversion/example_conversions_test.go`:
- Line 335: Update the test case's reason string in example_conversions_test.go:
change the 'reason' field that currently says "Should flatten both parent and
child SchemaTypeObject fields, processing children first." to accurately state
parent-first processing (e.g., "Should flatten both parent and child
SchemaTypeObject fields, processing parents before children.") so the test
description matches the actual behavior verified by the test (the test case with
the 'reason' field referencing parent/child SchemaTypeObject).

---

Nitpick comments:
In `@pkg/examples/conversion/example_conversions_test.go`:
- Around line 383-397: Replace the manual O(n²) bubble-sort inside the helper
function sortStrings with the stdlib sort.Strings for idiomatic and faster
sorting: retain the nil check and the copy into result (used by
cmp.Transformer), then call sort.Strings(result) before returning; ensure you
import "sort" in the test file and update any references to sortStrings as
needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f82eefa5-5565-4aa6-b70c-6aaf2b9b7588

📥 Commits

Reviewing files that changed from the base of the PR and between 3913aa2 and 5433818.

📒 Files selected for processing (2)
  • pkg/examples/conversion/example_conversions.go
  • pkg/examples/conversion/example_conversions_test.go

},
},
"FlattenNestedParentAndChild": {
reason: "Should flatten both parent and child SchemaTypeObject fields, processing children first.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the processing order in test reason.

The test reason states "processing children first" but the implementation processes parents before children (lexical ordering ensures "outer" is processed before "outer.inner", as explained in lines 67-72 of the main file). This test correctly verifies that both parent and child are flattened, but the comment should reflect the actual parent-first processing order to avoid confusing future maintainers.

📝 Suggested correction
-			reason: "Should flatten both parent and child SchemaTypeObject fields, processing children first.",
+			reason: "Should flatten both parent and child SchemaTypeObject fields, processing parents first.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reason: "Should flatten both parent and child SchemaTypeObject fields, processing children first.",
reason: "Should flatten both parent and child SchemaTypeObject fields, processing parents first.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/examples/conversion/example_conversions_test.go` at line 335, Update the
test case's reason string in example_conversions_test.go: change the 'reason'
field that currently says "Should flatten both parent and child SchemaTypeObject
fields, processing children first." to accurately state parent-first processing
(e.g., "Should flatten both parent and child SchemaTypeObject fields, processing
parents before children.") so the test description matches the actual behavior
verified by the test (the test case with the 'reason' field referencing
parent/child SchemaTypeObject).

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.

ApplyAPIConverters does not flatten NestingModeSingle (SchemaTypeObject) blocks in generated examples

1 participant