Skip to content

typescript-angular: fix oneOf and anyOf generates incorrect model for primitive types #4341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 31, 2019
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
Expand Down Expand Up @@ -441,12 +442,16 @@ protected boolean isReservedWord(String word) {
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(openAPIType)) {
if (ModelUtils.isComposedSchema(p)) {
return openAPIType;
} else if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type))
if (languageSpecificPrimitives.contains(type)) {
return type;
} else
}
} else {
type = openAPIType;
}
return toModelName(type);
}

Expand Down Expand Up @@ -560,6 +565,20 @@ public String toEnumName(CodegenProperty property) {
}
}

@Override
protected void addImport(CodegenModel m, String type) {
if (type == null) {
return;
}

String[] parts = type.split("( [|&] )|[<>]");
for (String s : parts) {
if (needToImport(s)) {
m.imports.add(s);
}
}
}

@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// process enum in models
Expand Down Expand Up @@ -701,11 +720,43 @@ public void postProcessFile(File file, String fileType) {

@Override
public String toAnyOfName(List<String> names, ComposedSchema composedSchema) {
return String.join(" | ", names);
List<String> types = composedSchema.getAnyOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
Schema inner = ap.getItems();
schemaType = schemaType + "<" + getSchemaType(inner) + ">";
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" | ", types);
}

@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
return String.join(" | ", names);
List<String> types = composedSchema.getOneOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
Schema inner = ap.getItems();
schemaType = schemaType + "<" + getSchemaType(inner) + ">";
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" | ", types);
}

@Override
public String toAllOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = composedSchema.getAllOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
Schema inner = ap.getItems();
schemaType = schemaType + "<" + getSchemaType(inner) + ">";
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" & ", types);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
private static String CLASS_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9]*$";
private static String FILE_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9.-]*$";

private static final String DEFAULT_IMPORT_PREFIX = "./";

public static final String NPM_REPOSITORY = "npmRepository";
public static final String WITH_INTERFACES = "withInterfaces";
public static final String TAGGED_UNIONS = "taggedUnions";
Expand Down Expand Up @@ -330,36 +332,13 @@ public String getTypeDeclaration(Schema p) {
}


@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
if (isLanguagePrimitive(openAPIType) || isLanguageGenericType(openAPIType)) {
return openAPIType;
}
applyLocalTypeMapping(openAPIType);
return openAPIType;
}

private String applyLocalTypeMapping(String type) {
if (typeMapping.containsKey(type)) {
type = typeMapping.get(type);
}
return type;
}

private boolean isLanguagePrimitive(String type) {
return languageSpecificPrimitives.contains(type);
}

private boolean isLanguageGenericType(String type) {
for (String genericType : languageGenericTypes) {
if (type.startsWith(genericType + "<")) {
return true;
}
}
return false;
}

@Override
public void postProcessParameter(CodegenParameter parameter) {
super.postProcessParameter(parameter);
Expand Down Expand Up @@ -567,17 +546,26 @@ public String toApiFilename(String name) {

@Override
public String toApiImport(String name) {
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return apiPackage() + "/" + toApiFilename(name);
}

@Override
public String toModelFilename(String name) {
return this.convertUsingFileNamingConvention(this.sanitizeName(name)) + modelFileSuffix;
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return DEFAULT_IMPORT_PREFIX + this.convertUsingFileNamingConvention(this.sanitizeName(name)) + modelFileSuffix;
}

@Override
public String toModelImport(String name) {
return modelPackage() + "/" + toModelFilename(name);
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return modelPackage() + "/" + toModelFilename(name).substring(DEFAULT_IMPORT_PREFIX.length());
}

public String getNpmRepository() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{{#models}}
{{#model}}
{{#tsImports}}
import { {{classname}} } from './{{filename}}';
import { {{classname}} } from '{{filename}}';
{{/tsImports}}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{#models}}
{{#model}}
export * from './{{{ classFilename }}}';
export * from '{{{ classFilename }}}';
{{/model}}
{{/models}}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void testModelFileSuffix() {
codegen.additionalProperties().put("modelSuffix", "MySuffix");
codegen.processOpts();

Assert.assertEquals("testNameMySuffix", codegen.toModelFilename("testName"));
Assert.assertEquals("./testNameMySuffix", codegen.toModelFilename("testName"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,69 @@ public void arrayModelTest() {
Assert.assertEquals(cm.vars.size(), 0);
}

@Test(description = "convert an array oneof model")
public void arrayOneOfModelTest() {
final Schema schema = new ArraySchema()
.items(new ComposedSchema()
.addOneOfItem(new StringSchema())
.addOneOfItem(new IntegerSchema().format("int64")))
.description("an array oneof model");
final DefaultCodegen codegen = new TypeScriptAngularClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", schema);


Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "an array oneof model");
Assert.assertEquals(cm.arrayModelType, "string | number");
Assert.assertEquals(cm.vars.size(), 0);
}

@Test(description = "convert an any of with array oneof model")
public void objectPropertyAnyOfWithArrayOneOfModelTest() {
final Schema schema = new ObjectSchema().addProperties("value",
new ComposedSchema().addAnyOfItem(new StringSchema()).addAnyOfItem(new ArraySchema()
.items(new ComposedSchema()
.addOneOfItem(new StringSchema())
.addOneOfItem(new IntegerSchema().format("int64")))))
.description("an any of with array oneof model");
final DefaultCodegen codegen = new TypeScriptAngularClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", schema);

String s = codegen.getSchemaType((Schema)schema.getProperties().get("value"));


Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "an any of with array oneof model");
Assert.assertEquals(cm.vars.size(), 1);
Assert.assertEquals(s, "string | Array<string | number>");
}

@Test(description = "import a typemapping")
public void importTypeMappingModelTest() {
final Schema schema = new ArraySchema()
.items(new Schema().$ref("Children"))
.description("a typemapping array model");
final DefaultCodegen codegen = new TypeScriptAngularClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
codegen.setOpenAPI(openAPI);
codegen.typeMapping().put("Children", "Test");
codegen.importMapping().put("Test", "@myTest/package");
final CodegenModel cm = codegen.fromModel("sample", schema);

Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a typemapping array model");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.imports.size(), 1);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Test")).size(), 1);
}

@Test(description = "convert a map model")
public void mapModelTest() {
final Schema schema = new Schema()
Expand Down