Validation is done using apache library bval-jsr-2.0.5.jar. So this might
be well out-of-scope of CXF.



-----Oorspronkelijk bericht-----
Van: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>
Verzonden: maandag 18 november 2024 10:02
Aan: 'Andriy Redko' <drr...@gmail.com>; 'dev@cxf.apache.org'
<dev@cxf.apache.org>
Onderwerp: RE: CXF JAX-RS: working with multipart form-data

Hi Andriy,

Thanks for the example. I tried something similar last week but since my
method was annotated with @Parameter(required=true,schema =
@Schema(implementation=MessageToSend.class)) request parameter validation
failed on the server side (see server trace below).
I have a JAXRSBeanValidationInInterceptor provider active at the server
side and somehow matching the multipart request body to the method
signature fails.
So does it mean that validating request parameters takes place before
handling the multipart body translating each part to a method parameter?

My actual method signature looked like:
        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") @NotNull @Size(min
= 10, max = 36) @Parameter(description="When retrying a failed call, the
retry call should have the same Idempotency Key.") String idempotencyKey,
                        @FormDataParam(value="messageToSend")
@Parameter(required=true,schema =
@Schema(implementation=MessageToSend.class)) @Multipart(value =
"messageToSend", type="application/json", required= true) MessageToSend
messageToSend,
                        @FormDataParam(value="upfile1") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile1", type="application/pdf", required = false) InputStream
upfile1Detail,
                        @FormDataParam(value="upfile2") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile2", type="application/pdf", required = false) InputStream
upfile2Detail,
                        @FormDataParam(value="upfile3") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile3", type="application/pdf", required = false) InputStream
upfile3Detail)
        throws GeneralSecurityException, AuthorizationException;


Even if I reduce the method signature to:
        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") @NotNull @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",
type="application/json", required= true) MessageToSend messageToSend,
                        @Multipart(value = "upfile1",
type="application/pdf", required = false) InputStream upfile1Detail,
                        @Multipart(value = "upfile2",
type="application/pdf", required = false) InputStream upfile2Detail,
                        @Multipart(value = "upfile3",
type="application/pdf", required = false) InputStream upfile3Detail)
        throws GeneralSecurityException, AuthorizationException;

I am still getting a '500 server error' saying that arg0 of createMessage
may not be null.

Here are some extracts from the code:

1.API interface (only createMessage shown)
====================================
        @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(schema=@S
chema(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(implementatio
n=ErrorMessage.class))),
                        @ApiResponse(
                                        responseCode = "401",
                                        description = "Invalid
authorization",
                                        content =
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
n=ErrorMessage.class))),
                        @ApiResponse(
                                        responseCode = "500",
                                        description = "Unexpected Server
Error",
                                        content =
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
n=ErrorMessage.class))),
                        @ApiResponse(
                                        responseCode = "502",
                                        description = "Bad Gateway",
                                        content =
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
n=ErrorMessage.class))),
                        @ApiResponse(
                                        responseCode = "503",
                                        description = "Service
unavailable",
                                        content =
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
n=ErrorMessage.class))),
                        @ApiResponse(
                                        responseCode = "504",
                                        description = "Gateway Timeout",
                                        content =
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
n=ErrorMessage.class)))
        })
        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") @NotNull @Size(min
= 10, max = 36) @Parameter(description="When retrying a failed call, the
retry call should have the same Idempotency Key.") String idempotencyKey,
                        @FormDataParam(value="messageToSend")
@Parameter(required=true,schema =
@Schema(implementation=MessageToSend.class)) @Multipart(value =
"messageToSend", type="application/json", required= true) MessageToSend
messageToSend,
                        @FormDataParam(value="upfile1") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile1", type="application/pdf", required = false) InputStream
upfile1Detail,
                        @FormDataParam(value="upfile2") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile2", type="application/pdf", required = false) InputStream
upfile2Detail,
                        @FormDataParam(value="upfile3") @Parameter(schema
= @Schema(type = "string", format = "binary")) @Multipart(value =
"upfile3", type="application/pdf", required = false) InputStream
upfile3Detail)
        throws GeneralSecurityException, AuthorizationException;


2. Client Invocation (only createMessage)
=================================
        public Response createMessage(String xCorrelationId,String
idempotencyKey,MessageToSend messageToSend,
                        InputStream upfile1,
                        InputStream upfile2,
                        InputStream upfile3)
        throws GeneralSecurityException, AuthorizationException {
                //Create a multipartbody
                List<Attachment> attachments = new ArrayList<>();
                attachments.add(new
AttachmentBuilder().id("messageToSend").object(messageToSend).contentDispo
sition(new ContentDisposition("form-data;
name=\"messageToSend\";")).mediaType("application/json").build());
                if (upfile1 != null) {
                        attachments.add(new AttachmentBuilder()
                                        .id("upfile1")
                                        .dataHandler(new DataHandler(new
InputStreamDataSource(upfile1,"application/pdf","upfile1")))
                                        .contentDisposition(new
ContentDisposition("form-data; name=\"upfile1\";"))
                                        .build());
                }
                if (upfile2 != null) {
                        attachments.add(new AttachmentBuilder()
                                        .id("upfile2")
                                        .dataHandler(new DataHandler(new
InputStreamDataSource(upfile2,"application/pdf","upfile2")))
                                        .contentDisposition(new
ContentDisposition("form-data; name=\"upfile1\";"))
                                        .build());
                }
                if (upfile3 != null) {
                        attachments.add(new AttachmentBuilder()
                                        .id("upfile3")
                                        .dataHandler(new DataHandler(new
InputStreamDataSource(upfile3,"application/pdf","upfile3")))
                                        .contentDisposition(new
ContentDisposition("form-data; name=\"upfile1\";"))
                                        .build());
                }
                MultipartBody body = new
MultipartBody(attachments,MediaType.MULTIPART_FORM_DATA_TYPE,false);
                
                //Authorize API client
                Client client = WebClient.client(getApiProxy());
        
authorizationHandler.authorize(client,resourceClientId,consumerClientId,nu
ll,consumerPrivKey);
                
                WebClient wc = WebClient.fromClient(client)
                                .path("/messages") /*UJ: Is there a way to
construct the path as is done in ClientProxyImpl making use of the path
annotations on the method. */
                                .accept(MediaType.APPLICATION_JSON_TYPE);
                
                return
wc.post(Entity.entity(body,MediaType.MULTIPART_FORM_DATA_TYPE));
        }

