4

I'm using Java 11 and want to create an OpenAPI 3 request to upload multipart form data (a file and some other fields). I have this in my openApi yml file ...

  /myobjects/:
      post:
        tags:
          - my-objects
        summary: Adding my object
        operationId: addmyobject
        description: Creates a new my object.
        requestBody:
          content:
            multipart/form-data:
              schema:
                $ref: '#/components/schemas/MyObjectDTO'
          required: true
    ...
    MyObjectDTO:
      type: object
      properties:
        myId:
          type: integer
          format: int
          readOnly: true
        name:
          type: string
          maxLength: 100
          required: true
          example: myRequest
    ...
        myFile:
          type: string
          format: binary
      required:
        - name

I'm using the following configuraiton for Swagger code gen (Maven) in my pom.xml file ...

        <plugin>
            <groupId>io.swagger.codegen.v3</groupId>
            <artifactId>swagger-codegen-maven-plugin</artifactId>
            <version>3.0.17</version>
            ...
            <configuration>
                ...
                <generateApis>true</generateApis>
                <generateApiTests>false</generateApiTests>
                <generateModelTests>false</generateModelTests>
                <generateApiDocumentation>true</generateApiDocumentation>
                <generateModels>true</generateModels>
                <generateSupportingFiles>false</generateSupportingFiles>
                <languageSpecificPrimitives>true</languageSpecificPrimitives>
                <importMappings>
                    ...
                </importMappings>
                <configOptions>
                    <interfaceOnly>true</interfaceOnly>
                    <java8>false</java8>
                    <dateLibrary>java8</dateLibrary>
                    <sourceFolder>.</sourceFolder>
                    <throwsException>true</throwsException>
                    <useTags>true</useTags>
                </configOptions>
            </configuration>

The problem is when my API call generates, not only are all the fields from my DTO included, but they are all marked as required, despite the fact I have only specified that "name" is a required field in my DTO.

    @Operation(summary = "Adding my object", description = "Creates a new my object.", tags={ "my-objects" })
    @ApiResponses(value = { 
        @ApiResponse(responseCode = "201", description = "OK", content = @Content(schema = @Schema(implementation = ResponseData.class))) })
    @RequestMapping(value = "/myobjects/",
        produces = { "application/json" }, 
        consumes = { "multipart/form-data" },
        method = RequestMethod.POST)
    default ResponseEntity<ResponseData> addmyobject(
@Parameter(description = "", required=true) @RequestParam(value="myId", required=true) Integer myId
,
@Parameter(description = "", required=true) @RequestParam(value="name", required=true) String name
,
...
,
@Parameter(description = "file detail") @Valid @RequestPart("file") MultipartFile myFile
) throws Exception {

Is there a way to rewrite my yml, or configure code generation so that I can specify which fields are required and which I want included in my multi-part upload call?

Dave
  • 15,639
  • 133
  • 442
  • 830
  • Looks like a codegen bug. Open an issue at https://github.com/swagger-api/swagger-codegen/issues, or check if there's an existing issue. – Helen Aug 25 '22 at 01:30
  • Is it though? If you wanted to have different required fields for PUT and POST requests and only specified the DTO, how would open API be able to distinguish between what was needed for PUT and what was needed for POST? – Dave Aug 29 '22 at 20:50
  • @Dave added an answer below for a way to amend the OpenAPI.yml, based on how we create it in our organisation, hope it helps – djmonki Sep 01 '22 at 10:59

2 Answers2

0

My understanding of the OpenAPI 3 yml, is that the required: declaration for type: object should be declared before the properties: declaration, thus it determines which of the properties are required. Also removing required: true from the requestBody: configuration as the schema already defines what is required -

  /myobjects/:
      post:
        tags:
          - my-objects
        summary: Adding my object
        operationId: addmyobject
        description: Creates a new my object.
        requestBody:
          content:
            multipart/form-data:
              schema:
                $ref: '#/components/schemas/MyObjectDTO'
    ...
    MyObjectDTO:
      type: object
      required:
        - name
      properties:
        myId:
          type: integer
          format: int
          readOnly: true
        name:
          type: string
          maxLength: 100
          required: true
          example: myRequest
    ...
        myFile:
          type: string
          format: binary
djmonki
  • 3,020
  • 7
  • 18
  • Switching the order of the "required" block didn't matter. The generated code is still showing every property as "required" (even the ones that aren't listed under the "required" block) – Dave Sep 01 '22 at 20:23
  • We're using version 3.0.27 of the swagger codegen plugin by the way. When I get sometime, I'll go through our project and try and do compare with your example above. – djmonki Sep 01 '22 at 20:32
  • @Dave - updated the code, by removing the ```required: true``` attribute from the ```requestBody:``` configuration. See if that resolved your issue. This should work, as the schema defines which elements of the requestBody are required (i.e. just the 'name') – djmonki Sep 02 '22 at 13:08
0

I had a similar issue with swagger codegen. Looks indeed like a bug.

I fixed it by overriding the mustache file formParams.mustache.

You can let the codegen plugin override the default mustache files by putting your own file(s) in a folder on your classpath (like src/main/resources/mustache) and configure it in your pom by adding: <templateDirectory>${project.basedir}/src/main/resources/mustache</templateDirectory> to the <configuration> of the plugin.

The content of my formParams.mustache is:

{{#isFormParam}}{{^isBinary}}{{^useOas2}}@Parameter(in = ParameterIn.DEFAULT, description = "{{{description}}}"{{#required}}, required=true{{/required}},schema=@Schema({{#allowableValues}}{{> allowableValues }}{{/allowableValues}}{{#defaultValue}}{{#allowableValues}},{{/allowableValues}} defaultValue="{{{defaultValue}}}"{{/defaultValue}})){{/useOas2}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestParam(value="{{baseName}}"{{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}})  {{{dataType}}} {{paramName}}{{/isBinary}}{{#isBinary}}{{#useOas2}}@ApiParam(value = "file detail"){{/useOas2}}{{^useOas2}}@Parameter(description = "file detail"){{/useOas2}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestPart(value="{{baseName}}"{{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}}) MultipartFile {{baseName}}{{/isBinary}}{{/isFormParam}}

As I can remember, the last {{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}} part did the trick for me.

Arie
  • 171
  • 6
  • Not sure what you're talking about. What is a mustache file? – Dave Sep 06 '22 at 12:59
  • Mustache files (or handlebar files) are the templates that are used by the swagger-codegen to translate your openapi.yaml file to the actual Java code. You can override the default templates (or create your own if wanted) to make changes in the existing templates – Arie Sep 06 '22 at 13:34
  • The default templates can be found here: https://github.com/swagger-api/swagger-codegen-generators/tree/master/src/main/resources/handlebars/JavaSpring – Arie Sep 06 '22 at 13:36