Skip to content

Commit e60c51a

Browse files
committed
[scala][http4s] fix codegen for using reserved words in openapi
1 parent 6fdb632 commit e60c51a

File tree

7 files changed

+53
-21
lines changed

7 files changed

+53
-21
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sServerCodegen.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
package org.openapitools.codegen.languages;
1818

19+
import com.google.common.collect.ImmutableMap;
20+
import com.samskivert.mustache.Escapers;
21+
import com.samskivert.mustache.Mustache;
1922
import io.swagger.v3.oas.models.media.Schema;
2023
import org.openapitools.codegen.*;
2124
import org.openapitools.codegen.meta.features.*;
2225
import org.openapitools.codegen.model.*;
26+
import org.openapitools.codegen.templating.mustache.EscapeKeywordLambda;
2327
import org.openapitools.codegen.utils.ModelUtils;
2428
import org.slf4j.Logger;
2529
import org.slf4j.LoggerFactory;
@@ -140,7 +144,7 @@ public ScalaHttp4sServerCodegen() {
140144
additionalProperties.put("infoEmail", "team@openapitools.org");
141145
additionalProperties.put("licenseInfo", "Apache 2.0");
142146
additionalProperties.put("licenseUrl", "http://apache.org/licenses/LICENSE-2.0.html");
143-
147+
additionalProperties.put("fnEscapeBacktick", new EscapeBacktickLambda());
144148

145149
languageSpecificPrimitives = new HashSet<>(
146150
Arrays.asList(
@@ -557,7 +561,12 @@ public String getHelp() {
557561

558562
@Override
559563
public String escapeReservedWord(String name) {
560-
return "_" + name;
564+
if (this.reservedWordsMappings().containsKey(name)) {
565+
return this.reservedWordsMappings().get(name);
566+
}
567+
// Reserved words will be further escaped at the mustache compiler level.
568+
// Scala escaping done here (via `, without compiler escaping) would otherwise be HTML encoded.
569+
return "`" + name + "`";
561570
}
562571

563572
@Override
@@ -807,12 +816,12 @@ private String cpToPathParameter(CodegenParameter cp, Set<String> imports, Map<S
807816

808817
if (_vendorExtensions.size() == 1) { // only `x-type`
809818
if ("String".equals(cp.getDataType())) {
810-
return cp.paramName;
819+
return escapeReservedWordUnapply(cp.baseName);
811820
} else {
812-
return cp.dataType + "Varr(" + cp.paramName + ")";
821+
return cp.dataType + "Varr(" + escapeReservedWordUnapply(cp.baseName) + ")";
813822
}
814823
} else {
815-
return cp.baseName + "Varr(" + cp.paramName + ")";
824+
return cp.baseName + "Varr(" + escapeReservedWordUnapply(cp.baseName) + ")";
816825
}
817826
}
818827

@@ -844,11 +853,34 @@ private String cpToQueryParameter(CodegenParameter cp, Set<String> imports, Map<
844853
}
845854

846855
vendorExtensions.putAll(refineProp(cp, imports));
847-
return cp.baseName + "QueryParam(" + cp.paramName + ")";
856+
return cp.baseName + "QueryParam(" + escapeReservedWordUnapply(cp.baseName) + ")";
848857
}
849858

850859
@Override
851860
public GeneratorLanguage generatorLanguage() {
852861
return GeneratorLanguage.SCALA;
853862
}
863+
864+
@Override
865+
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
866+
return super.addMustacheLambdas()
867+
.put("escapeReservedWordUnapply", new EscapeKeywordLambda(this::escapeReservedWordUnapply));
868+
}
869+
870+
private String escapeReservedWordUnapply(String value) {
871+
// The unapply method doesn’t allow you to work with reserved variables via backticks;
872+
// in such cases you should use the variable via a placeholder instead.
873+
return isReservedWord(value) ? "_" + value : value;
874+
}
875+
876+
private static class EscapeBacktickLambda extends AbstractScalaCodegen.CustomLambda {
877+
@Override
878+
public String formatFragment(String fragment) {
879+
if (fragment.startsWith("`") && fragment.endsWith("`")) {
880+
String unescaped = fragment.substring(1, fragment.length() - 1);
881+
return "`" + Escapers.HTML.escape(unescaped) + "`";
882+
}
883+
return Escapers.HTML.escape(fragment);
884+
}
885+
}
854886
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
{{#pathParams}}
2-
{{paramName}}: {{{vendorExtensions.x-type}}},
2+
{{#fnEscapeBacktick}}{{{paramName}}}{{/fnEscapeBacktick}}: {{{vendorExtensions.x-type}}},
33
{{/pathParams}}
44
{{#queryParams}}
55
{{#isArray}}
66
{{#required}}
7-
{{paramName}}: List[{{{items.vendorExtensions.x-type}}}],
7+
{{#fnEscapeBacktick}}{{{paramName}}}{{/fnEscapeBacktick}}: List[{{{items.vendorExtensions.x-type}}}],
88
{{/required}}
99
{{^required}}
10-
{{paramName}}: Option[List[{{{items.vendorExtensions.x-type}}}]],
10+
{{#fnEscapeBacktick}}{{{paramName}}}{{/fnEscapeBacktick}}: Option[List[{{{items.vendorExtensions.x-type}}}]],
1111
{{/required}}
1212
{{/isArray}}
1313
{{^isArray}}
1414
{{#required}}
15-
{{paramName}}: {{{vendorExtensions.x-type}}},
15+
{{#fnEscapeBacktick}}{{{paramName}}}{{/fnEscapeBacktick}}: {{{vendorExtensions.x-type}}},
1616
{{/required}}
1717
{{^required}}
18-
{{paramName}}: Option[{{{vendorExtensions.x-type}}}],
18+
{{#fnEscapeBacktick}}{{{paramName}}}{{/fnEscapeBacktick}}: Option[{{{vendorExtensions.x-type}}}],
1919
{{/required}}
2020
{{/isArray}}
2121
{{/queryParams}}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{^authName}}
2-
delegate.{{operationId}}.handle(req, {{#pathParams}}{{paramName}}, {{/pathParams}}{{#queryParams}}{{paramName}}, {{/queryParams}}responses)
2+
delegate.{{operationId}}.handle(req, {{#pathParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/pathParams}}{{#queryParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/queryParams}}responses)
33
{{/authName}}
44
{{#authName}}
5-
delegate.{{operationId}}.handle_{{authName}}(auth, req, {{#pathParams}}{{paramName}}, {{/pathParams}}{{#queryParams}}{{paramName}}, {{/queryParams}}responses)
5+
delegate.{{operationId}}.handle_{{authName}}(auth, req, {{#pathParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/pathParams}}{{#queryParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/queryParams}}responses)
66
{{/authName}}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{^authName}}
2-
delegate.{{operationId}}.handle(req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{paramName}}, {{/pathParams}}{{#queryParams}}{{paramName}}, {{/queryParams}}responses)
2+
delegate.{{operationId}}.handle(req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/pathParams}}{{#queryParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/queryParams}}responses)
33
{{/authName}}
44
{{#authName}}
5-
delegate.{{operationId}}.handle_{{authName}}(auth, req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{paramName}}, {{/pathParams}}{{#queryParams}}{{paramName}}, {{/queryParams}}responses)
5+
delegate.{{operationId}}.handle_{{authName}}(auth, req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/pathParams}}{{#queryParams}}{{#lambda.escapeReservedWordUnapply}}{{baseName}}{{/lambda.escapeReservedWordUnapply}}, {{/queryParams}}responses)
66
{{/authName}}

modules/openapi-generator/src/main/resources/scala-http4s-server/types.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {{modelPackage}}.{{classname}}.{{classname}}
2323
/**
2424
* {{{description}}}
2525
{{#vars}}
26-
* @param {{name}} {{{description}}}
26+
* @param {{#fnEscapeBacktick}}{{{name}}}{{/fnEscapeBacktick}} {{{description}}}
2727
{{/vars}}
2828
*/
2929
{{#vendorExtensions.x-isSealedTrait}}
@@ -104,7 +104,7 @@ object {{classname}} extends Enumeration {
104104
{{#vendorExtensions.x-another}}
105105
case class {{classname}}(
106106
{{#vars}}
107-
{{name}}: {{^required}}Option[{{{vendorExtensions.x-type}}}]{{/required}}{{#required}}{{{vendorExtensions.x-type}}}{{/required}}{{^-last}},{{/-last}}
107+
{{#fnEscapeBacktick}}{{{name}}}{{/fnEscapeBacktick}}: {{^required}}Option[{{{vendorExtensions.x-type}}}]{{/required}}{{#required}}{{{vendorExtensions.x-type}}}{{/required}}{{^-last}},{{/-last}}
108108
{{/vars}}
109109
){{#vendorExtensions.x-extends}} extends {{.}}{{/vendorExtensions.x-extends}}{{#vendorExtensions.x-extendsWith}} with {{.}}{{/vendorExtensions.x-extendsWith}}
110110
object {{classname}} {

samples/server/petstore/scala-http4s-server/src/main/scala/org/openapitools/apis/FakeApi.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ trait FakeApiDelegate[F[_]] {
5757

5858
def handle(
5959
req: Request[F],
60-
_type: String,
61-
_var: Option[String],
60+
`type`: String,
61+
`var`: Option[String],
6262
responses: reservedWordsResponses[F]
6363
): F[Response[F]]
6464

samples/server/petstore/scala-http4s-server/src/main/scala/org/openapitools/models/types.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import java.time.ZonedDateTime
1515
/**
1616
* Describes the result of uploading an image resource
1717
* @param code
18-
* @param _type
18+
* @param `type`
1919
* @param message
2020
*/
2121

2222
case class ApiResponse(
2323
code: Option[Int],
24-
_type: Option[String],
24+
`type`: Option[String],
2525
message: Option[String]
2626
)
2727
object ApiResponse {

0 commit comments

Comments
 (0)