Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusable (Request/Response) Object #2122

Closed
messerm opened this issue Apr 18, 2019 · 5 comments · Fixed by #4917
Closed

Reusable (Request/Response) Object #2122

messerm opened this issue Apr 18, 2019 · 5 comments · Fixed by #4917

Comments

@messerm
Copy link

messerm commented Apr 18, 2019

Hi,
we have reusable Request/Response Objects defined in our openApi 3 yaml file but the generated output doesn't seem to support this feature for request (and response) definition of a path.
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#components-object

Open API schema:

openapi: 3.0.0
servers:
  - url: https://www.example.com/
info:
  version: "0.4.13"
  title: 'Test API'
################################################################
#
# PATHS
#
################################################################
paths:
  /projects:
    post:
      tags:
        - Project
      summary: 'Add project'
      operationId: addProject
      description: Add a project
      responses:
        '200':
          description: Succeeded
          content:
            application/json:
              schema:
                type: object
                required:
                  - name
                properties:
                  name:
                    type: string
      requestBody:
        $ref: '#/components/requestBodies/AddProjectRequestBody'

components:
  requestBodies:
    AddProjectRequestBody:
      description: Project to add
      content:
        application/json:
          schema:
            type: object
            required:
              - name
            properties:
              name:
                type: string
                description: name of the project
              color:
                type: number
                description: color as integer for the project

Take the following generation code:

var document = await SwaggerYamlDocument.FromFileAsync("openapi.yaml");

var settings = new SwaggerToCSharpClientGeneratorSettings();

var generator = new SwaggerToCSharpClientGenerator(document, settings);

var output = generator.GenerateFile(ClientGeneratorOutputType.Full);

await File.WriteAllTextAsync("Output.cs", output);

Output.cs contains the following code:

[System.CodeDom.Compiler.GeneratedCode("NSwag", "12.1.0.0 (NJsonSchema v9.13.28.0 (Newtonsoft.Json v9.0.0.0))")]
public partial interface IMyServiceClient
{
    /// <summary>Add project</summary>
    /// <returns>Succeeded</returns>
    /// <exception cref="MyServiceException">A server side error occurred.</exception>
    System.Threading.Tasks.Task<Response> AddProjectAsync(object body);

    /// <summary>Add project</summary>
    /// <returns>Succeeded</returns>
    /// <exception cref="MyServiceException">A server side error occurred.</exception>
    /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    System.Threading.Tasks.Task<Response> AddProjectAsync(object body, System.Threading.CancellationToken cancellationToken);
}

Defining the same using schemas works well:
Open API schema:

openapi: 3.0.0
servers:
  - url: https://www.example.com/
info:
  version: "0.4.13"
  title: 'Test API'
################################################################
#
# PATHS
#
################################################################
paths:
  /projects:
    post:
      tags:
        - Project
      summary: 'Add project'
      operationId: addProject
      description: Add a project
      responses:
        '200':
          description: Succeeded
          content:
            application/json:
              schema:
                type: object
                required:
                  - name
                properties:
                  name:
                    type: string
      requestBody:
        description: Project to add
        content:
          application/json:
            schema:
               $ref: '#/components/schemas/AddProjectRequestBody'

components:
  schemas:
    AddProjectRequestBody:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: name of the project
        color:
          type: number
          description: color as integer for the project

output:

[System.CodeDom.Compiler.GeneratedCode("NSwag", "12.1.0.0 (NJsonSchema v9.13.28.0 (Newtonsoft.Json v9.0.0.0))")]
public partial interface IMyServiceClient
{
    /// <summary>Add project</summary>
    /// <param name="body">Project to add</param>
    /// <returns>Succeeded</returns>
    /// <exception cref="MyServiceException">A server side error occurred.</exception>
    System.Threading.Tasks.Task<Response> AddProjectAsync(AddProjectRequestBody body);

    /// <summary>Add project</summary>
    /// <param name="body">Project to add</param>
    /// <returns>Succeeded</returns>
    /// <exception cref="MyServiceException">A server side error occurred.</exception>
    /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    System.Threading.Tasks.Task<Response> AddProjectAsync(AddProjectRequestBody body, System.Threading.CancellationToken cancellationToken);
}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.13.28.0 (Newtonsoft.Json v9.0.0.0)")]
public partial class AddProjectRequestBody 
{
    /// <summary>name of the project</summary>
    [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)]
    [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
    public string Name { get; set; }

    /// <summary>color as integer for the project</summary>
    [Newtonsoft.Json.JsonProperty("color", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public double Color { get; set; }
}

Is this not supported or am I missing something? I can assume that this isn't an easy task since one can have different content types which would make generating the payload object more complicated.

Martyn

@lakeman
Copy link

lakeman commented Sep 20, 2022

I've hacked together basic support for parsing & codegen lakeman@4528a59

A proper fix would probably add public OpenApiRequestBody ActualRequestBody to OpenApiOperation and replace most uses of .RequestBody, but I wanted to minimise the impact of my changes.

That and some test cases...

@gotti619
Copy link

gotti619 commented Oct 11, 2023

Hi,

I have the same issue here.
I'm referening to requestBodies, but I can't get the reference:

paths:
  /test:
    post:
      operationId: createTest
      summary: Create a test event
      description: Test description
      requestBody:
        $ref: '#/components/requestBodies/TestFail'
components:
  requestBodies:
    TestFail:
      type: object
      allOf:
        - $ref: '#/components/schemas/ExtendedSchema'
        - properties:
            id:
              type: string
              description: Unique ID
  schemas:
    ....

In C#, the Content is empty:

NSwag.OpenApiOperation Operation;

if(Operation?.RequestBody?.Content?.Keys.Count > 0)
{
    //Content has no keys
}

@MrPerun
Copy link

MrPerun commented Oct 18, 2023

Same problem for me, openApi with requestBodies, and type of client method parameter is generated as object? instead of concrete type.

@gotti619
Copy link

5 years for a fix lol, but thanks for the update!

@Eruzo
Copy link

Eruzo commented Jul 26, 2024

Just tested this with our contracts (most recent master branch). It does not work. Still get object? body. See below for some stripped down oas contract

/path/{arg}/collection/search:
    post:
      tags:
      - "collection"
      summary: "Search collection"
      description: ""
      operationId: "PostCollectionSearch"
      requestBody:
        $ref: "#/components/requestBodies/PostCollectionSearchRequest"
        
requestBodies:
    PostCollectionSearchRequest:
      description: "The request body of PostCollectionSearch.\n"
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/CollectionSearchCriteria"

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 a pull request may close this issue.

5 participants