fix(csharp): make generichost oneOf constructors public

Closed
KA
3mo ago
+687-469

Description

Fixes #23046.

The C# Generic Host template previously used model mutability (x-model-is-mutable) to determine the visibility of oneOf constructors. However, mutability does not reliably indicate whether callers need to construct a model: an immutable oneOf wrapper may still be used as an API input.

This change adds the codegen vendor extension x-model-is-operation-input. It identifies models reachable from operation parameters or request bodies, including through:

  • referenced and composed schemas (oneOf, allOf, and anyOf)
  • object properties
  • array items
  • schema-valued additionalProperties
  • parameter and request-body media-type content

Note: Read-only schema branches are excluded.

For a oneOf constructor, the template first checks x-model-is-operation-input:

  • If the flag is present, the constructor uses the configured visibility:
    public by default or internal when nonPublicApi=true.
  • If the flag is absent, the constructor is always internal.

Example

Given this OpenAPI document:

openapi: 3.0.3
info:
  title: Upload API
  version: 1.0.0

paths:
  /upload:
    post:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InitiateUploadRequest'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseOnlyValue'

components:
  schemas:
    InitiateUploadRequest:
      type: object
      properties:
        dynamicMetadata:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/DynamicMetadataValue'

    DynamicMetadataValue:
      oneOf:
        - type: string
        - type: number

    ResponseOnlyValue:
      oneOf:
        - type: string
        - type: integer

DynamicMetadataValue is reachable from the /upload request body through InitiateUploadRequest.dynamicMetadata.additionalProperties. Its constructors therefore use the configured visibility, which is public by default:

public DynamicMetadataValue(string value)
public DynamicMetadataValue(decimal value)

ResponseOnlyValue is referenced only by the response. It is not an operation input, so its constructors remain internal:

internal ResponseOnlyValue(string value)
internal ResponseOnlyValue(int value)

Compatibility note

Note that this change may alter the generated API surface for some oneOf models. Constructors that were previously public because the model was considered mutable will now be internal when the model is only used in responses or is not reachable from an operation input.

This is intentional, but consumers that manually construct response-only oneOf models may need to update their code.

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
    @mandrean (2017/08) @shibayan (2020/02) @Blackclaws (2021/03) @lucamazzanti (2021/05) @iBicha (2023/07)

Summary by cubic

Make C# Generic Host oneOf model constructors public only for operation inputs (request bodies and parameters). Fixes #23046 so immutable oneOf inputs are constructible across assemblies by default, while non-input oneOf constructors remain internal.

  • Bug Fixes
    • Emit {{>visibility}} for Generic Host oneOf constructors and set to public only when the model is an operation input; otherwise internal. Respects nonPublicApi=true.
    • Detect operation input models in the generator (x-model-is-operation-input); updated template and codegen, added unit test and minimal OpenAPI fixture, and regenerated samples.