Skip to content

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

Merged
willdavsmith merged 17 commits into
radius-project:mainfrom
willdavsmith:type-flatten
Jun 12, 2026
Merged

Honor x-ms-client-flatten in Bicep type generator#12001
willdavsmith merged 17 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.
    • Each flattened child is emitted as ReadOnly. Required, WriteOnly, Identifier, and DeployTimeConstant are intentionally not propagated from the wrapper — the flat alias is unambiguously a read-side projection of the underlying properties payload.

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 and read-side only. The wrapper properties: { ... } is preserved as the writable authoring surface — existing templates that author via .properties.{ ... } continue to work unchanged. Hoisted flat aliases are emitted with the ReadOnly flag, so they can be consumed via reference('foo').bar / output x = foo.bar but cannot be assigned from a template body. This is intentional: it eliminates .properties. from read-side expressions without requiring any breaking authoring-side 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.

@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.24%. Comparing base (c43a316) to head (4ea1842).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12001      +/-   ##
==========================================
- Coverage   52.26%   52.24%   -0.02%     
==========================================
  Files         736      736              
  Lines       47033    47033              
==========================================
- Hits        24581    24573       -8     
- Misses      20100    20104       +4     
- Partials     2352     2356       +4     

☔ 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

@brooke-hamilton brooke-hamilton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🚀

Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
Comment thread hack/bicep-types-radius/src/autorest.bicep/test/flatten/flatten.test.ts Outdated
Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts Outdated
Comment thread hack/bicep-types-radius/src/autorest.bicep/test/flatten/flatten.test.ts Outdated
Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
type-generator.ts:
- Mask WriteOnly from flattened-child flags (brooke-hamilton, L501). Without
  this, a PUT-only child would be emitted as ReadOnly|WriteOnly — a
  contradictory combination that contradicts the doc-comment. Latent today
  because the test specs are symmetric, but the code/comment mismatch is fixed.
- Remove the unused parentFlags parameter from flagsForFlattenedChild and
  expandFlattenedInto (brooke-hamilton, L486). The function never read it,
  and the PR description's claim that parent flags propagated was wrong.
  Drop both call-site parentFlags computations as well.
- Change expandFlattenedInto's return type to void (brooke-hamilton, L136).
  Both call sites discarded the boolean and the function is atomic, so the
  unused return was misleading.
- Detect collisions against the *full* sibling-name set, not just keys
  already present in target (brooke-hamilton, L531). In the nested
  parseObjectType path, expansion runs mid-loop while siblings are still
  being added, so a hoisted child colliding with a *later* sibling was
  silently slipping through. Now precompute siblingNames upfront in both
  call sites and pass it to expandFlattenedInto.

flatten.test.ts:
- Use mkdtemp under os.tmpdir() and clean up in afterAll, so the staging
  output is no longer left in the source tree across runs (Copilot L118 +
  brooke-hamilton L116).
- Drop the misleading 'warning behavior' phrase from the file header
  comment — the test only asserts types.json structure (Copilot L24).
- Replace magic numbers 0x01/0x02 with ObjectTypePropertyFlags.Required /
  .ReadOnly imports (brooke-hamilton L133), and additionally assert that
  WriteOnly is masked off (matches the new generator behavior above).
- Assert that the non-colliding 'extra' child is NOT partially hoisted to
  the resource body in TestType3 (brooke-hamilton L207) — this is the
  fall-back-to-nested-representation contract.

Generated artifacts (Radius bicep-types and integration baselines) are
unchanged: the test specs use symmetric PUT/GET and have no
flatten-child-vs-later-sibling collisions, so neither latent bug fix
changes today's output.

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

Copy link
Copy Markdown
Contributor Author

Addressed all 9 review comments from @copilot and @brooke-hamilton in f98963f. Each inline thread has a detailed reply.

Summary

  • type-generator.ts

    • Mask WriteOnly from flattened child flags (L501) so a PUT-only child can never be emitted as ReadOnly | WriteOnly.
    • Drop the dead parentFlags parameter from flagsForFlattenedChild / expandFlattenedInto and the two call-site parsePropertyFlags computations that fed it (L486 / L136).
    • Change expandFlattenedInto return type to void (L136).
    • Detect flatten collisions against a precomputed sibling-name set, not just keys currently in target, so a child colliding with a later sibling in the nested path no longer silently slips through (L531). Behavior on current Radius specs is unchanged.
  • flatten.test.ts

    • mkdtemp under os.tmpdir() + afterAll cleanup so staging output no longer lives in the source tree (L116 / L118).
    • Replace magic numbers with ObjectTypePropertyFlags.{ReadOnly,Required,WriteOnly} from bicep-types and add a & WriteOnly assertion (L133).
    • Assert that the non-colliding sibling extra is not partially hoisted in the TestType3 fallback (L207).
    • Drop the misleading "warning behavior" phrase from the file header (L24).
  • PR description updated to drop the inaccurate "parent flags are OR'd into children" claim.

