issue-20718 fixed code generation for jersey2/3 and okhttp-gson when using modelMapping with a package and anyOf/oneOf (#20721)

Co-authored-by: Sebastian Droeppelmann <sebastian@d-liver.solutions>
This commit is contained in:
xtroce 2025-04-25 11:13:36 +02:00 committed by GitHub
parent bdb063f170
commit 6ee94d636b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 218 additions and 35 deletions

View File

@ -667,6 +667,9 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
additionalProperties.put("removeAnnotations", (Mustache.Lambda) (fragment, writer) -> {
writer.write(removeAnnotations(fragment.execute()));
});
additionalProperties.put("sanitizeDataType", (Mustache.Lambda) (fragment, writer) -> {
writer.write(sanitizeDataType(fragment.execute()));
});
}
/**
@ -1843,6 +1846,22 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
return dataType;
}
/**
* Sanitize the datatype.
* This will remove all characters except alphanumeric ones.
* It will also first use {{@link #removeAnnotations(String)}} to remove the annotations added to the datatype
* @param dataType the data type string
* @return the data type string without annotations and any characters except alphanumeric ones
*/
public String sanitizeDataType(String dataType) {
String content = removeAnnotations(dataType);
if (content != null && content.matches(".*\\P{Alnum}.*")) {
content = content.replaceAll("\\P{Alnum}", "");
}
return content;
}
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
// recursively add import for mapping one type to multiple imports

View File

@ -218,7 +218,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}

View File

@ -280,7 +280,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}

View File

@ -218,7 +218,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}

View File

@ -280,7 +280,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}

View File

@ -54,8 +54,8 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isArray}}
{{#isArray}}
final Type typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
{{/isArray}}
{{/anyOf}}
{{/composedSchemas}}
@ -74,7 +74,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
// check if the actual instance is of the type `{{{dataType}}}`
if (value.getActualInstance() instanceof {{#isArray}}List<?>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) {
{{#isPrimitiveType}}
JsonPrimitive primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive();
JsonPrimitive primitive = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
{{/isPrimitiveType}}
@ -82,7 +82,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{#isArray}}
List<?> list = (List<?>) value.getActualInstance();
if (list.get(0) instanceof {{{items.dataType}}}) {
JsonArray array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray();
JsonArray array = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray();
elementAdapter.write(out, array);
return;
}
@ -90,7 +90,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isPrimitiveType}}
{{^isArray}}
{{^isPrimitiveType}}
JsonElement element = adapter{{{dataType}}}.toJsonTree(({{{dataType}}})value.getActualInstance());
JsonElement element = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance());
elementAdapter.write(out, element);
return;
{{/isPrimitiveType}}
@ -122,20 +122,20 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
if (!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isPrimitiveType}}
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(jsonElement);
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isPrimitiveType}}
{{/isNumber}}
{{/isArray}}
@ -167,7 +167,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{/items}}
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isArray}}
{{classname}} ret = new {{classname}}();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
@ -291,7 +291,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}

View File