3. Server Implementation
=====================
        /**
         * {@inheritDoc}
         * @see
be.dvtm.aeo.common.magda.documenten.api.MessagesApi#createMessage(java.lan
g.String, java.lang.String,
be.dvtm.aeo.common.magda.documenten.model.MessageToSend,
java.io.InputStream, java.io.InputStream, java.io.InputStream)
         */
        @Override
        public Response createMessage(
                        String xCorrelationId,
                        String idempotencyKey,
                        MessageToSend messageToSend,
                        InputStream upfile1,
                        InputStream upfile2,
                        InputStream upfile3) throws
GeneralSecurityException, AuthorizationException {

        //NOT REALLY RELEVANT AS IT DOESN'T GET THIS FAR due to failing
validation
        }
                
4. Testcase
=========
//The server is setup as follows:
        private Server createMagdadocServer(String baseAddress) {
                //Create a Server instance
                final JAXRSServerFactoryBean factory = new
JAXRSServerFactoryBean();
                factory.setAddress(baseAddress);

                //01.Set root resource class and provider
                factory.setResourceClasses(MagdadocSimulatorApi.class);
                factory.setResourceProvider(MagdadocSimulatorApi.class,
new SingletonResourceProvider(new MagdadocSimulator(), true));

                //02.set Logging feature
                List<Feature> featureList = new ArrayList<Feature>();
                featureList.add(new LoggingFeature());
                factory.setFeatures(featureList);
                
                //03.activate wadl generator (just too see what the server
has deployed)
                WadlGenerator wadlGen = new WadlGenerator();
                wadlGen.setLinkAnyMediaTypeToXmlSchema(true);

                //04.Set Providers
                List<Object> providers = new ArrayList<Object>();
                providers.add(new JacksonJsonProvider(new
CustomObjectMapper()));
                providers.add(new MultipartProvider());
                providers.add(wadlGen);
                factory.setProviders(providers);

                //05. Set interceptors
                JAXRSBeanValidationInInterceptor validationInInterceptor =
new JAXRSBeanValidationInInterceptor();
                validationInInterceptor.setProvider(new
BeanValidationProvider());
                List<Interceptor<? extends Message>> interceptors = new
ArrayList<>();
                interceptors.add(validationInInterceptor);
                factory.setInInterceptors(interceptors);

                return factory.create();
        }

