Skip to content

feat: support HEAD operations as boolean or { body: boolean } in Modular SDK with head-as-boolean and wrap-non-model-return#3887

Merged
MaryGao merged 12 commits intomainfrom
copilot/migration-tsp-support-httpmethod-head
Apr 8, 2026
Merged

feat: support HEAD operations as boolean or { body: boolean } in Modular SDK with head-as-boolean and wrap-non-model-return#3887
MaryGao merged 12 commits intomainfrom
copilot/migration-tsp-support-httpmethod-head

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

A new head-as-boolean option (mirroring HLC's --head-as-boolean) enables boolean return types for HEAD operations with no response body in the Modular SDK. The behavior depends on the combination of head-as-boolean and wrap-non-model-return:

wrap-non-model-return head-as-boolean Return type
true true Promise<{ body: boolean }>
false true Promise<boolean>
true false Promise<void>
false false Promise<void>

The dedicated head-as-boolean opt-in prevents unintended changes to HEAD operations like headIfModifiedSince (conditional request headers) which must continue to return void.

Changes

rlc-common/src/interfaces.ts

  • Added headAsBoolean?: boolean to the RLCOptions interface

typespec-ts/src/lib.ts

  • Added "head-as-boolean" emitter option with JSON schema description

typespec-ts/src/transform/transfromRLCOptions.ts

  • Wired head-as-boolean emitter option through to headAsBoolean in RLCOptions

typespec-ts/test/util/emitUtil.ts

  • Wired head-as-boolean option in the test utility so scenario YAML configs can set it

operationHelpers.ts

  • isHeadOperation() — new helper to centralize the HTTP verb check
  • checkWrapNonModelReturn() — HEAD + void response returns { shouldWrap: true } only when both wrapNonModelReturn and headAsBoolean are enabled
  • buildNonModelResponseTypeDeclaration() — emits { body: boolean } type body for HEAD void operations when both head-as-boolean and wrap-non-model-return are set
  • getDeserializePrivateFunction() — two cases:
    • With wrap-non-model-return: true: generates return { body: result.status.startsWith("2") }
    • With wrap-non-model-return: false: generates return result.status.startsWith("2") (plain boolean)
  • getOperationFunction() — returns Promise<boolean> when head-as-boolean: true and wrap-non-model-return: false

Examples

With wrap-non-model-return: true and head-as-boolean: true:

export type CheckExistenceResponse = { body: boolean };

export async function _checkExistenceDeserialize(
  result: PathUncheckedResponse,
): Promise<CheckExistenceResponse> {
  const expectedStatuses = ["204", "404"];
  if (!expectedStatuses.includes(result.status)) {
    throw createRestError(result);
  }
  return { body: result.status.startsWith("2") };
}

With wrap-non-model-return: false and head-as-boolean: true:

export async function _headResourceDeserialize(
  result: PathUncheckedResponse,
): Promise<boolean> {
  const expectedStatuses = ["204"];
  if (!expectedStatuses.includes(result.status)) {
    throw createRestError(result);
  }
  return result.status.startsWith("2");
}

HEAD operations without head-as-boolean: true continue to return Promise<void> unchanged.

Tests

Unit test scenarios in wrapNonModelReturn.md cover all four combinations of wrap-non-model-return and head-as-boolean for HEAD operations.

Copilot AI and others added 2 commits April 2, 2026 02:04
… wrap-non-model-return enabled

Agent-Logs-Url: https://github.com/Azure/autorest.typescript/sessions/f170ca53-8db2-4eaf-b1a0-06ef4ce0bb46

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for HttpMethod.Head in Modular feat: support HEAD operations as { body: boolean } in Modular SDK with wrap-non-model-return Apr 2, 2026
Copilot AI requested a review from v-jiaodi April 2, 2026 02:07
@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Apr 2, 2026

@copilot Following HLC’s implementation logic https://github.com/Azure/autorest.typescript/blob/main/packages/autorest.typescript/src/generators/modelsGenerator.ts#L224, validate the current fix and resolve the CI failures.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 2, 2026

@copilot Following HLC’s implementation logic https://github.com/Azure/autorest.typescript/blob/main/packages/autorest.typescript/src/generators/modelsGenerator.ts#L224, validate the current fix and...

Following the HLC logic which requires headAsBoolean to be explicitly set (not just any HEAD operation), I've:

  1. Added a separate head-as-boolean emitter option (mirroring HLC's head-as-boolean) so only services that explicitly opt-in get { body: boolean } wrapping for HEAD void operations
  2. The root cause of the CI failures was that wrap-non-model-return defaults to true for Azure flavor, causing ALL HEAD operations (including headIfModifiedSince for conditional request headers) to incorrectly get boolean wrapping

Key changes in d9b8c91:

  • Added headAsBoolean to RLCOptions in rlc-common
  • Added head-as-boolean option to lib.ts and wired through transfromRLCOptions.ts
  • Updated checkWrapNonModelReturn to require BOTH wrapNonModelReturn and headAsBoolean (matching HLC's headAsBoolean && addedResponses.length === 0)
  • headIfModifiedSince now correctly still returns Promise<void>
  • All 620 unit tests pass

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dev.azure.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title feat: support HEAD operations as { body: boolean } in Modular SDK with wrap-non-model-return feat: support HEAD operations as { body: boolean } in Modular SDK with wrap-non-model-return and head-as-boolean Apr 2, 2026
@v-jiaodi v-jiaodi marked this pull request as ready for review April 2, 2026 05:10
@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Apr 2, 2026

@copilot According to typespec operation checkExistenceAtManagementGroupScope to add UT https://github.com/Azure/azure-rest-api-specs/blob/99cfa558b8ed9c11464a426f401406531c26d6ce/specification/resources/resource-manager/Microsoft.Resources/deployments/DeploymentExtended.tsp#L447

  checkExistenceAtManagementGroupScope is Extension.CheckExistence<
    Extension.ManagementGroup<"groupId">,
    DeploymentExtended,
    Error = CloudError
  >;

expected response code is xxxResponse and xxxResponse = { body: boolean};

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 2, 2026

@copilot According to typespec operation checkExistenceAtManagementGroupScope to add UT https://github.com/Azure/azure-rest-api-specs/blob/99cfa558b8ed9c11464a426f401406531c26d6ce/specification/resour...

Added in d42161e. The new scenario in wrapNonModelReturn.md models the exact ARM pattern from the referenced spec:

@route("/providers/Microsoft.Management/managementGroups/{groupId}/providers/Microsoft.Resources/deployments/{deploymentName}")
@head
op checkExistenceAtManagementGroupScope(
  @path groupId: string,
  @path deploymentName: string,
): { @statusCode _: 204; } | { @statusCode _: 404; };

With wrap-non-model-return: true + head-as-boolean: true, it generates:

  • CheckExistenceAtManagementGroupScopeResponse = { body: boolean }
  • _checkExistenceAtManagementGroupScopeDeserialize returning { body: result.status.startsWith("2") }

@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Apr 2, 2026

@copilot revert commit d42161e895c43f302c1ddbf958f338c5b263298d

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 2, 2026

@copilot revert commit d42161e895c43f302c1ddbf958f338c5b263298d

Reverted in 7163ccc. The ARM-style checkExistenceAtManagementGroupScope scenario has been removed from wrapNonModelReturn.md.

@lirenhe
Copy link
Copy Markdown
Member

lirenhe commented Apr 3, 2026

@qiaozha, could you help to review this PR? thanks

Comment thread packages/rlc-common/src/interfaces.ts
Comment thread packages/typespec-ts/src/modular/helpers/operationHelpers.ts Outdated
@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Apr 8, 2026

@copilot please resolve comment #3887 (comment) and #3887 (comment)

…odel-return is false

Agent-Logs-Url: https://github.com/Azure/autorest.typescript/sessions/4c293779-5e0d-4d27-a332-82eec0ff0ad4

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

@copilot please resolve comment #3887 (comment) and #3887 (comment)

Resolved in 0a49d1d. Both cases are now handled correctly:

  • Case 1 (wrap-non-model-return: true + head-as-boolean: true): returns Promise<{ body: boolean }>
  • Case 2 (wrap-non-model-return: false + head-as-boolean: true): now returns Promise<boolean> (plain boolean, no wrap) ✓

Changes: getDeserializePrivateFunction and getOperationFunction both check for !response.type && isHeadOperation && headAsBoolean && !shouldWrap to return boolean. The test for wrap-non-model-return: false, head-as-boolean: true is updated accordingly.

Copilot AI changed the title feat: support HEAD operations as { body: boolean } in Modular SDK with wrap-non-model-return and head-as-boolean feat: support HEAD operations as boolean or { body: boolean } in Modular SDK with head-as-boolean and wrap-non-model-return Apr 8, 2026
@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Apr 8, 2026

@copilot Run pnpm format to fix ci error.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

@copilot Run pnpm format to fix ci error.

Done in e55eb5f. The formatter wrapped a long conditional in operationHelpers.ts.

@MaryGao MaryGao merged commit dec6ef0 into main Apr 8, 2026
16 checks passed
@MaryGao MaryGao deleted the copilot/migration-tsp-support-httpmethod-head branch April 8, 2026 05:56
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.

[migration tsp] Modular should support HttpMethod.Head the same way HLC does

4 participants