Hi Jean, Here is how you could make it work (there is some magic knowledge involved sadly). First of all, define such annotation anywhere in your codebase (where it dims appropriate): import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface FormDataParam { String value(); } Use this annotation on each Attachment parameter:
/* Skipping other annotations as those are not important here */ public Response createMessage( @HeaderParam("x-correlation-id") @NotNull @Size(min = 10, max = 36) @Parameter(description="ID of the transaction. Use this ID for log tracing and incident handling.") String xCorrelationId, @HeaderParam("Idempotency-Key") @Size(min = 10, max = 36) @Parameter(description="When retrying a failed call, the retry call should have the same Idempotency Key.") String idempotencyKey, @FormDataParam("upfile1") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile1", type="application/octet-stream", required = false) InputStream upfile1Detail, @FormDataParam("upfile2") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile2", type="application/octet-stream", required = false) InputStream upfile2Detail, @FormDataParam("upfile3") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile3", type="application/octet-stream", required = false) Attachment upfile3Detail, @FormDataParam("upfile4") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile4", type="application/octet-stream", required = false) Attachment upfile4Detail, @FormDataParam("upfile5") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile5", type="application/octet-stream", required = false) Attachment upfile5Detail, @FormDataParam("upfile6") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile6", type="application/octet-stream", required = false) Attachment upfile6Detail, @FormDataParam("upfile7") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile7", type="application/octet-stream", required = false) Attachment upfile7Detail, @FormDataParam("upfile8") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile8", type="application/octet-stream", required = false) Attachment upfile8Detail, @FormDataParam("upfile9") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile9", type="application/octet-stream", required = false) Attachment upfile9Detail, @FormDataParam("upfile10") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile10", type="application/octet-stream", required = false) Attachment upfile10Detail, @FormDataParam("upfile11") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile11", type="application/octet-stream", required = false) Attachment upfile11Detail, @FormDataParam("upfile12") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile12", type="application/octet-stream", required = false) Attachment upfile12Detail, @FormDataParam("upfile13") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile13", type="application/octet-stream", required = false) Attachment upfile13Detail, @FormDataParam("upfile14") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile14", type="application/octet-stream", required = false) Attachment upfile14Detail, @FormDataParam("upfile15") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile15", type="application/octet-stream", required = false) Attachment upfile15Detail, @FormDataParam("upfile16") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile16", type="application/octet-stream", required = false) Attachment upfile16Detail, @FormDataParam("upfile17") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile17", type="application/octet-stream", required = false) Attachment upfile17Detail, @FormDataParam("upfile18") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile18", type="application/octet-stream", required = false) Attachment upfile18Detail, @FormDataParam("upfile19") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile19", type="application/octet-stream", required = false) Attachment upfile19Detail, @FormDataParam("upfile20") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "upfile20", type="application/octet-stream", required = false) Attachment upfile20Detail, @FormDataParam("qrfile") @Parameter(schema = @Schema(type = "string", format = "binary")) @Multipart(value = "qrfile", type="application/octet-stream", required = false) Attachment qrfileDetail ) { .... } With that, you will get a nice request body schema (publishing a bit large YAML snippet to preserve the context): paths: /sample/messages: post: tags: - messages summary: "Send a message, using a channel (email, paper mail, ebox) and delivery\ \ method (registered or normal) of your choice. More than 6 upfiles only supported\ \ for PAPER delivery." operationId: createMessage parameters: - name: x-correlation-id in: header description: ID of the transaction. Use this ID for log tracing and incident handling. required: true schema: maxLength: 36 minLength: 10 type: string - name: Idempotency-Key in: header description: "When retrying a failed call, the retry call should have the\ \ same Idempotency Key." schema: maxLength: 36 minLength: 10 type: string requestBody: content: multipart/form-data: schema: type: object properties: upfile1: type: string format: binary upfile2: type: string format: binary upfile3: type: string format: binary upfile4: type: string format: binary upfile5: type: string format: binary upfile6: type: string format: binary upfile7: type: string format: binary upfile8: type: string format: binary upfile9: type: string format: binary upfile10: type: string format: binary upfile11: type: string format: binary upfile12: type: string format: binary upfile13: type: string format: binary upfile14: type: string format: binary upfile15: type: string format: binary upfile16: type: string format: binary upfile17: type: string format: binary upfile18: type: string format: binary upfile19: type: string format: binary upfile20: type: string format: binary qrfile: type: string format: binary The key here is @FormDataParam annotation which (originally) comes from Jersey but has special treatment in Swagger Core (but, likely, no attribution to Jersey). Hope it helps! Thank you. Best Regards, Andriy Redko > V2.2.22 (15/05/2024) is the latest version of io.swagger.core.v3 libraries. > I upgrade to this version to make sure I had the latest swagger > implementation. > -----Original Message----- > From: Andriy Redko <drr...@gmail.com> > Sent: donderdag 4 juli 2024 4:44 > To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; dev@cxf.apache.org > Subject: Re: CXF JAX-RS: working with multipart form-data > Hi Jean, > Interesting, I was experimenting with different ways to express what you > need, but no luck so far, I will try to spend a bit more time on that this > week since OAS 3.x does support multipart [1] but we may indeed hit the > limitation(s) of this particular Swagger Core version. Thank you. > [1] > https://swagger.io/docs/specification/describing-request-body/multipart-requests/ > Best Regards, > Andriy Redko >> Hi Andriy, >> I already tried this but it didn't work. E.g. for following API interface >> specification: >> /** >> * Send a message, using a channel (email, paper mail, >> ebox) and delivery method (registered or normal) of your choice. More than >> 6 upfiles only supported for PAPER delivery. >> * >> */ >> @POST >> @Path("/messages") >> @Consumes("multipart/form-data") >> @Produces({ "application/json" }) >> @Operation( >> summary = >> "Send a message, using a channel (email, paper mail, ebox) and >> delivery method (registered or normal) of your choice. More than 6 >> upfiles only supported for PAPER delivery.", >> tags = >> {"messages" }, >> operationId="createMessage", >> security=@SecurityRequirement(name="BearerAuthentication")) >> @ApiResponses({ >> @ApiResponse( >> responseCode = "201", >> description = "Created", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,array=@ArraySchema(schem >> a=@Schema(implementation=SendStatusMessage.class))), >> headers = {@Header( >> name="X-Magda-Exceptions", >> required=false, >> description="Only used in the context of EBOX delivery and if there >> was a problem with the consent of the receiver's ebox.", >> schema=@Schema(implementation=MagdaExceptionList.class)) >> }), >> @ApiResponse( >> responseCode = "400", >> description = "Invalid data supplied", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))), >> @ApiResponse( >> responseCode = "401", >> description = "Invalid authorization", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))), >> @ApiResponse( >> responseCode = "500", >> description = "Unexpected Server Error", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))), >> @ApiResponse( >> responseCode = "502", >> description = "Bad Gateway", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))), >> @ApiResponse( >> responseCode = "503", >> description = "Service unavailable", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))), >> @ApiResponse( >> responseCode = "504", >> description = "Gateway Timeout", >> content = >> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >> ation=ErrorMessage.class))) >> }) >> public Response createMessage( >> @HeaderParam("x-correlation-id") @NotNull @Size(min = 10, max = 36) >> @Parameter(description="ID of the transaction. Use this ID for log >> tracing and incident handling.") String xCorrelationId, >> @HeaderParam("Idempotency-Key") @Size(min = 10, max = 36) >> @Parameter(description="When retrying a failed call, the retry call >> should have the same Idempotency Key.") String idempotencyKey, >> @Parameter(required=true,schema = >> @Schema(implementation=MessageToSend.class)) @Multipart(value = >> "messageToSend", type="application/json", required= true) >> MessageToSend messageToSend, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile1", type="application/octet-stream", >> required = false) Attachment upfile1Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile2", type="application/octet-stream", >> required = false) Attachment upfile2Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile3", type="application/octet-stream", >> required = false) Attachment upfile3Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile4", type="application/octet-stream", >> required = false) Attachment upfile4Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile5", type="application/octet-stream", >> required = false) Attachment upfile5Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile6", type="application/octet-stream", >> required = false) Attachment upfile6Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile7", type="application/octet-stream", >> required = false) Attachment upfile7Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile8", type="application/octet-stream", >> required = false) Attachment upfile8Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile9", type="application/octet-stream", >> required = false) Attachment upfile9Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile10", type="application/octet-stream", >> required = false) Attachment upfile10Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile11", type="application/octet-stream", >> required = false) Attachment upfile11Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile12", type="application/octet-stream", >> required = false) Attachment upfile12Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile13", type="application/octet-stream", >> required = false) Attachment upfile13Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile14", type="application/octet-stream", >> required = false) Attachment upfile14Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile15", type="application/octet-stream", >> required = false) Attachment upfile15Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile16", type="application/octet-stream", >> required = false) Attachment upfile16Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile17", type="application/octet-stream", >> required = false) Attachment upfile17Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile18", type="application/octet-stream", >> required = false) Attachment upfile18Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile19", type="application/octet-stream", >> required = false) Attachment upfile19Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "upfile20", type="application/octet-stream", >> required = false) Attachment upfile20Detail, >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> @Multipart(value = "qrfile", type="application/octet-stream", required >> = false) Attachment qrfileDetail); >> I’ve attached the generated openapi specification. It only contains the >> ‘messageToSend’ as part of the multipart/form-data requestBody content, >> all attachments are ignored. >> Below I’ve listed the libraries I’ve included in the project (cxf v3.5.8 >> and swagger v2.2.2). Which of these libraries is acutal responsible for >> generating the openapi.json specification from the interface description? >> * cxf-rt-rs-service-description-common-openapi:3.5.8 >> * cxf-rt-rs-service-description-openapi:3.5.8 >> * cxf-rt-rs-service-description-swagger-ui:3.5.8 >> * swagger-core:2.2.2 >> * swagger-annotations:2.2.2 >> * swagger-integration:2.2.2 >> * swagger-jaxrs2: 2.2.2 >> * swagger-model: 2.2.2 >> Note that I am still on JDK8, so I guess I can’t upgrade to a higher >> version (currently our projects use cxf-v3.5.6 and swagger 2.1.13). >> Regards, >> J.P. Urkens >> -----Original Message----- >> From: Andriy Redko <drr...@gmail.com> >> Sent: woensdag 3 juli 2024 5:57 >> To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >> dev@cxf.apache.org >> Subject: Re: CXF JAX-RS: working with multipart form-data >> Hi Jean Pierre, >> I suspect the @Multipart annotation is coming from CXF >> (org.apache.cxf.jaxrs.ext.multipart.Multipart), right? If yes, this is not >> a part of JAX-RS specification but CXF specific extension. You may need to >> add Swagger API annotation to the parameters in question: >> @Parameter(schema = @Schema(type = "string", format = "binary")) >> Hope it helps. >> Thank you. >> Best Regards, >> Andriy Redko >> Monday, July 1, 2024, 12:09:17 PM, you wrote: >>> Hi all, >>> I am having problems to correctly annotate service methods which consumes >>> multipart/form-data that contains attachments next to other model >>> objects. >>> I’ve an openapi specification that contains following requestBody >>> definition: >>> /messages: >>> post: >>> tags: >>> - "messages" >>> summary: "Send a message, using a channel (email, paper mail, ebox) >>> and delivery method (registered or normal) of your choice. More than 6 >>> upfiles only supported for PAPER delivery." >>> operationId: createMessage >>> parameters: >>> - $ref: '#/components/parameters/CorrelationId' >>> - $ref: '#/components/parameters/Idempotency-Key' >>> requestBody: >>> content: >>> multipart/form-data: >>> schema: >>> type: object >>> required: >>> - messageToSend >>> properties: >>> messageToSend: >>> $ref: '#/components/schemas/MessageToSend' >>> upfile1: >>> type: string >>> format: binary >>> nullable: true >>> upfile2: >>> type: string >>> format: binary >>> nullable: true >>> upfile3: >>> type: string >>> format: binary >>> nullable: true >>> upfile4: >>> type: string >>> format: binary >>> nullable: true >>> upfile5: >>> type: string >>> format: binary >>> nullable: true >>> upfile6: >>> type: string >>> format: binary >>> nullable: true >>> upfile7: >>> type: string >>> format: binary >>> nullable: true >>> upfile8: >>> type: string >>> format: binary >>> nullable: true >>> upfile9: >>> type: string >>> format: binary >>> nullable: true >>> upfile10: >>> type: string >>> format: binary >>> nullable: true >>> upfile11: >>> type: string >>> format: binary >>> nullable: true >>> upfile12: >>> type: string >>> format: binary >>> nullable: true >>> upfile13: >>> type: string >>> format: binary >>> nullable: true >>> upfile14: >>> type: string >>> format: binary >>> nullable: true >>> upfile15: >>> type: string >>> format: binary >>> nullable: true >>> upfile16: >>> type: string >>> format: binary >>> nullable: true >>> upfile17: >>> type: string >>> format: binary >>> nullable: true >>> upfile18: >>> type: string >>> format: binary >>> nullable: true >>> upfile19: >>> type: string >>> format: binary >>> nullable: true >>> upfile20: >>> type: string >>> format: binary >>> nullable: true >>> qrfile: >>> type: string >>> format: binary >>> nullable: true >>> required: true >>> When using the openapi-generator-maven-plugin v7.6.0 it generates >>> following method signature: >>> @POST >>> @Path("/messages") >>> @Consumes("multipart/form-data") >>> @Produces({ "application/json" }) >>> @Operation( >>> summary = "Send a message, using a channel >>> (email, paper mail, ebox) and delivery method (registered or normal) >>> of your choice. More than 6 upfiles only supported for PAPER >>> delivery.", >>> tags = {"messages" }, >>> operationId="createMessage", >>> security=@SecurityRequirement(name="BearerAuthentication"), >>> responses= { >>> @ApiResponse( >>> responseCode >>> = "201", >>> description = >>> "Created", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,array=@ArraySchema(schem >>> a=@Schema(implementation=SendStatusMessage.class))), >>> headers = >>> {@Header( >>> name="X-Magda-Exceptions", >>> required=false, >>> description="Only used in the context of EBOX delivery and if there >>> was a problem with the consent of the receiver's ebox.", >>> schema=@Schema(implementation=MagdaExceptionList.class)) >>> }), >>> @ApiResponse( >>> responseCode >>> = "400", >>> description = >>> "Invalid data supplied", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))), >>> @ApiResponse( >>> responseCode >>> = "401", >>> description = >>> "Invalid authorization", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))), >>> @ApiResponse( >>> responseCode >>> = "500", >>> description = >>> "Unexpected Server Error", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))), >>> @ApiResponse( >>> responseCode >>> = "502", >>> description = >>> "Bad Gateway", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))), >>> @ApiResponse( >>> responseCode >>> = "503", >>> description = >>> "Service unavailable", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))), >>> @ApiResponse( >>> responseCode >>> = "504", >>> description = >>> "Gateway Timeout", >>> content = >>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implement >>> ation=ErrorMessage.class))) >>> }) >>> public Response createMessage( >>> @HeaderParam("x-correlation-id") @NotNull >>> @Size(min = 10, max = 36) @Parameter(description="ID of the >>> transaction. Use this ID for log tracing and incident handling.") >>> String xCorrelationId, >>> @HeaderParam("Idempotency-Key") @Size(min = >>> 10, max = 36) @Parameter(description="When retrying a failed call, the >>> retry call should have the same Idempotency Key.") String >>> idempotencyKey, >>> @Multipart(value = "messageToSend”, required= >>> true) MessageToSend messageToSend, >>> @Multipart(value = "upfile1", required = >>> false) Attachment upfile1Detail, >>> @Multipart(value = "upfile2", required = >>> false) Attachment upfile2Detail, >>> @Multipart(value = "upfile3", required = >>> false) Attachment upfile3Detail, >>> @Multipart(value = "upfile4", required = >>> false) Attachment upfile4Detail, >>> @Multipart(value = "upfile5", required = >>> false) Attachment upfile5Detail, >>> @Multipart(value = "upfile6", required = >>> false) Attachment upfile6Detail, >>> @Multipart(value = "upfile7", required = >>> false) Attachment upfile7Detail, >>> @Multipart(value = "upfile8", required = >>> false) Attachment upfile8Detail, >>> @Multipart(value = "upfile9", required = >>> false) Attachment upfile9Detail, >>> @Multipart(value = "upfile10", required = >>> false) Attachment upfile10Detail, >>> @Multipart(value = "upfile11", required = >>> false) Attachment upfile11Detail, >>> @Multipart(value = "upfile12", required = >>> false) Attachment upfile12Detail, >>> @Multipart(value = "upfile13", required = >>> false) Attachment upfile13Detail, >>> @Multipart(value = "upfile14", required = >>> false) Attachment upfile14Detail, >>> @Multipart(value = "upfile15", required = >>> false) Attachment upfile15Detail, >>> @Multipart(value = "upfile16", required = >>> false) Attachment upfile16Detail, >>> @Multipart(value = "upfile17", required = >>> false) Attachment upfile17Detail, >>> @Multipart(value = "upfile18", required = >>> false) Attachment upfile18Detail, >>> @Multipart(value = "upfile19", required = >>> false) Attachment upfile19Detail, >>> @Multipart(value = "upfile20", required = >>> false) Attachment upfile20Detail, >>> @Multipart(value = "qrfile", required = >>> false) Attachment qrfileDetail); If I now generate the swagger from this >>> code (I modified the annotations in the generated code for using OAS v3 >>> annotations through swagger-jaxrs2 v2.1.13 and I am using cxf-v3.5.6 >>> having swagger-ui v4.18.2 generate the user interface) none of the upload >>> files appears as request parameter, only the messageToSend is shown. >>> Is the above signature for the method createMessage(...) incorrect? >>> If I look at the generated openapi.json all the Attachment upFiles are >>> missing from the specification? So is it a problem/short-coming(?) of the >>> used software libraries, which then: >>> · cxf-rt-rs-service-description-common-openapi v3.5.6 -> >>> this library references swagger-jaxrs2 v2.1.13 · swagger-jaxrs2 >>> v2.1.13 -> can I upgradethis to >>> e.g. swagger-jaxrs2 v2.2.22 (latest) while retaining cxf v3.5.6? >>> · ...another? >>> Regards, >>> J.P.