Skip to content

Honor x-ms-client-flatten in Bicep type generator#12001

Open
willdavsmith wants to merge 11 commits into
radius-project:mainfrom
willdavsmith:type-flatten
Open

Honor x-ms-client-flatten in Bicep type generator#12001
willdavsmith wants to merge 11 commits into
radius-project:mainfrom
willdavsmith:type-flatten

Conversation

@willdavsmith

@willdavsmith willdavsmith commented May 27, 2026

Copy link
Copy Markdown
Contributor

Description

Implements x-ms-client-flatten in the Radius Bicep type generator so the ARM .properties. envelope no longer leaks into user-authored Bicep templates.

Before:

container.properties.container.image
gateway.properties.url

After:

container.container.image
gateway.url

The annotation is already present on every Radius resource (via TrackedResourceRequired in typespec/radius/v1/trackedresource.tsp); the generator just had not been honoring it.

Type of change

  • This pull request adds or changes features of Radius and has an approved issue (issue link required).

Fixes: #12000

Contributor checklist

  • Existing tests pass
  • New tests added (unit + integration + functional)
  • Generated artifacts refreshed
  • [N/A] Schema/typespec changes — annotation already existed
  • [N/A] Sample/doc Bicep rewrites — deferred to a follow-up; this PR is backward compatible

Changes

Generator (hack/bicep-types-radius/src/autorest.bicep)

  • src/type-generator.ts
    • New helpers: isFlattenSafe, flagsForFlattenedChild, expandFlattenedInto.
    • Wire flattening into both processResourceBody (resource envelope) and parseObjectType (nested objects).
    • Fall back to the nested representation (with a warning via logWarning) when flattening would lose information:
      • The schema uses a discriminator (polymorphic variants).
      • A flattened child name collides with an existing parent property.
    • Parent ReadOnly / WriteOnly flags are OR'd into each flattened child. Required / Identifier / DeployTimeConstant are intentionally not propagated.

Tests

  • test/integration/specs/basic/.../spec.json — extended with:
    • TestType1 — happy-path flatten on a resource body.
    • TestType2 — polymorphic child, exercises the discriminator fallback.
    • TestType3 — child property names collide with the resource envelope, exercises the collision fallback.
  • Checked-in baselines (test/integration/generated/basic/...) refreshed.
  • New programmatic Jest suite test/flatten/flatten.test.ts asserts the contract end-to-end: walks the generated types.json and verifies hoisting, fallback emission, and warning behavior.

Regenerated Bicep types

Ran make generate-bicep-types. Updated:

  • hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json
  • hack/bicep-types-radius/generated/applications/applications.datastores/2023-10-01-preview/types.json
  • hack/bicep-types-radius/generated/applications/applications.messaging/2023-10-01-preview/types.json
  • hack/bicep-types-radius/generated/radius/radius.core/2025-08-01-preview/types.json
  • hack/bicep-types-radius/generated/index.json

Spot-checked: e.g. Applications.Core/containers now exposes application, container, connections, extensions, environment, identity, restartPolicy, runtimes, resources, provisioningState, status directly on the resource body, with no ApplicationProperties / ContainerProperties wrapper.

Functional test

test/functional-portable/corerp/noncloud/resources/container_flatten_test.go + testdata/corerp-resources-container-flatten.bicepTest_Container_Flatten deploys an Application + Container written entirely in the flat syntax (no .properties.) and validates the RP resources and the rendered pod in the app namespace. The bicep template only compiles against the regenerated types, so the test doubles as a regression guard.

Compatibility

  • Flattening is purely additive on the Bicep type-generator side. Existing templates that still use .properties.{ ... } continue to compile because properties was already required and is preserved when fallbacks fire. However, for resources where flattening succeeds (which is the vast majority), the legacy properties: { ... } syntax will no longer be valid — users must adopt the flat syntax for those resources. This is the intended UX change.
  • Existing sample/doc/test Bicep files in this repo are NOT rewritten in this PR; that is a deliberately scoped follow-up.

Deployment-engine dependency (resolved)

The deploy-time half of this feature shipped in azure-octo/deployment-engine#583 (merged), which flattens the JToken stored in the reference value lookup so flat references like reference('myCtnr').application resolve at deploy time. The :latest ghcr.io/radius-project/deployment-engine image picks up the fix automatically.

Validated end-to-end on a local kind cluster prior to merge: Test_Container_Flatten passes.

Copilot AI review requested due to automatic review settings May 27, 2026 17:17
@willdavsmith willdavsmith requested review from a team as code owners May 27, 2026 17:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the Radius Autorest-based Bicep type generator to honor x-ms-client-flatten, hoisting fields from flattened object properties (notably the ARM properties envelope) onto the parent resource/object shape to remove .properties. from user-authored Bicep.

Changes:

  • Implement x-ms-client-flatten in the generator for both resource bodies and nested object types, with safe fallbacks (discriminator / name-collision) and warnings.
  • Add/extend integration + functional tests to validate flattening behavior end-to-end and refresh integration baselines.
  • Regenerate published Radius Bicep type artifacts (generated/**/types.json and generated/index.json) and add a functional RP test + template using the new flat authoring syntax.

