Skip to content

Commit a3ec267

Browse files
github-actions[bot]patr1kt0thricardozaninihbelmiro
authored
[quarkus2] Add @PartFilename config - on/off, value, value suffix (#463)
* Add @PartFilename config - on/off, value, value suffix (#452) * Add @PartFilename config - on/off, value, value suffix * Split PartFilenameTest into classic and reactive * Get rid of code duplicity in tests, fix README.md * Revert messed up README.md formatting * Apply suggestions from code review Co-authored-by: Ricardo Zanini <[email protected]> --------- Co-authored-by: Ricardo Zanini <[email protected]> * Replaced jakarta with javax Fixed version --------- Co-authored-by: Patrik Toth <[email protected]> Co-authored-by: Ricardo Zanini <[email protected]> Co-authored-by: Helber Belmiro <[email protected]>
1 parent 72831fb commit a3ec267

File tree

17 files changed

+719
-6
lines changed

17 files changed

+719
-6
lines changed

README.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ quarkus.openapi-generator.codegen.spec.my_openapi_yaml.return-response=true
183183
Since the most part of this extension work is in the `generate-code` execution phase of the Quarkus Maven's plugin, the log configuration must be set in the Maven context. When building your project, add `-Dorg.slf4j.simpleLogger.log.org.openapitools=off` to the `mvn` command to reduce the internal generator noise. For example:
184184

185185
```shell
186-
mvn clean install -Dorg.slf4j.simpleLogger.log.org.openapitools=off
186+
mvn clean install -Dorg.slf4j.simpleLogger.log.org.openapitools=off
187187
```
188188

189189
For more information, see the [Maven Logging Configuration](https://maven.apache.org/maven-logging.html) guide.
@@ -430,8 +430,6 @@ The token propagation can be used with type "oauth2" or "bearer" security scheme
430430
| `quarkus.openapi-generator.[filename].auth.[security_scheme_name].token-propagation=[true,false]` | `quarkus.openapi-generator.petstore_json.auth.petstore_auth.token-propagation=true`<br/>Enables the token propagation for all the operations that are secured with the `petstore_auth` scheme in the `petstore_json` file.
431431
| `quarkus.openapi-generator.[filename].auth.[security_scheme_name].header-name=[http_header_name]` | `quarkus.openapi-generator.petstore_json.auth.petstore_auth.header-name=MyHeaderName`<br/>Says that the authorization token to propagate will be read from the HTTP header `MyHeaderName` instead of the standard HTTP `Authorization` header.
432432

433-
434-
435433
## Circuit Breaker
436434

437435
You can define the [CircuitBreaker annotation from MicroProfile Fault Tolerance](https://microprofile.io/project/eclipse/microprofile-fault-tolerance/spec/src/main/asciidoc/circuitbreaker.asciidoc)
@@ -583,7 +581,7 @@ public class MultipartBody {
583581

584582
@FormParam("file")
585583
@PartType(MediaType.APPLICATION_OCTET_STREAM)
586-
@PartFilename("defaultFileName")
584+
@PartFilename("fileFile")
587585
public File file;
588586

589587
@FormParam("fileName")
@@ -642,6 +640,50 @@ quarkus.openapi-generator.codegen.spec.my_multipart_requests_yml.skip-form-model
642640

643641
See the module [multipart-request](integration-tests/multipart-request) for an example of how to use this feature.
644642

643+
In case the default `PartFilename` annotation is not required, its generation can be disabled by setting the `generate-part-filename` property (globally or corresponding to your spec) in the `application.properties` to `false`, e.g.:
644+
645+
```properties
646+
quarkus.openapi-generator.codegen.spec.my_multipart_requests_yml.generate-part-filename=false
647+
```
648+
649+
By default, the `PartFilename`'s value representing the filename is prefixed by the field name. This can be changed by setting the `use-field-name-in-part-filename` property (globally or corresponding to your spec) in the `application.properties` to `false`, e.g.:
650+
651+
```properties
652+
quarkus.openapi-generator.codegen.spec.my_multipart_requests_yml.use-field-name-in-part-filename=false
653+
```
654+
655+
And in case the default `PartFilename` value is not suitable (e.g. a conversion service only allows/supports specific extensions), the value can be set by using the `part-filename-value` property (globally or corresponding to your spec) in the `application.properties`, e.g.:
656+
657+
```properties
658+
quarkus.openapi-generator.codegen.spec.my_first_multipart_requests_yml.part-filename-value=".pdf"
659+
```
660+
661+
So for instance, by setting `part-filename-value` to `some.pdf` and `use-field-name-in-part-filename` to `false` the generated code will look like this:
662+
663+
```java
664+
public class MultipartBody {
665+
666+
@FormParam("file")
667+
@PartType(MediaType.APPLICATION_OCTET_STREAM)
668+
@PartFilename("some.pdf")
669+
public File file;
670+
}
671+
```
672+
673+
And by setting only `part-filename-value` to `.pdf`, the generated code will look like this:
674+
675+
```java
676+
public class MultipartBody {
677+
678+
@FormParam("file")
679+
@PartType(MediaType.APPLICATION_OCTET_STREAM)
680+
@PartFilename("file.pdf")
681+
public File file;
682+
}
683+
```
684+
685+
See the module [part-filename](integration-tests/part-filename) for examples of how to use these features.
686+
645687
### Default content-types according to OpenAPI Specification and limitations
646688

647689
The [OAS 3.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#special-considerations-for-multipart-content) specifies the following default content-types for a multipart:

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ public enum ConfigName {
4848
IMPORT_MAPPINGS("import-mappings"),
4949
NORMALIZER("open-api-normalizer"),
5050
RETURN_RESPONSE("return-response"),
51-
ENABLE_SECURITY_GENERATION("enable-security-generation");
51+
ENABLE_SECURITY_GENERATION("enable-security-generation"),
52+
GENERATE_PART_FILENAME("generate-part-filename"),
53+
PART_FILENAME_VALUE("part-filename-value"),
54+
USE_FIELD_NAME_IN_PART_FILENAME("use-field-name-in-part-filename");
5255

5356
private final String name;
5457

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,30 @@ public class CommonItemConfig {
7171
*/
7272
@ConfigItem(name = "mutiny")
7373
public Optional<Boolean> supportMutiny;
74+
75+
/**
76+
* Defines, whether the `PartFilename` ({@link org.jboss.resteasy.reactive.PartFilename} or
77+
* {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) annotation should be generated for
78+
* MultipartForm POJOs. By setting to `false`, the annotation will not be generated.
79+
*/
80+
@ConfigItem(name = "generate-part-filename")
81+
public Optional<Boolean> generatePartFilename;
82+
83+
/**
84+
* Defines the filename for a part in case the `PartFilename` annotation
85+
* ({@link org.jboss.resteasy.reactive.PartFilename} or
86+
* {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) is generated.
87+
* In case no value is set, the default one is `&lt;fieldName&gt;File` or `file`, depending on the
88+
* {@link CommonItemConfig#useFieldNameInPartFilename} configuration.
89+
*/
90+
@ConfigItem(name = "part-filename-value")
91+
public Optional<String> partFilenameValue;
92+
93+
/**
94+
* Defines, whether the filename should also include the property name in case the `PartFilename` annotation
95+
* ({@link org.jboss.resteasy.reactive.PartFilename} or
96+
* {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) is generated.
97+
*/
98+
@ConfigItem(name = "use-field-name-in-part-filename")
99+
public Optional<Boolean> useFieldNameInPartFilename;
74100
}

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,18 @@ protected void generate(final Config config, final Path openApiFilePath, final P
187187
getValues(config, openApiFilePath, CodegenConfig.ConfigName.ENABLE_SECURITY_GENERATION, Boolean.class)
188188
.orElse(true));
189189

190+
generator.withGeneratePartFilenameConfig(
191+
getValues(config, openApiFilePath, CodegenConfig.ConfigName.GENERATE_PART_FILENAME, Boolean.class)
192+
.orElse(true));
193+
194+
getValues(config, openApiFilePath, CodegenConfig.ConfigName.PART_FILENAME_VALUE, String.class)
195+
.ifPresent(generator::withPartFilenameValueConfig);
196+
197+
generator.withUseFieldNameInPartFilenameConfig(
198+
getValues(config, openApiFilePath, CodegenConfig.ConfigName.USE_FIELD_NAME_IN_PART_FILENAME,
199+
Boolean.class)
200+
.orElse(true));
201+
190202
SmallRyeConfig smallRyeConfig = config.unwrap(SmallRyeConfig.class);
191203

192204
getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.TYPE_MAPPINGS, String.class, String.class)

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ public void withTemplateDir(Path templateDir) {
179179
this.configurator.addAdditionalProperty("templateDir", templateDir.toString());
180180
}
181181

182+
public OpenApiClientGeneratorWrapper withGeneratePartFilenameConfig(final Boolean generatePartFilename) {
183+
this.configurator.addAdditionalProperty("generate-part-filename", generatePartFilename);
184+
return this;
185+
}
186+
187+
public OpenApiClientGeneratorWrapper withPartFilenameValueConfig(final String partFilenameValue) {
188+
this.configurator.addAdditionalProperty("part-filename-value", partFilenameValue);
189+
return this;
190+
}
191+
192+
public OpenApiClientGeneratorWrapper withUseFieldNameInPartFilenameConfig(final Boolean useFieldNameInPartFilename) {
193+
this.configurator.addAdditionalProperty("use-field-name-in-part-filename", useFieldNameInPartFilename);
194+
return this;
195+
}
196+
182197
public List<File> generate(final String basePackage) {
183198
this.basePackage = basePackage;
184199
this.consolidatePackageNames();

deployment/src/main/resources/templates/libraries/microprofile/multipartFormdataPojo.qute

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,38 @@
33
@FormParam("{p.baseName}")
44
{#if p.isFile || (p.isString && p.dataFormat == 'base64')}
55
{#if is-resteasy-reactive}
6+
{#if generate-part-filename}
7+
{#if part-filename-value}
8+
{#if use-field-name-in-part-filename}
9+
@org.jboss.resteasy.reactive.PartFilename("{p.baseName}{part-filename-value}")
10+
{#else}
11+
@org.jboss.resteasy.reactive.PartFilename("{part-filename-value}")
12+
{/if}
13+
{#else}
14+
{#if use-field-name-in-part-filename}
615
@org.jboss.resteasy.reactive.PartFilename("{p.baseName}File")
16+
{#else}
17+
@org.jboss.resteasy.reactive.PartFilename("file")
18+
{/if}
19+
{/if}
20+
{/if}
721
@org.jboss.resteasy.reactive.PartType(MediaType.APPLICATION_OCTET_STREAM)
822
{#else}
23+
{#if generate-part-filename}
24+
{#if part-filename-value}
25+
{#if use-field-name-in-part-filename}
26+
@org.jboss.resteasy.annotations.providers.multipart.PartFilename("{p.baseName}{part-filename-value}")
27+
{#else}
28+
@org.jboss.resteasy.annotations.providers.multipart.PartFilename("{part-filename-value}")
29+
{/if}
30+
{#else}
31+
{#if use-field-name-in-part-filename}
932
@org.jboss.resteasy.annotations.providers.multipart.PartFilename("{p.baseName}File")
33+
{#else}
34+
@org.jboss.resteasy.annotations.providers.multipart.PartFilename("file")
35+
{/if}
36+
{/if}
37+
{/if}
1038
@org.jboss.resteasy.annotations.providers.multipart.PartType(MediaType.APPLICATION_OCTET_STREAM)
1139
{/if}
1240
{#else if p.isPrimitiveType or (p.isArray && p.items.isPrimitiveType)}
@@ -24,4 +52,4 @@
2452
{/if}
2553
public {p.dataType} {p.paramName};
2654
{/for}
27-
}
55+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>quarkus-openapi-generator-integration-tests</artifactId>
7+
<groupId>io.quarkiverse.openapi.generator</groupId>
8+
<version>2.0.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>quarkus-openapi-generator-it-part-filename</artifactId>
13+
<name>Quarkus - Openapi Generator - Integration Tests - PartFilename</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>io.quarkiverse.openapi.generator</groupId>
18+
<artifactId>quarkus-openapi-generator</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.jboss.resteasy</groupId>
22+
<artifactId>resteasy-multipart-provider</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>io.quarkus</groupId>
26+
<artifactId>quarkus-resteasy-reactive-common</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>io.quarkus</groupId>
30+
<artifactId>quarkus-junit5</artifactId>
31+
<scope>test</scope>
32+
</dependency>
33+
</dependencies>
34+
<build>
35+
<plugins>
36+
<plugin>
37+
<groupId>io.quarkus</groupId>
38+
<artifactId>quarkus-maven-plugin</artifactId>
39+
<extensions>true</extensions>
40+
<executions>
41+
<execution>
42+
<goals>
43+
<goal>build</goal>
44+
<goal>generate-code</goal>
45+
<goal>generate-code-tests</goal>
46+
</goals>
47+
</execution>
48+
</executions>
49+
</plugin>
50+
</plugins>
51+
</build>
52+
<profiles>
53+
<profile>
54+
<id>native-image</id>
55+
<activation>
56+
<property>
57+
<name>native</name>
58+
</property>
59+
</activation>
60+
<build>
61+
<plugins>
62+
<plugin>
63+
<artifactId>maven-surefire-plugin</artifactId>
64+
<configuration>
65+
<skipTests>${native.surefire.skip}</skipTests>
66+
</configuration>
67+
</plugin>
68+
<plugin>
69+
<artifactId>maven-failsafe-plugin</artifactId>
70+
<executions>
71+
<execution>
72+
<goals>
73+
<goal>integration-test</goal>
74+
<goal>verify</goal>
75+
</goals>
76+
<configuration>
77+
<systemPropertyVariables>
78+
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
79+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
80+
<maven.home>${maven.home}</maven.home>
81+
</systemPropertyVariables>
82+
</configuration>
83+
</execution>
84+
</executions>
85+
</plugin>
86+
</plugins>
87+
</build>
88+
<properties>
89+
<quarkus.package.type>native</quarkus.package.type>
90+
</properties>
91+
</profile>
92+
</profiles>
93+
</project>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
openapi: 3.0.3
2+
info:
3+
title: "Multipart form data API"
4+
description: An api that uses multipart/form-data as request type
5+
version: 1.0.0
6+
7+
servers:
8+
- url: "http://my.endpoint.com/api/v1"
9+
10+
paths:
11+
/user-profile-data:
12+
post:
13+
tags:
14+
- Do Not Generate Part Filename
15+
description: Upload data for a user profile, adapted from https://swagger.io/docs/specification/describing-request-body/multipart-requests/
16+
operationId: PostUserProfileData
17+
requestBody:
18+
required: true
19+
description: The data of a user profile
20+
content:
21+
multipart/form-data:
22+
schema:
23+
$ref: "#/components/schemas/UserProfileData"
24+
responses:
25+
"204":
26+
description: "Data uploaded"
27+
"400":
28+
description: "Invalid ID supplied"
29+
30+
components:
31+
schemas:
32+
Address:
33+
title: Address
34+
type: object
35+
properties:
36+
street:
37+
type: string
38+
description: Street name
39+
example: 7th avenue
40+
city:
41+
type: string
42+
description: Name of the city
43+
example: New Delhi
44+
UserId:
45+
type: string
46+
format: string
47+
ProfileImage:
48+
type: string
49+
format: binary
50+
51+
UserProfileData:
52+
type: object
53+
properties: # Request parts
54+
id: # Part 1 (string value)
55+
$ref: "#/components/schemas/UserId"
56+
address: # Part2 (object)
57+
$ref: "#/components/schemas/Address"
58+
profileImage: # Part 3 (an image)
59+
$ref: "#/components/schemas/ProfileImage"

0 commit comments

Comments
 (0)