@ -50,18 +50,18 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{^isArray}}
{{^isMap}}
{{^vendorExtensions.x-duplicated-data-type}}
final TypeAdapter<{{{dataType}}}> adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}.class));
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}.class));
{{/vendorExtensions.x-duplicated-data-type}}
{{/isMap}}
{{/isArray}}
{{#isArray}}
final Type typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
{{/isArray}}
{{#isMap}}
final Type typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType();
final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}));
{{/isMap}}
{{/oneOf}}
{{/composedSchemas}}
@ -81,12 +81,12 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
if (value.getActualInstance() instanceof {{#isArray}}List<?>{{/isArray}}{{#isMap}}Map<?, ?>{{/isMap}}{{^isMap}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isMap}}) {
{{#isPrimitiveType}}
{{^isMap}}
JsonPrimitive primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive();
JsonPrimitive primitive = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
{{/isMap}}
{{#isMap}}
JsonObject object = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonObject();
JsonObject object = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, object);
return;
{{/isMap}}
@ -95,7 +95,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{#isArray}}
List<?> list = (List<?>) value.getActualInstance();
if (list.get(0) instanceof {{{items.dataType}}}) {
JsonArray array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray();
JsonArray array = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray();
elementAdapter.write(out, array);
return;
}
@ -104,7 +104,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{^isMap}}
{{^isArray}}
{{^isPrimitiveType}}
JsonElement element = adapter{{{dataType}}}.toJsonTree(({{{dataType}}})value.getActualInstance());
JsonElement element = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance());
elementAdapter.write(out, element);
return;
{{/isPrimitiveType}}
@ -163,20 +163,20 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
if (!jsonElement.getAsJsonPrimitive().isNumber()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isNumber}}
{{^isNumber}}
{{#isPrimitiveType}}
if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isPrimitiveType}}
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(jsonElement);
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(jsonElement);
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isPrimitiveType}}
{{/isNumber}}
{{/isMap}}
@ -204,12 +204,12 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}
}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isArray}}
{{#isMap}}
if (!jsonElement.isJsonObject()) {
@ -235,13 +235,13 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}
}
{{/isFreeFormObject}}
actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}};
actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}};
{{/isMap}}
match++;
log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'");
@ -369,7 +369,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
* @return The actual instance of `{{{dataType}}}`
* @throws ClassCastException if the instance is not `{{{dataType}}}`
*/
public {{{dataType}}} get{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}() throws ClassCastException {
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
return ({{{dataType}}})super.getActualInstance();
}
@ -408,7 +408,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(jsonElement);
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(jsonElement);
{{/isPrimitiveType}}
{{/isNumber}}
{{/isArray}}
@ -435,7 +435,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}
@ -465,7 +465,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
{{/isNumber}}
{{^isNumber}}
{{^isPrimitiveType}}
{{{dataType}}}.validateJsonElement(element);
{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element);
{{/isPrimitiveType}}
{{/isNumber}}
{{/items}}

View File

@ -975,4 +975,9 @@ public class AbstractJavaCodegenTest {
// DateSchema dateSchema = (DateSchema) openAPI.getPaths().get("/thingy/{date}").getPost().getParameters().get(0).getSchema();
// Assert.assertTrue(codegen.escapeQuotationMark(codegen.toExampleValue(dateSchema)).matches("2021-01-01"));
// }
@Test(description = "test sanitizing name of dataType when using schemaMapping and oneOf/allOf (issue 20718)")
public void testSanitizedDataType() {
assertThat(codegen.sanitizeDataType("org.somepkg.DataType")).isEqualTo("orgsomepkgDataType");
}
}

View File