Reviewed changes

Copilot reviewed 5 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/functional-portable/corerp/noncloud/resources/testdata/corerp-resources-container-flatten.bicep New functional-test Bicep template exercising flat (no .properties) authoring syntax.
test/functional-portable/corerp/noncloud/resources/container_flatten_test.go New RP functional test deploying the flat-syntax template and validating RP + K8s outputs.
hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts Core generator change: implement safe flattening + warnings in resource and object parsing.
hack/bicep-types-radius/src/autorest.bicep/test/flatten/flatten.test.ts New Jest suite running Autorest end-to-end and asserting flatten contract on emitted types.json.
hack/bicep-types-radius/src/autorest.bicep/test/integration/specs/basic/resource-manager/Test.Rp1/stable/2021-10-31/spec.json Extends integration spec with flatten happy-path + discriminator/collision fallback cases.
hack/bicep-types-radius/src/autorest.bicep/test/integration/generated/basic/test.rp1/2021-10-31/types.json Refreshed baseline types output reflecting flattening and new test resources.
hack/bicep-types-radius/src/autorest.bicep/test/integration/generated/basic/test.rp1/2021-10-31/types.md Refreshed markdown baseline reflecting flattened shapes and new resources.
hack/bicep-types-radius/src/autorest.bicep/test/integration/generated/basic/test.rp1/2021-10-31/docs/testtype1.md New/updated per-resource docs baseline for flattened TestType1.
hack/bicep-types-radius/src/autorest.bicep/test/integration/generated/basic/test.rp1/2021-10-31/docs/testtype2.md New per-resource docs baseline for discriminator fallback TestType2.
hack/bicep-types-radius/src/autorest.bicep/test/integration/generated/basic/test.rp1/2021-10-31/docs/testtype3.md New per-resource docs baseline for collision fallback TestType3.
hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json Regenerated published types with flattened resource bodies (notably removing wrapper property objects).
hack/bicep-types-radius/generated/applications/applications.datastores/2023-10-01-preview/types.json Regenerated published types with flattened resource bodies.
hack/bicep-types-radius/generated/applications/applications.messaging/2023-10-01-preview/types.json Regenerated published types with flattened resource bodies.
hack/bicep-types-radius/generated/radius/radius.core/2025-08-01-preview/types.json Regenerated published types with flattened resource bodies.
hack/bicep-types-radius/generated/index.json Updates index refs to match regenerated type-array indices.

