[Typescript] Fix oneOf (#9628)

* on of

* fix map

* samples

* file to any

* samples

* add unit tests

* clear code
This commit is contained in:
Kuzma
2021-06-22 00:42:47 +03:00
committed by GitHub
parent 2e85ccdec8
commit 969cea8ce1
2 changed files with 151 additions and 20 deletions

View File

@@ -19,6 +19,7 @@ package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.apache.commons.lang3.StringUtils;
@@ -32,6 +33,8 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
@@ -144,10 +147,12 @@ public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenCo
typeMapping.put("object", "any");
typeMapping.put("integer", "number");
typeMapping.put("Map", "any");
typeMapping.put("map", "any");
typeMapping.put("date", "string");
typeMapping.put("DateTime", "Date");
typeMapping.put("binary", "any");
typeMapping.put("File", "any");
typeMapping.put("file", "any");
typeMapping.put("ByteArray", "string");
typeMapping.put("UUID", "string");
typeMapping.put("Error", "Error");
@@ -373,40 +378,56 @@ public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenCo
}
@Override
public String toModelName(String name) {
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
public String toModelName(final String name) {
String fullModelName = name;
fullModelName = addPrefix(fullModelName, modelNamePrefix);
fullModelName = addSuffix(fullModelName, modelNameSuffix);
return toTypescriptTypeName(fullModelName, "Model");
}
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
protected String addPrefix(String name, String prefix) {
if (!StringUtils.isEmpty(prefix)) {
name = prefix + "_" + name;
}
return name;
}
protected String addSuffix(String name, String suffix) {
if (!StringUtils.isEmpty(suffix)) {
name = name + "_" + suffix;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
}
return name;
}
protected String toTypescriptTypeName(final String name, String safePrefix) {
ArrayList<String> exceptions = new ArrayList<String>(Arrays.asList("\\|", " "));
String sanName = sanitizeName(name, "(?![| ])\\W", exceptions);
sanName = camelize(sanName);
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
// this is unlikely to happen, because we have just camelized the name, while reserved words are usually all lowcase
if (isReservedWord(sanName)) {
String modelName = safePrefix + sanName;
LOGGER.warn(sanName + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
// model name starts with number
if (name.matches("^\\d.*")) {
String modelName = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
if (sanName.matches("^\\d.*")) {
String modelName = safePrefix + sanName; // e.g. 200Response => Model200Response
LOGGER.warn(sanName + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
if (languageSpecificPrimitives.contains(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
if (languageSpecificPrimitives.contains(sanName)) {
String modelName = safePrefix + sanName;
LOGGER.warn(sanName + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
return modelName;
}
// camelize the model name
// phone_number => PhoneNumber
return camelize(name);
return sanName;
}
@Override
@@ -521,7 +542,9 @@ public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenCo
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))
return type;
@@ -853,4 +876,55 @@ public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenCo
codegenModel.additionalPropertiesType = getTypeDeclaration((Schema) schema.getAdditionalProperties());
addImport(codegenModel, codegenModel.additionalPropertiesType);
}
@Override
public String toAnyOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getAnyOf());
return String.join(" | ", types);
}
@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getOneOf());
return String.join(" | ", types);
}
/**
* Extracts the list of type names from a list of schemas.
* Excludes `AnyType` if there are other valid types extracted.
*
* @param schemas list of schemas
* @return list of types
*/
protected List<String> getTypesFromSchemas(List<Schema> schemas) {
List<Schema> filteredSchemas = schemas.size() > 1
? schemas.stream().filter(schema -> !"AnyType".equals(super.getSchemaType(schema))).collect(Collectors.toList())
: schemas;
return filteredSchemas.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());
}
@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);
}
}
}
}

View File

@@ -0,0 +1,57 @@
package org.openapitools.codegen.typescript;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.*;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.languages.TypeScriptClientCodegen;
import org.openapitools.codegen.utils.ModelUtils;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TypeScriptClientModelTest {
@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 TypeScriptClientCodegen();
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 TypeScriptClientCodegen();
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>");
}
}