5.Server LOG
===========
[MAGDADOC] 2024-11-18 09:16:10,089 [qtp681015501-59] INFO  $--$
(org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - REQ_IN
    Address: http://localhost:8091/services/magdadoc/messages
    HttpMethod: POST
    Content-Type: multipart/form-data;
boundary="uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190"
    ExchangeId: 1eabcefe-eb7b-4d59-af35-2c3e72c8674a
    Headers: {transfer-encoding=chunked, Accept=application/json,
Cache-Control=no-cache, User-Agent=Apache-CXF/3.5.8,
connection=keep-alive, content-type=multipart/form-data;
boundary="uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190", Host=localhost:8091,
Pragma=no-cache}
    Payload:
--uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190
Content-Type: application/json
Content-Transfer-Encoding: binary
Content-ID: <messageToSend>
Content-Disposition: form-data; name="messageToSend";

{
  "delivery" : "EBOX",
  "eboxDeliveryData" : {
    "recipient" : {
      "eboxType" : "ENTERPRISE",
      "ssin" : null,
      "enterpriseNumber" : "0123456789",
      "partition" : null,
      "eboxIdValue" : "0123456789"
    },
    "forTheAttentionOf" : null,
    "originalMessageId" : null,
    "subject" : {
      "nl" : "ProjectOnontvankelijkMail Project: 2025-EP-0001",
      "fr" : null,
      "de" : null
    },
    "messageTypeId" : null,
    "expirationDate" : null,
    "senderOrganizationId" : null,
    "senderApplicationId" : null,
    "registeredMail" : false,
    "attachments" : [ {
      "attachmentTitle" : null,
      "httpPartName" : "upfile1",
      "mainContent" : true,
      "digest" : null,
      "attachmentSigned" : false
    }, {
      "attachmentTitle" : null,
      "httpPartName" : "upfile2",
      "mainContent" : false,
      "digest" : null,
      "attachmentSigned" : false
    }, {
      "attachmentTitle" : null,
      "httpPartName" : "upfile3",
      "mainContent" : false,
      "digest" : null,
      "attachmentSigned" : false
    } ],
    "bodyMainContent" : false,
    "bodyContent" : null,
    "businessDataList" : [ ],
    "messageData" : null,
    "paymentData" : null,
    "replyAuthorized" : false,
    "replyDueDate" : null,
    "messageActions" : [ ],
    "erroneousMessageId" : null
  },
  "paperDeliveryData" : null,
  "emailDeliveryData" : null,
  "businessData" : [ ]
}
--uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190
--- Content suppressed ---

--- Content suppressed ---

--- Content suppressed ---
[MAGDADOC] 2024-11-18 09:16:10,124 [qtp681015501-59] INFO  $--$
(org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - FAULT_OUT
    Content-Type: application/json
    ResponseCode: 500
    ExchangeId: 1eabcefe-eb7b-4d59-af35-2c3e72c8674a
    Headers: {}
    Payload: <ns1:XMLFault
xmlns:ns1="http://cxf.apache.org/bindings/xformat";><ns1:faultstring
xmlns:ns1="http://cxf.apache.org/bindings/xformat";>javax.validation.Constr
aintViolationException: createMessage.arg0: may not be null,
createMessage.arg1: may not be null</ns1:faultstring></ns1:XMLFault>



-----Oorspronkelijk bericht-----
Van: Andriy Redko <drr...@gmail.com>
Verzonden: maandag 18 november 2024 0:07
Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>;
dev@cxf.apache.org
Onderwerp: Re: CXF JAX-RS: working with multipart form-data

Hi Jean,

So I have been able to spend some time on the issue and it seems like you
might not be using the client properly (hence getting the exceptions),
just a hypothesis.
Here I have crafted a version of the API:

  @POST
  @Path("/multipart")
  @Consumes("multipart/form-data")
  @Produces("text/xml")
  public Response addParts(@Multipart(value = "messageToSend",
type="application/xml") MessageToSend messageToSend,
                          @Multipart("upfile1Detail") Attachment a1,
                          @Multipart("upfile2Detail") Attachment a2,
                           @Multipart("upfile3Detail") Attachment a3)
      ...
  }


And the client invocation sequence:

        final Client client = ClientBuilder.newClient();
        final MultipartBody builder = new MultipartBody(Arrays.asList(
            new AttachmentBuilder()
                .mediaType("application/xml")
                .id("messageToSend")
                .object(new MessageToSend())
                .build(),
            new AttachmentBuilder()
                .id("upfile1Detail")
                .dataHandler(new DataHandler(new
InputStreamDataSource(getClass().getResourceAsStream("/org/apache/cxf/syst
est/jaxrs/resources/attachmentData"), "text/xml")))
                .contentDisposition(new ContentDisposition("form-data;
name=\"field1\";"))
                .build(),
            new AttachmentBuilder()
                .id("upfile2Detail")
                .dataHandler(new DataHandler(new InputStreamDataSource(new
ByteArrayInputStream(new byte[0]), "text/xml")))
                .contentDisposition(new ContentDisposition("form-data;
name=\"field2\";"))
                .build(),
            new AttachmentBuilder()
                .id("upfile3Detail")
                .dataHandler(new DataHandler(new InputStreamDataSource(new
ByteArrayInputStream(new byte[0]), "text/xml")))
                .contentDisposition(new ContentDisposition("form-data;
name=\"field3\";"))
                .build()));

        final Response response = client
                .target(address)
                .request("text/xml")
                .post(Entity.entity(builder, "multipart/form-data"));


It works perfectly when the unified Attachment body part is used. I also
crafted the test case over
here [1], to help you out to get it working or point me out if there is a
gap here that I missed.
Thank you.

[1] https://github.com/apache/cxf/pull/2152

Best Regards,
    Andriy Redko



> Hi Andriy,

> The option to use a List<Attachment> or a MultipartBody does work, I've
> testcases to confirm this.
> But it somehow breaks the original spec since trying to do a round trip
code->>>spec),
>  the spec generated from the code (based on annotations) no longer
> reflects the input spec.

> What I find unexpected is that for multipart bodies all input parameters
> are attempted to be wrapped into Attachment objects,
> (cf. method
>
org.apache.cxf.jaxrs.client.ClientProxyImpl#handleMultipart(MultivaluedMap
> <ParameterType, Parameter> map,OperationResourceInfo ori,Object[]
> params)).
> So why doesn't the stack allow to mix request body parameters that are
> either @Multipart annotated or are Attachment itself.
> Now you can't mix them since
> org.apache.cxf.jaxrs.client.ClientProxyImpl#getParametersInfo(Method
> m,Object[] params, OperationResourceInfo ori) will fail
> with error "SINGLE_BODY_ONLY". It wouldn't be hard to support the
mixture
> of both, or even an @Multipart annotated Attachment parameter (you
could
> just combine what is specified in the annotation with what is already
> present in the Attachment, giving priority to one of both in case of
> overlapping parameters).

> Further, if 'Content-Disposition' is obligatory (at least by openAPI
spec,
> however don't know whether this is the industry reference) why doesn't
the
> @Multipart
> annotation allow to specify it? Why i.o. setting header
Content-ID=<value>
> isn't the header Content-Disposition=form-date:name="value" set when
> wrapping
> a @Multipart annotated object into an Attachment object?

> Strangely I don't even find a reference to the header Content-ID in
> https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers. It is
described
> in
> https://www.rfc-editor.org/rfc/rfc2045#section-7,
> https://www.rfc-editor.org/rfc/rfc2392.txt and should have the form
> 'url-addr-spec according to RFC822'
> enclosed within '<>' and it is used to reference a multipartbody part in
> another part of the message. This doesn't seem to be the context in
which
> it is used in
> JAX-RS messages, further the url-addr-spec actually tells me there
should
> a ' @' sign in the value of the content-id header which is surely not
the
> case in all examples
> I've seen sofar. So why is CXF even using Content-ID?

> Regards,

> J.P.




> -----Oorspronkelijk bericht-----
> Van: Andriy Redko <drr...@gmail.com>
> Verzonden: vrijdag 15 november 2024 21:02
> Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>;
> dev@cxf.apache.org
> Onderwerp: Re: CXF JAX-RS: working with multipart form-data

> Hi Jean,

> Sorry for the delay, just went over through the message thread. So
option
> 1-2 should
> indeed work just fine. And for 1st option, you could indeed set the
> headers manually.
> And in this case, you will need to craft the OpenAPI spec manually, it
> won't be properly
> deducted.

> I don't think adding more attributes to @Multipart would help since it
is
> going to
> in conflict with File / Attachment that by itself source these
attributes.
> But gimme
> some time to experiment over the weekend, I think, intuitively, that
this
> could work
> (doesn't right now):

>         Response testMessage1(
>                         @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") @NotNull
@Size(min
> = 10, max = 36)
> @Parameter(description="When retrying a failed call, the retry call
should
> have the same Idempotency Key.") String idempotencyKey,
>                         @FormDataParam(value="messageToSend")
> @Parameter(required=true,schema =
> @Schema(implementation=MessageToSend.class)) @Multipart(value =
> "messageToSend", type="application/json", required= true) MessageToSend
> messageToSend,
>                         @FormDataParam(value="upfile1")
@Parameter(schema
> = @Schema(type =
> "string", format = "binary")) Attachment upfile1Detail,
>                         @FormDataParam(value="upfile2")
@Parameter(schema
> = @Schema(type =
> "string", format = "binary")) Attachment upfile2Detail,
>                         @FormDataParam(value="upfile3")
@Parameter(schema
> = @Schema(type =
> "string", format = "binary")) Attachment upfile3Detail)

> If we could fold it into :

>     Response testMessage1(
>                         @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") @NotNull
@Size(min
> = 10, max = 36)
> @Parameter(description="When retrying a failed call, the retry call
should
> have the same Idempotency Key.") String idempotencyKey,
>                         @FormDataParam(value="messageToSend")
> @Parameter(required=true,schema =
> @Schema(implementation=MessageToSend.class)) @Multipart(value =
> "messageToSend", type="application/json", required= true) MessageToSend
> messageToSend,
>                         List<Attachment> attachments)

> This is just rough idea, but I will try look more closely into it.
> Thanks.

> Best Regards,
>     Andriy Redko


>> What I found out when trying to send multipart/form-data requests with
> CXF
>> v3.5:

>> 1. You can have just one request body parameter. This can be a
>> MultipartBody, or a List<Attachment> but you'll have to set the headers
>> (Content-ID,Content-Type, Content-Disposition) yourself.

>> 2. I believe you can also have just one request body parameter
>> representing a List<Files> or a single File. Here a Content-Disposition
>> header will be set based on the filename (didn't check this, but the
> code
>> seems to reveal this).

>> 3. You can have just one request parameter annotated with @Multipart.
> This
>> will add the Content-ID and Content-Type headers based on the
> annotation,
>> but not the Content-Disposition.

>> 4. You can have multiple request body parameters but then all of them
> need
>> to be annotated with @Multipart. This will add the headers Content-Type
>> and Content-ID as specified in the annotation, but it will not add a
>> 'Content-Disposition' header.

>> So the OpenAPI specification/examples as enlisted below requires us to
>> convert everything to attachments (to get the Content-Disposition
header
>> in place) and either send a request body consisting of a
> List<Attachment>
>> or MultipartBody.

>> The fact that in CXF-v3.5.x you can not:
>> *  specify a Content-Disposition header for a @Multipart input
parameter
>> *  mix Attachment objects with @Multipart annotated objects (which by
> the
>> stack are converted to Attachment objects) as a list of input
parameters

>> seems to be a short-coming.  It doesn't align well with the OpenAPI
> v3.0.x
>> specification.

>> Is this a correct conclusion?

>> Regards,

>> J.P. Urkens

>> -----Oorspronkelijk bericht-----
>> Van: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>
>> Verzonden: donderdag 14 november 2024 11:29
>> Aan: 'Andriy Redko' <drr...@gmail.com>; 'dev@cxf.apache.org'
>> <dev@cxf.apache.org>
>> Onderwerp: RE: CXF JAX-RS: working with multipart form-data

>> Hi Andriy,

>> Actually, I think you'll have to set the Content-Disposition yourself
in
>> the Attachment object, while for File objects it will be retrieved from
>> the file name.

>> Looking at
>
https://swagger.io/docs/specification/v3_0/describing-request-body/multipa
>> rt-requests/ how would you translate the request body to an method
>> signature that works with CXF (v3.5.x)?
>> The example shows the 'Content-Disposition' header to be present for
all
>> multipart parts irrespective of their data type and that is something I
>> don't know how to achieve in a clean way using CXF. The
>> @org.apache.cxf.jaxrs.ext.multipart.Multipart annotation won't do the
> job
>> and CXF only adds it for File objects and for Attachment objects (if it
> is
>> present in the Attachment object).

>> Regards,

>> J.P.


>> -----Oorspronkelijk bericht-----
>> Van: Andriy Redko <drr...@gmail.com>
>> Verzonden: woensdag 13 november 2024 23:42
>> Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>;
>> dev@cxf.apache.org
>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data

>> Hi Jean,

>>> When looking at the classes MultipartProvider and JAXRSUtils (cxf

>> v3.5.9)
>>> then it shows that only object for which a 'Content-Disposition'
>> header will
>>> be written is a File Object. The problem is that my application is
>>> generating the file content on the fly, so I have it either as a
>> byte[] or
>>> InputStream.

>> I believe the 'Content-Disposition' will be written for File and
>> Attachment. Respectively,
>> it is going to be read for these multipart content parts as well. This
> is
>> why the
>> @Multipart annotation has no 'Content-Disposition' or alike (I think).

>>>> Even passing a List<Attachment> doesn't work as the

>> MultiPartProvider will
>>> loop through the list and try to create a DataHandler for an
>> Attachment
>>> object which is also not supported (throws an exception).

>> This is surprising, I will take a look shortly why it does not work.
> What
>> kind of
>> exception are you getting?

>> Thank you.

>> Best Regards,
>>     Andriy Redko

>>> Hi Andriy,

>>> When looking at the classes MultipartProvider and JAXRSUtils (cxf

>> v3.5.9)
>>> then it shows that only object for which a 'Content-Disposition'
>> header will
>>> be written is a File Object. The problem is that my application is
>>> generating the file content on the fly, so I have it either as a
>> byte[] or
>>> InputStream.
>>> This surprises me as according to
>
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposit
>> ion:
>>> "a multipart/form-data body requires a Content-Disposition header to
>> provide
>>> information about each subpart of the form (e.g., for every form
>> field and
>>> any files that are part of field data)".
>>> Also the Multipart annotation class only allows to specify
>> Content-Type,
>>> Content-ID so there is no way for me to provide 'Content-Disposition'
>>> information on objects like byte[] or InputStream.

>>> Even passing a List<Attachment> doesn't work as the MultiPartProvider

>> will
>>> loop through the list and try to create a DataHandler for an
>> Attachment
>>> object which is also not supported (throws an exception).
>>> The only way I see to pass it is to construct Attachment objects for
>> each
>>> multipart part, with 'Content-Disposition' set, and add them all to a
>>> MultipartBody object and pass this as input parameter to my method
>>> signature. But then I loose all swager information for input objects
>> that
>>> are not byte[] or InputStream.

>>> Am I missing something?

>>> Regards,

>>> J.P.

>>> -----Oorspronkelijk bericht-----
>>> Van: Andriy Redko <drr...@gmail.com>
>>> Verzonden: vrijdag 4 oktober 2024 2:52
>>> Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>;
>>> dev@cxf.apache.org
>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data

>>> Hi Jean,

>>> Yeah, I think the @Multipart + Attachment may not work, but you could

>> accept
>>> the List<Attachment> instead, right? (since you send many).

>>> The logging configuration does not seem right: you use interceptors

>> AND
>>> feature (as per snippet below).

>>>                  factory.getOutInterceptors().add(new
>>> LoggingOutInterceptor());
>>>                  factory.getInInterceptors().add(new
>>> LoggingInInterceptor());
>>>                  LoggingFeature feature = new LoggingFeature();
>>>                  feature.setLogMultipart(true);
>>>                  feature.setLogBinary(true);
>>>                  ...

>>> You only need one of those, either interceptors (please configure
>>> setLogBinary & setLogMultipart for them):

>>>                  factory.getOutInterceptors().add(new
>>> LoggingOutInterceptor());
>>>                  factory.getInInterceptors().add(new
>>> LoggingInInterceptor());

>>> Or feature:
>>>                  LoggingFeature feature = new LoggingFeature();
>>>                  feature.setLogMultipart(true);
>>>                  feature.setLogBinary(true);
>>>                  ...

>>> Hope it helps, thanks!

>>> Best Regards,
>>>     Andriy Redko


>>>> Hi Andriy,

>>>> Thanks for the swift response, but I could still use some

>>> clarifications on:

>>>> 1) You mention that passing an Attachment object as service method
>>>> parameter should work.
>>>>     My initial test setup did pass an Attachment object as input
>>>> parameter
>>>>> 1)API interface declaration" in my mail. However when the

>>>> client (see code below) tries to send a request with this
>>>> signature, the
>>>> JAXRSUtils.writeMessageBody(...) method that is called by the CXF
>>>> stack throws an exception on the Attachment parameter saying:

>>>>         okt 03, 2024 9:46:54 AM
>>>> org.apache.cxf.jaxrs.provider.MultipartProvider
>>>> getHandlerForObject SEVERE: No message body writer found for class
>>>> : class org.apache.cxf.jaxrs.ext.multipart.Attachment.
>>>>         okt 03, 2024 9:47:05 AM
>>>> org.apache.cxf.jaxrs.utils.JAXRSUtils
>>>> logMessageHandlerProblem SEVERE: Problem with writing the data,
>>>> class java.util.ArrayList, ContentType: multipart/form-data
>>>>         okt 03, 2024 9:47:14 AM
>>>> org.apache.cxf.phase.PhaseInterceptorChain
>>>> doDefaultLogging WARNING: Interceptor for
>>>> {http://api.documenten.magda.common.aeo.dvtm.be/}MessagesApi has
>>>> thrown exception, unwinding now
>>>>                 org.apache.cxf.interceptor.Fault: Problem with
>>>> writing the data, class java.util.ArrayList, ContentType:

>>> multipart/form-data
>>>>                 at
>
org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.doWriteBody(ClientP
>> roxyImpl.java:1142)
>>>>                 at
>>>> org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handl
>>>> eMessage(AbstractClient.java:1223)

>>>> The JAXRSUtils.writeMessageBody(...) method takes an 'entity'
>>>> Object that is a List<Attachment>. The first Attachment in the list
>>>> contains an object of type MessageToSend, while the second one
>>>> contains an object of type Attachment for which 'no message body

>>> writer' could be found.
>>>> The stack creates itself an Attachment object for each parameter of
>>>> the multipart body, that is why I though that I can not pass it as
>>>> a parameter to my service method. I guess I am not allowed to

>> annotate
>>> an 'Attachment'
>>>> parameter with @Multipart annotation as currently done in the
>>>> method
>>>> signature:

>>>>         @FormDataParam(value="upfile1") @Parameter(schema =
>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>> "upfile1", type="application/pdf", required = false) Attachment
>>>> upfile1Detail

>>>> However leaving the @Multipart annotation for the Attachment
>>>> parameter away leads to the error:

>>>>         javax.ws.rs.ProcessingException: Resource method
>>>> be.dvtm.aeo.common.magda.documenten.api.MessagesApi.createMessage2
>>>> has more than one parameter representing a request body

>>>> I.e. now it is no longer clear that the Attachment parameter is
>>>> part of the 'multipart'. That's why I switched to using an
>>>> InputStream as parameter with @Multipart annotation but then I loose

>>> the Content-Disposition information.
>>>> The @Multipart annotation doesn't allow to specify
>>>> Content-Disposition information. Is there an alternative here?

>>>> 2) Logging of Binary Data. I create my client with:
>>>>         private static MessagesApi getThreadsafeProxy(String

>>> baseAddress) {
>>>>                 JacksonJsonProvider jjProvider = new
>>>> JacksonJsonProvider(new CustomObjectMapper());
>>>>                 List<Object> providers = Arrays.asList(new
>>>> MultipartProvider(), jjProvider);
>>>>                 final JAXRSClientFactoryBean factory = new

>>> JAXRSClientFactoryBean();
>>>>                 factory.setAddress(baseAddress);
>>>>                 factory.setServiceClass(MessagesApi.class);
>>>>                 factory.setProviders(providers);
>>>>                 factory.getOutInterceptors().add(new
>>> LoggingOutInterceptor());
>>>>                 factory.getInInterceptors().add(new
>>> LoggingInInterceptor());
>>>>                 factory.setThreadSafe(true);
>>>>                 LoggingFeature feature = new LoggingFeature();
>>>>                 feature.setLogMultipart(true);
>>>>                 feature.setLogBinary(true);

>> feature.addBinaryContentMediaTypes(MediaType.APPLICATION_OCTET_STREAM);
>> feature.addBinaryContentMediaTypes("application/pdf");
>>>>                 factory.setFeatures(Arrays.asList(feature));
>>>>                 MessagesApi api = factory.create(MessagesApi.class);
>>>>                 ClientConfiguration config =
>> WebClient.getConfig(api);
>>>>                 addTLSClientParameters(config.getHttpConduit());
>>>>                 return api;
>>>>         }
>>>>  Here I do activate the logging for multipart and binary and also
>>>> added some mediatypes (although couldn't find what it actually
>>>> does). So I was expecting to see the whole message (attachments are
>>>> rather small as it is a test).
>>>>  Well I used wireshark to get the full message and it doesn't show
>>>> any Content-Disposition headers for the multipart elements.

>>>> Regards,

>>>> J.P. Urkens

>>>> -----Original Message-----
>>>> From: Andriy Redko <drr...@gmail.com>
>>>> Sent: donderdag 3 oktober 2024 1:01
>>>> 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,

>>>>> What is the correct way to annotate a file attachment as part of a
>>>>> mutlipart/form-data content body?

>>>> We have many examples in our test suites over here [1], it really
>>>> depends on the problem at hand.

>>>>>         -> Question-01: Is this the appropriate way to pass files
>>>>> (PDF documents) as attachment in a mutlipart/form-data request, or
>>>>> are

>>>> there better ways?

>>>> You would probably better of with "multipart/mixed" [2] as in you
>>>> case, you are sending different data types. But
>>>> "multipart/form-data" should be fine as well. The right answer
>>>> answer depends on how large the files are. In many cases you are
>>>> better off using chunked transfer (no need to read the whole file in

>>> memory), like you do with InputStream.

>>>>>         -> Question-02: When I activate logging, I can't see
>>>>> anything about the file attachment, not is content, nor the
>>>>> appropriate Content-Disposition settings? I get '--Content

>>>> suppressed--', e.g.:

>>>> Correct, by default the logging interceptors do not log binary
>>>> data, you could checks the docs here [3].

>>>>>         -> Question-03: Don't I need to set anything regarding the
>>>>> Content-Disposition header? The example here is simplified in a
>>>>> sense that I used a test setup with just one file attachment. In
>>>>> the real interface up to
>>>>> 20 attachments can be passed. Somehow I need to know which part
>>>>> relates

>>>> to
>>>>> which                   attachment or not?

>>>> This is probably caused by the fact you are sending the InputStream
>>>> and not an Attachment, if my understanding is correct. I am pretty
>>>> sure passing the Attachment (as you did initially) should work.

>>>>>         -> Question-04: At the server implemtation side, since
>>>>> upfile1Detail is passed as a method parameter, who is going to
>>>>> close this InputStream at the server side?

>>>> The stream should be closed by the service implementation (since
>>>> the stream is expected to be consumed). The Apache CXF runtime will
>>>> try to close the stream in most cases as well but sometimes it may

>> not
>>> be able to.

>>>> Hope it answers your questions. Thanks!

>>>> [1]
>>>> https://github.com/apache/cxf/blob/main/systests/jaxrs/src/test/jav
>>>> a/org/apache/cxf/systest/jaxrs/MultipartStore.java
>>>> [2]
>>>> https://learn.microsoft.com/en-us/exchange/troubleshoot/administrat
>>>> ion/multipart-mixed-mime-message-format
>>>> [3] https://cxf.apache.org/docs/message-logging.html

>>>> Best Regards,
>>>>     Andriy Redko


>>>>> Hi Andriy,

>>>>> What is the correct way to annotate a file attachment as part of a
>>>>> mutlipart/form-data content body?
>>>>> I currently have the following (only the relevant parts):

>>>>> 1)API interface declaration
>>>>> ======================
>>>>>         @POST
>>>>>         @Path("/messages1")
>>>>>         @Consumes("multipart/form-data")
>>>>>         @Produces({ "application/json" })
>>>>>         @Operation(...)
>>>>>         @ApiResponses(...)
>>>>>         Response createMessage1(
>>>>>                         @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") @NotNull
>>>>> @Size(min = 10, max = 36) @Parameter(description="When retrying a
>>>>> failed call, the retry call should have the same Idempotency
>>>>> Key.")

>>>> String idempotencyKey,
>>>>>                         @FormDataParam(value="messageToSend")
>>>>> @Parameter(required=true,schema =
>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>> "messageToSend", type="application/json", required= true)
>>>>> MessageToSend messageToSend,
>>>>>                         @FormDataParam(value="upfile1")
>>>>> @Parameter(schema = @Schema(type = "string", format = "binary"))
>>>>> @Multipart(value = "upfile1", type="application/octet-stream",
>>>>> required = false) Attachment upfile1Detail);

>>>>> So I pass the file to upload as an Attachment object.

>>>>> 2)API client test code
>>>>> =================
>>>>>                 String xCorrelationId =

>> UUID.randomUUID().toString();
>>>>>                 String idempotencyKey =
>>>>> UUID.randomUUID().toString();

>>>>>                 //01. Get the attachment to include
>>>>>                 String fileName = "test.pdf";
>>>>>                 try (InputStream is =
>>>>> this.getClass().getClassLoader().getResourceAsStream(fileName);
>>>>>                                  BufferedReader reader = new
>>>>> BufferedReader(new InputStreamReader(is))) {
>>>>>                         if (is == null) {
>>>>>                                 Assert.fail("Couldn't load
>>>>> test.pdf

>>>> from classpath!");
>>>>>                         }
>>>>>                         DataHandler dataHandler = new
>>>>> DataHandler(is,

>>>> "application/pdf");
>>>>>                         ContentDisposition cd = new
>>>>> ContentDisposition("attachment;name=upfile1;filename="+fileName);
>>>>>                         Attachment upfile1Detail = new
>>>> AttachmentBuilder()
>>>>>                                 .id("upfile1")
>>>>>                                 .dataHandler(dataHandler)
>>>>>                                 .contentDisposition(cd)
>>>>>                                 .mediaType("application/pdf")
>>>>>                                 .build();

>>>>>                 //02. create the message to send
>>>>>                 MessageToSend mts = new MessageToSend();

>>>>>                 //03. Call the server
>>>>>                 Response resp =
>>>>> apiClient.createMessage1(xCorrelationId, idempotencyKey, mts,
>>>>> upfile1Detail);


>>>>> When running the API client test code and getting at '03.' The

>> method:
>>>>>         JAXRSUtils.writeMessageBody(List<WriterInterceptor>
>>>>> writers,Object entity,Class<?> type, Type genericType,Annotation[]
>>>>> annotations,MediaType mediaType,MultivaluedMap<String, Object>
>>>>> httpHeaders,Message message)

>>>>> is called. The 'entity' object is a list of Attachment objects

>> where:
>>>>>  - the first Attachment object contains an object of type
>>>>> MessageToSend
>>>>>  - the second Attachment object contains an object of type
>>>>> Attachment

>>>>> This second object leads to a fatal error within the
>>>>> JAXRSUtils.writeMessageBody(...) method:
>>>>>         okt 02, 2024 1:36:02 PM
>>>>> org.apache.cxf.jaxrs.provider.MultipartProvider
>>>>> getHandlerForObject
>>>>>         SEVERE: No message body writer found for class : class
>>>>> org.apache.cxf.jaxrs.ext.multipart.Attachment.
>>>>>         okt 02, 2024 1:36:02 PM
>>>>> org.apache.cxf.jaxrs.utils.JAXRSUtils
>>>>> logMessageHandlerProblem
>>>>>         SEVERE: Problem with writing the data, class
>>>>> java.util.ArrayList,
>>>>> ContentType: multipart/form-data


>>>>> I  modified the interface and implementation classes to use
>>>>> 'InputStream upfile1Detail' as type for the input parameter of the
>>>>> service method. And in this case my 'dummy' server implementation
>>>>> can read the file and save it to disk.
>>>>>         -> Question-01: Is this the appropriate way to pass files
>>>>> (PDF documents) as attachment in a mutlipart/form-data request, or
>>>>> are

>>>> there better ways?
>>>>>         -> Question-02: When I activate logging, I can't see
>>>>> anything about the file attachment, not is content, nor the
>>>>> appropriate Content-Disposition settings? I get '--Content

>>>> suppressed--', e.g.:

>>>>>                 [MAGDADOC] 2024-10-02 14:29:08,911 [main] INFO
>>>>> $--$
>>>>> (org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - REQ_OUT
>>>>>                     Address:
>>>>> http://localhost:8091/services/magdadoc/api/v1/messages/messages1
>>>>>                     HttpMethod: POST
>>>>>                     Content-Type: multipart/form-data;
>>>>> boundary="uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862"
>>>>>                     ExchangeId:

>> a583a695-d881-4fa7-b65a-8961cdbbd412
>>>>>                     Headers: {Authorization=Bearer
>>>>> f4262ccf-3250-4bcf-a1bc-7ee1bf9a56cf,
>>>>> Accept=application/json,
>>>>> Idempotency-Key=bd06c05d-9fe2-4b60-b8db-5ad1121b74dc,
>>>>> x-correlation-id=511c51ba-95fe-4f69-9443-f05c377cffab}
>>>>>                     Payload:
>>>>>                 --uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862
>>>>>                 Content-Type: application/json
>>>>>                 Content-Transfer-Encoding: binary
>>>>>                 Content-ID: <messageToSend>

>>>>>                 {
>>>>>                   "delivery" : "AUTOMATIC",
>>>>>                   "eboxDeliveryData" : { /* all fine */},
>>>>>                   "paperDeliveryData" : {/* all fine */},
>>>>>                   "emailDeliveryData" : null,
>>>>>                   "businessData" : [ ]
>>>>>                 }
>>>>>                 --uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862
>>>>>                 --- Content suppressed ---

>>>>>         -> Question-03: Don't I need to set anything regarding the
>>>>> Content-Disposition header? The example here is simplified in a
>>>>> sense that I used a test setup with just one file attachment. In
>>>>> the real interface up to
>>>>> 20 attachments can be passed. Somehow I need to know which part
>>>>> relates

>>>> to
>>>>> which                   attachment or not?
>>>>>         -> Question-04: At the server implemtation side, since
>>>>> upfile1Detail is passed as a method parameter, who is going to
>>>>> close this InputStream at the server side?

>>>>> Regards,

>>>>> J.P. Urkens

>>>>> P.S.: Is there a migration document describing upgrading CXF-3.5.6
>>>>> (my current version) to CXF-3.5.8 (latest JDK8 version). I'd like
>>>>> to know whether upgrading can happen without too much burden.


>>>>> -----Original Message-----
>>>>> From: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>
>>>>> Sent: vrijdag 5 juli 2024 13:04
>>>>> To: 'Andriy Redko' <drr...@gmail.com>; 'dev@cxf.apache.org'
>>>>> <dev@cxf.apache.org>
>>>>> Subject: RE: CXF JAX-RS: working with multipart form-data

>>>>> Hi Andriy,

>>>>> When searching the net I came along this Jersey annotation but
>>>>> since I was not depending on 'Jersey' components I skipped it. So
>>>>> apparently swagger-core has processing for externally defined
>>>>> annotations (like this FormDataParam in Jersey), indeed inside

>>>> know-how.

>>>>> I included it in my project as
>>>>> io.swagger.v3.oas.annotations.FormDataParam,
>>>>> since it should somehow be included in the supported  swagger
>>>>> annotations (maybe we should submit a request for it to the
>>>>> swagger-core team) and this indeed generates an appropriate

>>>> openapi.json spec.

>>>>> Thanks alot,

>>>>> J.P. Urkens



>>>>> -----Original Message-----
>>>>> From: Andriy Redko <drr...@gmail.com>
>>>>> Sent: vrijdag 5 juli 2024 2:16
>>>>> 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,

>>>>> 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/multip
>>>>>> a
>>>>>> r
>>>>>> t-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(sc
>>>>>>> h e m 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(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> ation=ErrorMessage.class))),
>>>>>>> @ApiResponse(
>>>>>>> responseCode = "401",
>>>>>>> description = "Invalid authorization", content =
>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> ation=ErrorMessage.class))),
>>>>>>> @ApiResponse(
>>>>>>> responseCode = "500",
>>>>>>> description = "Unexpected Server Error", content =
>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> ation=ErrorMessage.class))),
>>>>>>> @ApiResponse(
>>>>>>> responseCode = "502",
>>>>>>> description = "Bad Gateway",
>>>>>>> content =
>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> ation=ErrorMessage.class))),
>>>>>>> @ApiResponse(
>>>>>>> responseCode = "503",
>>>>>>> description = "Service unavailable", content =
>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> ation=ErrorMessage.class))),
>>>>>>> @ApiResponse(
>>>>>>> responseCode = "504",
>>>>>>> description = "Gateway Timeout",
>>>>>>> content =
>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>> e
>>>>>>> n
>>>>>>> t
>>>>>>> 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(s
>>>>>>>> c h em 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(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>                                         @ApiResponse(
>>>>>>>> responseCode = "401",
>>>>>>>> description = "Invalid authorization",
>>>>>>>>                                                         content =
>>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>                                         @ApiResponse(
>>>>>>>> responseCode = "500",
>>>>>>>> description = "Unexpected Server Error",
>>>>>>>>                                                         content =
>>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>                                         @ApiResponse(
>>>>>>>> responseCode = "502",
>>>>>>>> description = "Bad Gateway",
>>>>>>>>                                                         content =
>>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>                                         @ApiResponse(
>>>>>>>> responseCode = "503",
>>>>>>>> description = "Service unavailable",
>>>>>>>>                                                         content =
>>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>                                         @ApiResponse(
>>>>>>>> responseCode = "504",
>>>>>>>> description = "Gateway Timeout",
>>>>>>>>                                                         content =
>>>>>>>>
@Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>> m
>>>>>>>> e
>>>>>>>> nt
>>>>>>>> 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.

Reply via email to