Comment on lines +115 to +118
beforeAll(async () => {
const stagingDir = path.join(__dirname, "temp");
types = await generate(stagingDir);
}, 120000);
Comment on lines +17 to +24
// Functional test for x-ms-client-flatten support in the Bicep type generator.
//
// This test runs autorest end-to-end against the shared "basic" spec (which
// includes resources exercising the happy-path flatten, the polymorphic-child
// fallback, and the name-collision fallback) and asserts directly on the
// generated types.json. Unlike the integration test (which only baseline-diffs
// the output), this test fails with a focused error message if the flatten
// contract regresses.
@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.13%. Comparing base (1dbd9a1) to head (0438fb6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #12001   +/-   ##
=======================================
  Coverage   52.13%   52.13%           
=======================================
  Files         734      734           
  Lines       46704    46704           
=======================================
  Hits        24350    24350           
  Misses      20017    20017           
  Partials     2337     2337           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Implements flattening for properties annotated with x-ms-client-flatten=true
in the Radius autorest.bicep type generator, eliminating the .properties.
envelope from Bicep authoring for Radius resources.

Generator changes (hack/bicep-types-radius/src/autorest.bicep):
- Add isFlattenSafe, flagsForFlattenedChild, expandFlattenedInto helpers in
  type-generator.ts.
- Wire flattening into processResourceBody and parseObjectType so the
  extension is honored on both the resource envelope and on nested objects.
- Fall back to the nested representation (with a warning) when flattening
  would lose information: schemas using a discriminator (polymorphic
  variants) or schemas whose flattened child names would collide with an
  existing parent property.
- Propagate parent ReadOnly/WriteOnly flags to flattened children;
  Required/Identifier/DeployTimeConstant are not propagated.

Tests:
- Extend the autorest.bicep integration spec with TestType1 (happy path),
  TestType2 (discriminator fallback), TestType3 (name-collision fallback)
  and refresh checked-in baselines.
- Add programmatic functional test test/flatten/flatten.test.ts that asserts
  the flatten contract end-to-end against the regenerated types.json.
- Regenerate Radius Bicep types (applications.core, datastores, messaging,
  radius.core, generated/index.json) so users get the flat syntax.
- Add Radius functional test Test_Container_Flatten under
  test/functional-portable/corerp/noncloud/resources demonstrating the
  flat authoring syntax for Applications.Core/applications and
  Applications.Core/containers.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Extend the functional test template with output statements that read
flattened fields back from the deployed resources (app.environment,
container.application, container.container.image,
container.container.ports.web.containerPort). If the type generator had
failed to hoist any of these, Bicep compilation would fail and the deploy
step would error out, so the outputs act as a smoke test that the flat
syntax works symmetrically for authoring and for cross-resource
references.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
…ences

Replace the bicep `output` statements with a second container resource
(`ctnr2`) that declares its application, image, and listening port by
reading flattened fields directly off the first container (`ctnr`). Also
rename the first container's bicep symbol from `container` to `ctnr` to
disambiguate it from the inner `container` field. This is a closer
analogue to how real templates consume flattened fields from other
resources.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
willdavsmith and others added 5 commits May 27, 2026 15:35
Preserve the writable properties envelope (so existing Bicep templates
compile unchanged and the wire payload still matches the RP's OpenAPI
schema) while ALSO hoisting each child of properties onto the parent
object as a ReadOnly alias. This gives users a clean reference syntax
(e.g. ctnr.container.image) without breaking authoring.

- type-generator.ts: flagsForFlattenedChild forces ReadOnly and strips
  Required; processResourceBody and parseObjectType no longer skip
  emitting 'properties' after hoisting children.
- flatten.test.ts: assert hoisted children are ReadOnly + properties
  envelope is preserved.
- Re-recorded integration baselines; regenerated Radius Bicep types.
- Functional test authors via legacy envelope and reads via flat aliases.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Implements response-side flatten in the ARM-RPC layer so Bicep templates
can reference flat field names on Radius resources (e.g. ctnr.application
instead of ctnr.properties.application). This pairs with the additive
ReadOnly aliases emitted by the Bicep type generator so that flat
references actually resolve at deploy time via ARM's reference() function.

- New helper flattenPropertiesAliases walks a marshaled body, splats each
  child of 'properties' onto the parent object as an alias, and preserves
  the original 'properties' envelope unchanged. Reserved envelope keys are
  never overwritten and existing top-level keys win on collision.
- Wired into OKResponse, CreatedResponse, CreatedAsyncResponse and
  AcceptedAsyncResponse via a shared marshalResourceBody helper. Flatten
  is best-effort: on error the request still succeeds with the unflattened
  body.
- PaginatedList bodies are recursed; non-resource bodies (async operation
  status, error envelopes) are pass-through.
- 14 new table-driven unit tests + alias-identity test.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Documents the response-side flatten + Bicep type aliases approach,
non-goals, reserved-keys list, collision rule, alternatives considered,
risks, and test coverage.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Signed-off-by: willdavsmith <willdavsmith@gmail.com>
@willdavsmith willdavsmith marked this pull request as draft May 28, 2026 21:11
Decoding each resource response on the wire and splatting properties to
the top level has no effect on ARM reference() resolution, because the
deployment engine rebuilds the resource shape from its typed model
before evaluating expressions. The wire-level aliases are silently
dropped on the way to the expression engine.

The flat-reference behavior now lives in the companion
deployment-engine change
(azure-octo/deployment-engine#willdavsmith/flatten-properties-aliases),
which flattens the JToken stored in referenceValueLookup so that
reference('r').foo resolves to reference('r').properties.foo.

This commit reverts the RP-side flatten helper and its wiring on the
four success Apply methods, and rewrites the design note to reflect
the deployment-engine approach.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
@nicolejms nicolejms requested a review from brooke-hamilton June 2, 2026 18:38
Dropping the design note from this PR. The feature is documented in the PR
body and the existing in-code commentary in the Bicep type generator
(hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts) and the
deployment-engine flatten helper.

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Signed-off-by: willdavsmith <willdavsmith@gmail.com>
@willdavsmith willdavsmith marked this pull request as ready for review June 8, 2026 18:51
@radius-functional-tests

radius-functional-tests Bot commented Jun 8, 2026

Copy link
Copy Markdown

Radius functional test overview

🔍 Go to test action run

Click here to see the test run details
Name Value
Repository willdavsmith/radius
Commit ref 0438fb6
Unique ID func58d7589405
Image tag pr-func58d7589405
  • gotestsum 1.13.0
  • KinD: v0.29.0
  • Dapr: 1.14.4
  • Azure KeyVault CSI driver: 1.4.2
  • Azure Workload identity webhook: 1.3.0
  • Bicep recipe location ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-func58d7589405
  • Terraform recipe location http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip (in cluster)
  • applications-rp test image location: ghcr.io/radius-project/dev/applications-rp:pr-func58d7589405
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-func58d7589405
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-func58d7589405
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-func58d7589405
  • deployment-engine test image location: ghcr.io/radius-project/deployment-engine:latest

Test Status

⌛ Building Radius and pushing container images for functional tests...
✅ Container images build succeeded
⌛ Publishing Bicep Recipes for functional tests...
✅ Recipe publishing succeeded
⌛ Starting corerp-cloud functional tests...
⌛ Starting ucp-cloud functional tests...
✅ ucp-cloud functional tests succeeded
✅ corerp-cloud functional tests succeeded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bicep authoring: drop the .properties. envelope by honoring x-ms-client-flatten

3 participants