Skip to content
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

[scala] Support for Set when array has uniqueItems=true #4926

Merged
merged 5 commits into from
Jan 5, 2020
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 @@ -426,7 +426,7 @@ public void processOpts() {

additionalProperties.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, getSortParamsByRequiredFlag());
additionalProperties.put(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG, getSortModelPropertiesByRequiredFlag());

additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage());
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage());

Expand Down Expand Up @@ -674,7 +674,7 @@ private String getArrayTypeDeclaration(ArraySchema arr) {
// TODO: collection type here should be fully qualified namespace to avoid model conflicts
// This supports arrays of arrays.
String arrayType = typeMapping.get("array");
if (Boolean.TRUE.equals(arr.getUniqueItems())) {
if (ModelUtils.isSet(arr)) {
arrayType = typeMapping.get("set");
}
StringBuilder instantiationType = new StringBuilder(arrayType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
import io.swagger.v3.oas.models.media.Schema;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -106,6 +104,14 @@ public AbstractScalaCodegen() {
"yield"
));

importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");
// although Seq is a predef, before Scala 2.13, it _could_ refer to a mutable Seq in some cases.
importMapping.put("Seq", "scala.collection.immutable.Seq");
importMapping.put("Set", "scala.collection.immutable.Set");
importMapping.put("ListSet", "scala.collection.immutable.ListSet");

instantiationTypes.put("set", "Set");

cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC));
Expand Down Expand Up @@ -199,7 +205,11 @@ public String getTypeDeclaration(Schema p) {
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (ModelUtils.isSet(p)) {
openAPIType = "set";
}

String type;
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type)) {
Expand All @@ -219,7 +229,7 @@ public String toInstantiationType(Schema p) {
} else if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
return instantiationTypes.get("array") + "[" + inner + "]";
return ( ModelUtils.isSet(ap) ? instantiationTypes.get("set") : instantiationTypes.get("array") ) + "[" + inner + "]";
} else {
return null;
}
Expand Down Expand Up @@ -248,14 +258,54 @@ public String toDefaultValue(Schema p) {
} else if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
return "new ListBuffer[" + inner + "]() ";
String genericType;
if (ModelUtils.isSet(ap)) {
genericType = instantiationTypes.get("set");
} else {
genericType = instantiationTypes.get("array");
}

// test for immutable Monoids with .empty method for idiomatic defaults
if ("List".equals(genericType) ||
"Set".equals(genericType) ||
"Seq".equals(genericType) ||
"Array".equals(genericType) ||
"Vector".equals(genericType) ||
"IndexedSeq".equals(genericType) ||
"Iterable".equals(genericType) ||
"ListSet".equals(genericType)
) {
return genericType + "[" + inner + "].empty ";
}

// Assume that any other generic types can be new'd up.
return "new " + genericType + "[" + inner + "]() ";
} else if (ModelUtils.isStringSchema(p)) {
return null;
} else {
return null;
}
}

/**
* Convert OAS Property object to Codegen Property object
*
* @param name name of the property
* @param p OAS property object
* @return Codegen Property object
*/
@Override
public CodegenProperty fromProperty(String name, Schema p) {
CodegenProperty prop = super.fromProperty(name, p);
if (ModelUtils.isArraySchema(p)) {
ArraySchema as = (ArraySchema) p;
if (ModelUtils.isSet(as)) {
prop.containerType = "set";
}
}
return prop;
}

