forked from loafle/openapi-generator-original
[scala] Escape reserved words, support Array[Byte] (#7378)
* [scala] Escape reserved words, support Array[Byte] Previously, Array[Byte] was compiling to ArrayByte. This provides a type mapping to output the correct type. This also escapes reserved words with grave accents, as is most common in Scala. Escaping with an underscore prefix breaks serialization (in Jackson, for example) unless templates are modified manually. Escaping using grave accent should unblock most serializers from requiring template modifications. * [scala] Regenerate integration test outputs * [scala] Regenerate samples * [scala] Remove unused imports in related codegen files
This commit is contained in:
parent
acf70d0481
commit
75c0180c71
@ -6,6 +6,8 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.mustache.Escapers;
|
||||
import com.samskivert.mustache.Mustache;
|
||||
import io.swagger.codegen.CliOption;
|
||||
import io.swagger.codegen.CodegenConstants;
|
||||
import io.swagger.codegen.DefaultCodegen;
|
||||
@ -43,7 +45,50 @@ public abstract class AbstractScalaCodegen extends DefaultCodegen {
|
||||
"Any",
|
||||
"List",
|
||||
"Seq",
|
||||
"Map"));
|
||||
"Map",
|
||||
"Array"));
|
||||
|
||||
reservedWords.addAll(Arrays.asList(
|
||||
"abstract",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"def",
|
||||
"do",
|
||||
"else",
|
||||
"extends",
|
||||
"false",
|
||||
"final",
|
||||
"finally",
|
||||
"for",
|
||||
"forSome",
|
||||
"if",
|
||||
"implicit",
|
||||
"import",
|
||||
"lazy",
|
||||
"match",
|
||||
"new",
|
||||
"null",
|
||||
"object",
|
||||
"override",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"return",
|
||||
"sealed",
|
||||
"super",
|
||||
"this",
|
||||
"throw",
|
||||
"trait",
|
||||
"try",
|
||||
"true",
|
||||
"type",
|
||||
"val",
|
||||
"var",
|
||||
"while",
|
||||
"with",
|
||||
"yield"
|
||||
));
|
||||
|
||||
cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
|
||||
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
|
||||
@ -72,7 +117,30 @@ public abstract class AbstractScalaCodegen extends DefaultCodegen {
|
||||
if (this.reservedWordsMappings().containsKey(name)) {
|
||||
return this.reservedWordsMappings().get(name);
|
||||
}
|
||||
return "_" + name;
|
||||
// Reserved words will be further escaped at the mustache compiler level.
|
||||
// Scala escaping done here (via `, without compiler escaping) would otherwise be HTML encoded.
|
||||
return "`" + name + "`";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mustache.Compiler processCompiler(Mustache.Compiler compiler) {
|
||||
Mustache.Escaper SCALA = new Mustache.Escaper() {
|
||||
@Override public String escape (String text) {
|
||||
// Fix included as suggested by akkie in #6393
|
||||
// The given text is a reserved word which is escaped by enclosing it with grave accents. If we would
|
||||
// escape that with the default Mustache `HTML` escaper, then the escaper would also escape our grave
|
||||
// accents. So we remove the grave accents before the escaping and add it back after the escaping.
|
||||
if (text.startsWith("`") && text.endsWith("`")) {
|
||||
String unescaped = text.substring(1, text.length() - 1);
|
||||
return "`" + Escapers.HTML.escape(unescaped) + "`";
|
||||
}
|
||||
|
||||
// All none reserved words will be escaped with the default Mustache `HTML` escaper
|
||||
return Escapers.HTML.escape(text);
|
||||
}
|
||||
};
|
||||
|
||||
return compiler.withEscaper(SCALA);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,7 +5,6 @@ import io.swagger.codegen.*;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -96,6 +95,7 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC
|
||||
typeMapping.put("file", "File");
|
||||
typeMapping.put("binary", "Array[Byte]");
|
||||
typeMapping.put("ByteArray", "Array[Byte]");
|
||||
typeMapping.put("ArrayByte", "Array[Byte]");
|
||||
typeMapping.put("date-time", "Date");
|
||||
typeMapping.put("DateTime", "Date");
|
||||
|
||||
|
@ -37,7 +37,8 @@ public class AbstractScalaCodegenTest {
|
||||
|
||||
String result = abstractScalaCodegen.formatIdentifier(className, true);
|
||||
|
||||
Assert.assertTrue("_ReservedWord".equals(result));
|
||||
// NOTE: reserved words are further escaped at the compiler level.
|
||||
Assert.assertTrue("`ReservedWord`".equals(result));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -86,6 +86,7 @@ class HobbiesApi(
|
||||
* Query hobbies with some additional optional meaningless parameters
|
||||
*
|
||||
* @param s a string (optional, default to some string)
|
||||
* @param `class` a string, testing keyword escaping (optional, default to some string)
|
||||
* @param i an integer (optional, default to 1)
|
||||
* @param l a long (optional, default to 2)
|
||||
* @param bool a bool (optional, default to true)
|
||||
@ -97,8 +98,8 @@ class HobbiesApi(
|
||||
* @param bin an octet string (optional, default to DEADBEEF)
|
||||
* @return Hobby
|
||||
*/
|
||||
def getHobbies(s: Option[String] = Option("some string"), i: Option[Integer] = Option(1), l: Option[Long] = Option(2), bool: Option[Boolean] = Option(true), f: Option[Float] = Option(0.1), d: Option[Double] = Option(10.005), datetime: Option[Date] = Option(dateTimeFormatter.parse("2018-01-01T08:30:00Z-04:00")), date: Option[Date] = Option(dateFormatter.parse("2018-01-01")), b: Option[ArrayByte] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes), bin: Option[ArrayByte] = Option("DEADBEEF".getBytes)): Option[Hobby] = {
|
||||
val await = Try(Await.result(getHobbiesAsync(s, i, l, bool, f, d, datetime, date, b, bin), Duration.Inf))
|
||||
def getHobbies(s: Option[String] = Option("some string"), `class`: Option[String] = Option("some string"), i: Option[Integer] = Option(1), l: Option[Long] = Option(2), bool: Option[Boolean] = Option(true), f: Option[Float] = Option(0.1), d: Option[Double] = Option(10.005), datetime: Option[Date] = Option(dateTimeFormatter.parse("2018-01-01T08:30:00Z-04:00")), date: Option[Date] = Option(dateFormatter.parse("2018-01-01")), b: Option[Array[Byte]] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes), bin: Option[Array[Byte]] = Option("DEADBEEF".getBytes)): Option[Hobby] = {
|
||||
val await = Try(Await.result(getHobbiesAsync(s, `class`, i, l, bool, f, d, datetime, date, b, bin), Duration.Inf))
|
||||
await match {
|
||||
case Success(i) => Some(await.get)
|
||||
case Failure(t) => None
|
||||
@ -110,6 +111,7 @@ class HobbiesApi(
|
||||
* Query hobbies with some additional optional meaningless parameters
|
||||
*
|
||||
* @param s a string (optional, default to some string)
|
||||
* @param `class` a string, testing keyword escaping (optional, default to some string)
|
||||
* @param i an integer (optional, default to 1)
|
||||
* @param l a long (optional, default to 2)
|
||||
* @param bool a bool (optional, default to true)
|
||||
@ -121,8 +123,8 @@ class HobbiesApi(
|
||||
* @param bin an octet string (optional, default to DEADBEEF)
|
||||
* @return Future(Hobby)
|
||||
*/
|
||||
def getHobbiesAsync(s: Option[String] = Option("some string"), i: Option[Integer] = Option(1), l: Option[Long] = Option(2), bool: Option[Boolean] = Option(true), f: Option[Float] = Option(0.1), d: Option[Double] = Option(10.005), datetime: Option[Date] = Option(dateTimeFormatter.parse("2018-01-01T08:30:00Z-04:00")), date: Option[Date] = Option(dateFormatter.parse("2018-01-01")), b: Option[ArrayByte] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes), bin: Option[ArrayByte] = Option("DEADBEEF".getBytes)): Future[Hobby] = {
|
||||
helper.getHobbies(s, i, l, bool, f, d, datetime, date, b, bin)
|
||||
def getHobbiesAsync(s: Option[String] = Option("some string"), `class`: Option[String] = Option("some string"), i: Option[Integer] = Option(1), l: Option[Long] = Option(2), bool: Option[Boolean] = Option(true), f: Option[Float] = Option(0.1), d: Option[Double] = Option(10.005), datetime: Option[Date] = Option(dateTimeFormatter.parse("2018-01-01T08:30:00Z-04:00")), date: Option[Date] = Option(dateFormatter.parse("2018-01-01")), b: Option[Array[Byte]] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes), bin: Option[Array[Byte]] = Option("DEADBEEF".getBytes)): Future[Hobby] = {
|
||||
helper.getHobbies(s, `class`, i, l, bool, f, d, datetime, date, b, bin)
|
||||
}
|
||||
|
||||
}
|
||||
@ -130,6 +132,7 @@ class HobbiesApi(
|
||||
class HobbiesApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends ApiClient(client, config) {
|
||||
|
||||
def getHobbies(s: Option[String] = Option("some string"),
|
||||
`class`: Option[String] = Option("some string"),
|
||||
i: Option[Integer] = Option(1),
|
||||
l: Option[Long] = Option(2),
|
||||
bool: Option[Boolean] = Option(true),
|
||||
@ -137,8 +140,8 @@ class HobbiesApiAsyncHelper(client: TransportClient, config: SwaggerConfig) exte
|
||||
d: Option[Double] = Option(10.005),
|
||||
datetime: Option[Date] = Option(dateTimeFormatter.parse("2018-01-01T08:30:00Z-04:00")),
|
||||
date: Option[Date] = Option(dateFormatter.parse("2018-01-01")),
|
||||
b: Option[ArrayByte] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes),
|
||||
bin: Option[ArrayByte] = Option("DEADBEEF".getBytes)
|
||||
b: Option[Array[Byte]] = Option("c3dhZ2dlciBjb2RlZ2Vu".getBytes),
|
||||
bin: Option[Array[Byte]] = Option("DEADBEEF".getBytes)
|
||||
)(implicit reader: ClientResponseReader[Hobby]): Future[Hobby] = {
|
||||
// create path and map variables
|
||||
val path = (addFmt("/hobbies"))
|
||||
@ -151,6 +154,10 @@ class HobbiesApiAsyncHelper(client: TransportClient, config: SwaggerConfig) exte
|
||||
case Some(param) => queryParams += "s" -> param.toString
|
||||
case _ => queryParams
|
||||
}
|
||||
`class` match {
|
||||
case Some(param) => queryParams += "class" -> param.toString
|
||||
case _ => queryParams
|
||||
}
|
||||
i match {
|
||||
case Some(param) => queryParams += "i" -> param.toString
|
||||
case _ => queryParams
|
||||
|
@ -23,7 +23,7 @@ case class Hobby (
|
||||
enabled: Option[Boolean] = None,
|
||||
created: Option[Date] = None,
|
||||
timestamp: Option[Date] = None,
|
||||
bytes: Option[ArrayByte] = None,
|
||||
bytes: Option[Array[Byte]] = None,
|
||||
binary: Option[String] = None
|
||||
)
|
||||
|
||||
|
@ -167,6 +167,14 @@
|
||||
"in": "query",
|
||||
"default": "some string"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "a string, testing keyword escaping",
|
||||
"name": "class",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"default": "some string"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
|
@ -15,7 +15,7 @@ package io.swagger.client.model
|
||||
|
||||
case class ApiResponse (
|
||||
code: Option[Integer] = None,
|
||||
_type: Option[String] = None,
|
||||
`type`: Option[String] = None,
|
||||
message: Option[String] = None
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user