Verification: npm run build clean; flatten suite 6/6 green. Generated Radius bicep types are unchanged (re-ran make generate-bicep-types — no diff), because the current Radius specs don't exercise either of the latent-bug paths. The pre-existing integration-baseline trailing-newline diff is unrelated to this PR.

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

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

Comment thread hack/bicep-types-radius/src/autorest.bicep/test/flatten/flatten.test.ts Outdated
Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
Fixes the 'Format check with Prettier' CI failure introduced by the
review-feedback commit.

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

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

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

Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
Comment thread hack/bicep-types-radius/src/autorest.bicep/src/type-generator.ts
Comment thread hack/bicep-types-radius/src/autorest.bicep/test/flatten/flatten.test.ts Outdated
- type-generator.ts: track hoisted child names in siblingNames as they are
  added, so a *later* flattened wrapper that produces a colliding child
  name is detected and falls back to the nested representation instead of
  silently overwriting the earlier alias (Copilot L568). Both flatten paths
  already shared one Set per parent, so this just calls siblingNames.add()
  after each hoist.
- flatten.test.ts: remove the stray trailing '.' on the closing license
  banner separator (Copilot L15).

PR description: rewrite the Compatibility bullet that incorrectly claimed
the legacy 'properties: { ... }' authoring syntax would no longer be valid.
The implementation only adds ReadOnly flat aliases; the wrapper is
preserved as the writable authoring surface (Copilot L145).

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

Copy link
Copy Markdown
Contributor Author

Addressed all 3 new Copilot review comments in ae7fd26 (rebased on the latest main merge).

Code changes

  • type-generator.tsexpandFlattenedInto now updates siblingNames with each hoisted child name, so a second flattened wrapper producing a colliding child name on the same parent will now warn + fall back to nested representation instead of silently overwriting the earlier alias.
  • flatten.test.ts — removed stray trailing . on the license banner separator.

PR body

  • Rewrote the Compatibility bullet to match the actual behavior: flat aliases are emitted as ReadOnly (read-side use only); the properties: { ... } wrapper remains the writable authoring surface, so existing templates are unchanged.

Functional-test failures on the previous run were infra flakes, not regressions:

  • corerp-cloud Test_ACI / Test_TerraformPrivateGitModule_KubernetesRedis / Test_TerraformRecipe_AzureResourceGroup all died with Get "https://127.0.0.1:NNNN/..." EOF — port-forward to the local kind cluster dropped mid-deploy.
  • samples-noncloud Publish Terraform test recipes step timed out after 10 min waiting for the tf-module-server deployment to roll out (rollout wait error: timed out waiting for the condition).

Both are unrelated to this PR. The fresh push triggers a re-run automatically.

@radius-functional-tests

radius-functional-tests Bot commented Jun 12, 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 4ea1842
Unique ID func19a03ef341
Image tag pr-func19a03ef341
  • 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-func19a03ef341
  • 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-func19a03ef341
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-func19a03ef341
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-func19a03ef341
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-func19a03ef341
  • 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

@willdavsmith willdavsmith merged commit c571c68 into radius-project:main Jun 12, 2026
57 checks passed
Reshrahim pushed a commit to Reshrahim/radius that referenced this pull request Jun 15, 2026
# Description

Implements
[`x-ms-client-flatten`](https://github.com/Azure/autorest/tree/main/docs/extensions#x-ms-client-flatten)
in the Radius Bicep type generator so the ARM `.properties.` envelope no
longer leaks into user-authored Bicep templates.

Before:
```bicep
container.properties.container.image
gateway.properties.url
```

After:
```bicep
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: radius-project#12000

# Contributor checklist

- [x] Existing tests pass
- [x] New tests added (unit + integration + functional)
- [x] 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.
- Each flattened child is emitted as `ReadOnly`. `Required`,
`WriteOnly`, `Identifier`, and `DeployTimeConstant` are intentionally
not propagated from the wrapper — the flat alias is unambiguously a
read-side projection of the underlying `properties` payload.

## 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.bicep` —
`Test_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 and read-side only**. The wrapper
`properties: { ... }` is preserved as the writable authoring surface —
existing templates that author via `.properties.{ ... }` continue to
work unchanged. Hoisted flat aliases are emitted with the `ReadOnly`
flag, so they can be consumed via `reference('foo').bar` / `output x =
foo.bar` but cannot be assigned from a template body. This is
intentional: it eliminates `.properties.` from read-side expressions
without requiring any breaking authoring-side 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](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.

---------

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
Co-authored-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
Signed-off-by: Reshma Abdul Rahim <reshmarahim.abdul@microsoft.com>
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

4 participants