@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// remove model imports to avoid warnings for importing class in the same package in Scala
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ public String toDefaultValue(Schema p) {
} else if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
if (ModelUtils.isSet(ap)) {
return "Set[" + inner + "].empty ";
}
return "Seq[" + inner + "].empty ";
} else if (ModelUtils.isStringSchema(p)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ public ScalaGatlingCodegen() {
importMapping.remove("Map");

importMapping.put("Date", "java.util.Date");
importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");

typeMapping = new HashMap<String, String>();
typeMapping.put("enum", "NSString");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public ScalaHttpClientCodegen() {
importMapping.remove("Map");

importMapping.put("Date", "java.util.Date");
importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");

typeMapping = new HashMap<String, String>();
typeMapping.put("enum", "NSString");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ public ScalaLagomServerCodegen() {
importMapping.remove("Map");

importMapping.put("DateTime", "org.joda.time.DateTime");
importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");

typeMapping = new HashMap<>();
typeMapping.put("Integer", "Int");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ public String toDefaultValue(Schema p) {
if (ModelUtils.isArraySchema(p)) {
Schema items = ((ArraySchema) p).getItems();
String inner = getSchemaType(items);
if (ModelUtils.isSet(p)) {
return "Set.empty[" + inner + "]";
}
return "List.empty[" + inner + "]";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,27 @@ public ScalatraServerCodegen() {
"Integer",
"Long",
"Float",
"List",
"Set",
"Map")
);

typeMapping = new HashMap<>();
typeMapping.put("array", "List");
typeMapping.put("set", "Set");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Int");
typeMapping.put("integer", "Int");
typeMapping.put("long", "Long");
typeMapping.put("float", "Float");
typeMapping.put("byte", "Byte");
typeMapping.put("short", "Short");
typeMapping.put("char", "Char");
typeMapping.put("double", "Double");
typeMapping.put("object", "Any");
typeMapping.put("file", "File");
typeMapping.put("binary", "File");
typeMapping.put("number", "Double");

additionalProperties.put("appName", "OpenAPI Sample");
additionalProperties.put("appDescription", "A sample openapi server");
Expand All @@ -95,7 +108,6 @@ public ScalatraServerCodegen() {
supportingFiles.add(new SupportingFile("project/plugins.sbt", "project", "plugins.sbt"));
supportingFiles.add(new SupportingFile("sbt", "", "sbt"));

instantiationTypes.put("array", "ArrayList");
instantiationTypes.put("map", "HashMap");

importMapping = new HashMap<String, String>();
Expand All @@ -113,6 +125,11 @@ public ScalatraServerCodegen() {
importMapping.put("LocalDateTime", "org.joda.time.LocalDateTime");
importMapping.put("LocalDate", "org.joda.time.LocalDate");
importMapping.put("LocalTime", "org.joda.time.LocalTime");
importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");
importMapping.put("Set", "scala.collection.immutable.Set");
importMapping.put("ListSet", "scala.collection.immutable.ListSet");

instantiationTypes.put("set", "Set");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ public ScalazClientCodegen() {
importMapping.remove("List");
importMapping.remove("Set");
importMapping.remove("Map");

importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");

// Overrides defaults applied in DefaultCodegen which don't apply cleanly to Scala.
importMapping.put("Date", "java.util.Date");
Expand Down Expand Up @@ -214,7 +212,7 @@ public String toDefaultValue(Schema p) {
} else if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
String collectionType = typeMapping.get("array");
String collectionType = ModelUtils.isSet(ap) ? typeMapping.get("set") : typeMapping.get("array");

// We assume that users would map these collections to a monoid with an identity function
// There's no reason to assume mutable structure here (which may make consumption more difficult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,10 @@ public static boolean isArraySchema(Schema schema) {
return (schema instanceof ArraySchema);
}

public static boolean isSet(Schema schema) {
return ModelUtils.isArraySchema(schema) && Boolean.TRUE.equals(schema.getUniqueItems());
}

public static boolean isStringSchema(Schema schema) {
if (schema instanceof StringSchema || SchemaTypeUtil.STRING_TYPE.equals(schema.getType())) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,37 @@ public void complexListPropertyTest() {
Assert.assertTrue(property1.isContainer);
}

@Test(description = "convert a model with set (unique array) property")
public void complexSetPropertyTest() {
final Schema model = new Schema()
.description("a sample model")
.addProperties("children", new ArraySchema()
.items(new Schema().$ref("#/definitions/Children"))
.uniqueItems(Boolean.TRUE));
final DefaultCodegen codegen = new ScalaAkkaClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", model);

Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);

final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, "children");
Assert.assertEquals(property1.complexType, "Children");
Assert.assertEquals(property1.getter, "getChildren");
Assert.assertEquals(property1.setter, "setChildren");
Assert.assertEquals(property1.dataType, "Set[Children]");
Assert.assertEquals(property1.name, "children");
Assert.assertEquals(property1.defaultValue, "Set[Children].empty ");
Assert.assertEquals(property1.baseType, "Set");
Assert.assertEquals(property1.containerType, "set");
Assert.assertFalse(property1.required);
Assert.assertTrue(property1.isContainer);
}

@Test(description = "convert a model with complex map property")
public void complexMapPropertyTest() {
final Schema model = new Schema()
Expand Down Expand Up @@ -258,10 +289,33 @@ public void arrayModelTest() {
Assert.assertEquals(cm.description, "an array model");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.parent, "ListBuffer[Children]");
Assert.assertEquals(cm.arrayModelType, "Children");
Assert.assertEquals(cm.imports.size(), 2);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("ListBuffer", "Children")).size(), 2);
}

@Test(description = "convert an array model with unique items to set")
public void arrayAsSetModelTest() {
final Schema schema = new ArraySchema()
.items(new Schema().$ref("#/definitions/Children"))
.description("a set of Children models");
schema.setUniqueItems(true);

final DefaultCodegen codegen = new ScalaAkkaClientCodegen();
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, "a set of Children models");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.parent, "Set[Children]");
Assert.assertEquals(cm.arrayModelType, "Children");
Assert.assertEquals(cm.imports.size(), 2);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Set", "Children")).size(), 2);
}

@Test(description = "convert a map model")
public void mapModelTest() {
final Schema model = new Schema()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.languages.ScalaAkkaClientCodegen;
import org.openapitools.codegen.languages.ScalaHttpClientCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -119,6 +120,37 @@ public void listPropertyTest() {
Assert.assertTrue(property1.isContainer);
}

@Test(description = "convert a model with set (unique array) property")
public void complexSetPropertyTest() {
final Schema model = new Schema()
.description("a sample model")
.addProperties("children", new ArraySchema()
.items(new Schema().$ref("#/definitions/Children"))
.uniqueItems(Boolean.TRUE));
final DefaultCodegen codegen = new ScalaHttpClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", model);

Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);

final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, "children");
Assert.assertEquals(property1.complexType, "Children");
Assert.assertEquals(property1.getter, "getChildren");
Assert.assertEquals(property1.setter, "setChildren");
Assert.assertEquals(property1.dataType, "Set[Children]");
Assert.assertEquals(property1.name, "children");
Assert.assertEquals(property1.defaultValue, "Set[Children].empty ");
Assert.assertEquals(property1.baseType, "Set");
Assert.assertEquals(property1.containerType, "set");
Assert.assertFalse(property1.required);
Assert.assertTrue(property1.isContainer);
}

@Test(description = "convert a model with a map property")
public void mapPropertyTest() {
final Schema model = new Schema()
Expand Down Expand Up @@ -256,6 +288,28 @@ public void arrayModelTest() {
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("ListBuffer", "Children")).size(), 2);
}

@Test(description = "convert an array model with unique items to set")
public void arrayAsSetModelTest() {
final Schema schema = new ArraySchema()
.items(new Schema().$ref("#/definitions/Children"))
.description("a set of Children models");
schema.setUniqueItems(true);

final DefaultCodegen codegen = new ScalaHttpClientCodegen();
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, "a set of Children models");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.parent, "Set[Children]");
Assert.assertEquals(cm.arrayModelType, "Children");
Assert.assertEquals(cm.imports.size(), 2);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Set", "Children")).size(), 2);
}

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