@ -3476,4 +3476,120 @@ public class JavaClientCodegenTest {
JavaFileAssert.assertThat(files.get("Type.java")).fileContains("Type implements java.io.Serializable {");
}
/**
* This checks bug issue-20718
* A situation when schemaMapping is used and oneOf also is used with one of the schema-mapped dataTypes and the dataType
* contains a package definition
* The dataType needs to be sanitized to generate compileable code
*/
@Test
public void testClassesAreValidJavaJersey2() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java")
.setLibrary(JERSEY2)
.setSchemaMappings(Map.of(
"A", "some.pkg.A",
"B", "some.pkg.B"))
.setInputSpec("src/test/resources/bugs/issue_20718-dataType_with_schema_mapping.yml")
.setOutputDir(output.toString().replace("\\", "/"));
Map<String, File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));
File oneOfFile = files.get("ResultObjectOneOf.java");
assertNotNull(oneOfFile);
JavaFileAssert.assertThat(oneOfFile).fileContains(
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
File anyOfFile = files.get("ResultObjectAnyOf.java");
assertNotNull(anyOfFile);
JavaFileAssert.assertThat(anyOfFile).fileContains(
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
}
/**
* This checks bug issue-20718
* A situation when schemaMapping is used and oneOf also is used with one of the schema-mapped dataTypes and the dataType
* contains a package definition
* The dataType needs to be sanitized to generate compileable code
*/
@Test
public void testClassesAreValidJavaJersey3() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java")
.setLibrary(JERSEY3)
.setSchemaMappings(Map.of(
"A", "some.pkg.A",
"B", "some.pkg.B"))
.setInputSpec("src/test/resources/bugs/issue_20718-dataType_with_schema_mapping.yml")
.setOutputDir(output.toString().replace("\\", "/"));
Map<String, File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));
File oneOfFile = files.get("ResultObjectOneOf.java");
assertNotNull(oneOfFile);
JavaFileAssert.assertThat(oneOfFile).fileContains(
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
File anyOfFile = files.get("ResultObjectAnyOf.java");
assertNotNull(anyOfFile);
JavaFileAssert.assertThat(anyOfFile).fileContains(
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
}
/**
* This checks bug issue-20718
* A situation when schemaMapping is used and oneOf also is used with one of the schema-mapped dataTypes and the dataType
* contains a package definition
* The dataType needs to be sanitized to generate compileable code
*/
@Test
public void testClassesAreValidJavaOkHttpGson() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java")
.setLibrary(OKHTTP_GSON)
.setSchemaMappings(Map.of(
"A", "some.pkg.A",
"B", "some.pkg.B"))
.setInputSpec("src/test/resources/bugs/issue_20718-dataType_with_schema_mapping.yml")
.setOutputDir(output.toString().replace("\\", "/"));
Map<String, File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));
File oneOfFile = files.get("ResultObjectOneOf.java");
assertNotNull(oneOfFile);
JavaFileAssert.assertThat(oneOfFile).fileContains(
"final TypeAdapter<some.pkg.A> adaptersomepkgA = gson.getDelegateAdapter(this, TypeToken.get(some.pkg.A.class));",
"final TypeAdapter<some.pkg.B> adaptersomepkgB = gson.getDelegateAdapter(this, TypeToken.get(some.pkg.B.class));",
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
File anyOfFile = files.get("ResultObjectAnyOf.java");
assertNotNull(anyOfFile);
JavaFileAssert.assertThat(anyOfFile).fileContains(
"final TypeAdapter<some.pkg.A> adaptersomepkgA = gson.getDelegateAdapter(this, TypeToken.get(some.pkg.A.class));",
"final TypeAdapter<some.pkg.B> adaptersomepkgB = gson.getDelegateAdapter(this, TypeToken.get(some.pkg.B.class));",
"public some.pkg.A getsomepkgA() throws ClassCastException {",
"public some.pkg.B getsomepkgB() throws ClassCastException {"
);
}
}

View File

@ -0,0 +1,43 @@
openapi: 3.0.0
info:
title: Test generate oneOf with SchemaMapping with package
description: this test shows that the generation has a bug.
version: 1.0.0
paths:
/testOneOf:
get:
responses:
'201':
$ref: '#/components/responses/ResponseObjectOneOf'
/testAnyOf:
get:
responses:
'201':
$ref: '#/components/responses/ResponseObjectAnyOf'
components:
responses:
ResponseObjectOneOf:
description: 'The validation of the data provided by the client failed.'
content:
'application/json':
schema:
$ref: '#/components/schemas/ResultObjectOneOf'
ResponseObjectAnyOf:
description: 'The validation of the data provided by the client failed.'
content:
'application/json':
schema:
$ref: '#/components/schemas/ResultObjectAnyOf'
schemas:
ResultObjectOneOf:
oneOf:
- $ref: '#/components/schemas/A'
- $ref: '#/components/schemas/B'
ResultObjectAnyOf:
oneOf:
- $ref: '#/components/schemas/A'
- $ref: '#/components/schemas/B'
A:
type: object
B:
type: object