Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ If you find OpenAPI Generator useful for work, please consider asking your compa
[<img align="left" src="https://openapi-generator.tech/img/companies/socialboom.png" width="128" height="128">](https://socialboom.io/?utm_source=openapi-generator&utm_medium=sponsorship&utm_campaign=oss-sponsorship)
[<img align="left" src="https://openapi-generator.tech/img/companies/mediamister.jpg" width="128" height="128">](https://www.mediamister.com/?utm_source=openapi-generator&utm_medium=sponsorship&utm_campaign=oss-sponsorship)
[<img align="left" src="https://openapi-generator.tech/img/companies/instantfamous.png" width="128" height="128">](https://www.instant-famous.com/?utm_source=openapi-generator&utm_medium=sponsorship&utm_campaign=oss-sponsorship)
[<img align="left" src="https://openapi-generator.tech/img/companies/betterfollowers.png" width="128" height="128">](https://betterfollowers.com/?utm_source=openapi-generator&utm_medium=sponsorship&utm_campaign=oss-sponsorship)

<br clear="left">

Expand Down
1 change: 1 addition & 0 deletions docs/generators/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|companionObject|Whether to generate companion objects in data classes, enabling companion extensions.| |false|
|dateLibrary|Option. Date library to use|<dl><dt>**threetenbp-localdatetime**</dt><dd>Threetenbp - Backport of JSR310 (jvm only, for legacy app only)</dd><dt>**kotlinx-datetime**</dt><dd>kotlinx-datetime (preferred for multiplatform)</dd><dt>**string**</dt><dd>String</dd><dt>**java8-localdatetime**</dt><dd>Java 8 native JSR310 (jvm only, for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (jvm only, preferred for jdk 1.8+)</dd><dt>**threetenbp**</dt><dd>Threetenbp - Backport of JSR310 (jvm only, preferred for jdk &lt; 1.8)</dd></dl>|java8|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', 'original', and 'bestEffortBacktick' (like 'original' but tries to wrap values in backticks before falling back to sanitizing, e.g. `name,asc` stays `name,asc` rather than becoming nameCommaAsc; useful for sort/order enums)| |original|
|enumUnknownDefaultCase|Add an `unknown_default_open_api` enum case as a fallback for unrecognized values. Only `moshi`(serializationLibrary) decodes every unknown value to it: `jackson` skips nullable enums, `kotlinx_serialization` skips non-string enums, and neither `gson`(serializationLibrary) nor `multiplatform`(library) decodes to it at all.|<dl><dt>**false**</dt><dd>No changes to the enums are made, this is the default option.</dd><dt>**true**</dt><dd>Each enum gains an `unknown_default_open_api` case.</dd></dl>|false|
|explicitApi|Generates code with explicit access modifiers to comply with Kotlin Explicit API Mode.| |false|
|failOnUnknownProperties|Fail Jackson de-serialization on unknown properties| |false|
|generateOneOfAnyOfWrappers|Generate oneOf, anyOf schemas as wrappers. Only `jvm-retrofit2`(library) with `gson` or `kotlinx_serialization`(serializationLibrary) support this option.| |false|
Expand Down
6 changes: 6 additions & 0 deletions modules/openapi-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@
</plugins>
</reporting>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${swagger-parser-groupid.version}</groupId>
<artifactId>swagger-parser</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ public KotlinClientCodegen() {
cliOptions.add(CliOption.newBoolean(USE_RESPONSE_AS_RETURN_TYPE, "When using retrofit2 and coroutines, use `Response`<`T`> as return type instead of `T`.", true));

cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Use Jackson 3 dependencies (tools.jackson package). Requires serializationLibrary=jackson. Incompatible with openApiNullable."));

// AbstractKotlinCodegen calls cliOptions.clear(), dropping DefaultCodegen's registration.
// Re-registered on kotlin alone: no other Kotlin generator's templates implement the fallback.
CliOption enumUnknownDefaultCaseOpt = CliOption.newBoolean(
CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE,
"Add an `unknown_default_open_api` enum case as a fallback for unrecognized values. Only `moshi`(serializationLibrary) decodes every unknown value to it: `jackson` skips nullable enums, `kotlinx_serialization` skips non-string enums, and neither `gson`(serializationLibrary) nor `multiplatform`(library) decodes to it at all.");
Map<String, String> enumUnknownDefaultCaseOpts = new HashMap<>();
enumUnknownDefaultCaseOpts.put("false",
"No changes to the enums are made, this is the default option.");
enumUnknownDefaultCaseOpts.put("true",
"Each enum gains an `unknown_default_open_api` case.");
enumUnknownDefaultCaseOpt.setEnum(enumUnknownDefaultCaseOpts);
cliOptions.add(enumUnknownDefaultCaseOpt);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@
public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}> {
@Override
public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} enumeration) throws IOException {
jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}});
jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}}{{#isUuid}}.toString(){{/isUuid}});
}

@Override
public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException {
{{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}} {{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
{{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}} {{#isUuid}}UUID.fromString({{/isUuid}}{{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{#isUuid}}nextString()){{/isUuid}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{^isUuid}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUuid}}{{/isUri}}{{/isInteger}}{{/isNumber}};
return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
}
}

public static void validateJsonElement(JsonElement jsonElement) throws IOException {
{{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isUri}}URI.create({{/isUri}}jsonElement.{{#isNumber}}getAsString(){{/isNumber}}{{#isInteger}}getAsInt(){{/isInteger}}{{#isUri}}getAsString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}getAs{{{dataType}}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
{{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isUuid}}UUID.fromString({{/isUuid}}{{#isUri}}URI.create({{/isUri}}jsonElement.{{#isNumber}}getAsString(){{/isNumber}}{{#isInteger}}getAsInt(){{/isInteger}}{{#isUri}}getAsString()){{/isUri}}{{#isUuid}}getAsString()){{/isUuid}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{^isUuid}}getAs{{{dataType}}}(){{/isUuid}}{{/isUri}}{{/isInteger}}{{/isNumber}};
{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public class {{classname}} extends BaseApi {
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{.}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{.}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{dataType}}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientException {
public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{.}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{.}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{>nullableArgumentForApi}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientException {
{{#returnType}}
return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).getBody();
{{/returnType}}
Expand Down Expand Up @@ -107,7 +107,7 @@ public class {{classname}} extends BaseApi {
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
public ResponseEntity<{{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{.}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{.}}}{{/isResponseFile}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{dataType}}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientException {
public ResponseEntity<{{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{.}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{.}}}{{/isResponseFile}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{>nullableArgumentForApi}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientException {
Object localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
{{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,14 @@
{{#allVars}}
{{#required}}
if (!{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.IsSet)
throw new ArgumentException("Property is required for class {{classname}}.", nameof({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}));
throw new JsonException("Property is required for class {{classname}}: {{baseName}}.");

{{/required}}
{{/allVars}}
{{#allVars}}
{{^isNullable}}
if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.IsSet && {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.Value == null)
throw new ArgumentNullException(nameof({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}), "Property is not nullable for class {{classname}}.");
throw new JsonException("Property is not nullable for class {{classname}}: {{baseName}}.");

{{/isNullable}}
{{/allVars}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ where
Some(ref authorization) => authorization,
None => return Ok(Response::builder()
.status(StatusCode::FORBIDDEN)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_str("Unauthenticated"))
.expect("Unable to create Authentication Forbidden response")),
};
Expand All @@ -56,6 +57,7 @@ where
let missing_scopes = required_scopes.difference(scopes);
return Ok(Response::builder()
.status(StatusCode::FORBIDDEN)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(BoxBody::new(missing_scopes.fold(
"Insufficient authorization, missing scopes".to_string(),
|s, scope| format!("{s} {scope}"))
Expand Down Expand Up @@ -88,11 +90,13 @@ where
Ok(param_{{{paramName}}}) => param_{{{paramName}}},
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Couldn't parse path parameter {{{baseName}}}: {e}")))
.expect("Unable to create Bad Request response for invalid path parameter")),
},
Err(_) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{baseName}}}"])))
.expect("Unable to create Bad Request response for invalid percent decode"))
};
Expand Down Expand Up @@ -121,6 +125,7 @@ where
Err(err) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Invalid header {{{baseName}}} - {err}")))
.expect("Unable to create Bad Request response for invalid header {{{baseName}}}"));

Expand All @@ -130,6 +135,7 @@ where
{{#required}}
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_str("Missing required header {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing required header {{{baseName}}}"));
{{/required}}
Expand Down Expand Up @@ -171,6 +177,7 @@ where
Ok(param_{{{paramName}}}) => Some(param_{{{paramName}}}),
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {e}")))
.expect("Unable to create Bad Request response for invalid query parameter {{{baseName}}}")),
}
Expand All @@ -182,6 +189,7 @@ where
Some(param_{{{paramName}}}) => param_{{{paramName}}},
None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_str("Missing required query parameter {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing query parameter {{{baseName}}}")),
};
Expand All @@ -204,6 +212,7 @@ where
Ok(param_{{{paramName}}}) => Some(param_{{{paramName}}}),
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {e}")))
.expect("Unable to create Bad Request response for invalid query parameter {{{baseName}}}")),
}
Expand All @@ -215,6 +224,7 @@ where
Some(param_{{{paramName}}}) => param_{{{paramName}}},
None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_str("Missing required query parameter {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing query parameter {{{baseName}}}")),
};
Expand Down Expand Up @@ -318,6 +328,7 @@ where
Err(e) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("An internal server error occurred handling {{name}} header - {e}")))
.expect("Unable to create Internal Server Error for invalid response header"))
}
Expand All @@ -340,6 +351,10 @@ where
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
response.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/plain"),
);
*response.body_mut() = body_from_str("An internal error occurred");
},
}
Expand All @@ -349,6 +364,7 @@ where
},
Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(CONTENT_TYPE, mime::TEXT_PLAIN.as_ref())
.body(body_from_string(format!("Unable to read body: {}", e.into())))
.expect("Unable to create Bad Request response due to unable to read body")),
}
Expand Down
Loading
Loading