Skip to content

Commit 5c5293f

Browse files
committed
fix minor issues and regenerate samples
1 parent a98f1bd commit 5c5293f

13 files changed

Lines changed: 359 additions & 102 deletions

File tree

modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@SerializedName("{{{vendorExtensions.x-base-name-literal}}}")
1010
{{/gson}}
1111
{{#jackson}}
12-
@get:JsonProperty("{{#lambda.escapeInNormalString}}{{{vendorExtensions.x-base-name-literal}}}{{/lambda.escapeInNormalString}}")
12+
@get:JsonProperty("{{#lambda.escapeInNormalString}}{{{baseName}}}{{/lambda.escapeInNormalString}}")
1313
{{/jackson}}
1414
{{#kotlinx_serialization}}
1515
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")

modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@SerializedName("{{{vendorExtensions.x-base-name-literal}}}")
1010
{{/gson}}
1111
{{#jackson}}
12-
@get:JsonProperty("{{#lambda.escapeInNormalString}}{{{vendorExtensions.x-base-name-literal}}}{{/lambda.escapeInNormalString}}")
12+
@get:JsonProperty("{{#lambda.escapeInNormalString}}{{{baseName}}}{{/lambda.escapeInNormalString}}")
1313
{{/jackson}}
1414
{{#kotlinx_serialization}}
1515
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,35 @@ public void testCompanionObjectGeneratesCompanionInModel() throws IOException {
923923
TestUtils.assertFileContains(petModel, "companion object { }");
924924
}
925925

926+
/**
927+
* Issue 20502: $ in property baseName must be escaped to \$ in @get:JsonProperty strings
928+
* when using jackson serialization library (uses escapeInNormalString lambda).
929+
*/
930+
@Test
931+
public void issue20502_dollarInBaseNameEscapedInJsonProperty() throws IOException {
932+
File output = Files.createTempDirectory("test").toFile();
933+
output.deleteOnExit();
934+
935+
KotlinClientCodegen codegen = new KotlinClientCodegen();
936+
codegen.setOutputDir(output.getAbsolutePath());
937+
codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, "jackson");
938+
codegen.processOpts();
939+
940+
new DefaultGenerator()
941+
.opts(new ClientOptInput()
942+
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue20502-kotlin-string-escaping.yaml"))
943+
.config(codegen))
944+
.generate();
945+
946+
Path modelFile = Paths.get(output + "/src/main/kotlin/org/openapitools/client/models/ItemWithAllEscapingEdgeCases.kt");
947+
// baseName "$id" must be escaped to "\$id" inside @get:JsonProperty
948+
TestUtils.assertFileContains(modelFile, "@get:JsonProperty(\"\\$id\")");
949+
// baseName "name$Value" must be escaped to "name\$Value"
950+
TestUtils.assertFileContains(modelFile, "@get:JsonProperty(\"name\\$Value\")");
951+
// plain baseName without $ must appear verbatim (verifies lambda resolves {{{baseName}}} at all)
952+
TestUtils.assertFileContains(modelFile, "@get:JsonProperty(\"commentCloseValue\")");
953+
}
954+
926955
private static class ModelNameTest {
927956
private final String expectedName;
928957
private final String expectedClassName;

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5867,5 +5867,8 @@ public void issue20502_starSlashNotCorruptedInModelDefaults() throws IOException
58675867
assertFileContains(modelFile, "\"starts /* comment and ends */\"");
58685868
// $ in string defaults must be escaped to \$ (not left raw, which would cause interpolation)
58695869
assertFileContains(modelFile, "\"\\$one");
5870+
// baseName with $ must be escaped in @get:JsonProperty (verifies lambda resolves baseName correctly)
5871+
assertFileContains(modelFile, "@get:JsonProperty(\"\\$id\")");
5872+
assertFileContains(modelFile, "@get:JsonProperty(\"name\\$Value\")");
58705873
}
58715874
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
openapi: 3.0.0
2+
info:
3+
title: "Kotlin string-escaping worst-case: SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
4+
description: |
5+
Multi-line description with every problematic sequence:
6+
double-quote: "
7+
single-backslash: \
8+
double-backslash: \\
9+
dollar: $interpolated
10+
comment-close: */
11+
comment-open: /*
12+
triple-quote: """
13+
all combined: SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""
14+
version: "1.0.0"
15+
16+
servers:
17+
- url: 'http://localhost/v1'
18+
19+
# External path-level docs to show externalDocs on an operation
20+
externalDocs:
21+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
22+
url: 'http://example.com/docs'
23+
24+
paths:
25+
26+
# ── Issue 1: $ in path parameter names ────────────────────────────────────
27+
/items/{item$Id}/sub/{item$SubId}:
28+
get:
29+
operationId: getItemWithDollarPathParams
30+
summary: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
31+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
32+
externalDocs:
33+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
34+
url: 'http://example.com/ops/getItem'
35+
parameters:
36+
# $ in path param name — double-quote YAML form
37+
- name: "item$Id"
38+
in: path
39+
required: true
40+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
41+
schema:
42+
type: string
43+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
44+
45+
# $ in path param name — single-quote YAML form
46+
- name: "item$SubId"
47+
in: path
48+
required: true
49+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
50+
schema:
51+
type: string
52+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
53+
54+
# $ in query param name, with default
55+
- name: "filter$Type"
56+
in: query
57+
required: false
58+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
59+
schema:
60+
type: string
61+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
62+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
63+
64+
# deprecated query param
65+
- name: "filter$SubType"
66+
in: query
67+
required: false
68+
deprecated: true
69+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
70+
schema:
71+
type: string
72+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
73+
default: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
74+
75+
# $ in header param name
76+
- name: "X-Custom$Header"
77+
in: header
78+
required: false
79+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
80+
schema:
81+
type: string
82+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
83+
84+
# $ in cookie param name
85+
- name: "session$Token"
86+
in: cookie
87+
required: false
88+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
89+
schema:
90+
type: string
91+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
92+
93+
responses:
94+
'200':
95+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
96+
content:
97+
application/json:
98+
schema:
99+
$ref: '#/components/schemas/ItemWithAllEscapingEdgeCases'
100+
101+
# ── Issue 2: $ / " / \ in form body ───────────────────────────────────────
102+
/items:
103+
post:
104+
operationId: createItemWithDollarFormParams
105+
summary: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
106+
description: |
107+
Multi-line description:
108+
double-quote: "
109+
single-backslash: \
110+
double-backslash: \\
111+
dollar-interpolation: $some
112+
comment-closer: */
113+
comment-opener: /*
114+
triple-quote: """
115+
requestBody:
116+
required: true
117+
content:
118+
application/x-www-form-urlencoded:
119+
schema:
120+
type: object
121+
properties:
122+
form$Name:
123+
type: string
124+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
125+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
126+
form$Value:
127+
type: string
128+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
129+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
130+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
131+
responses:
132+
'201':
133+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
134+
content:
135+
application/json:
136+
schema:
137+
$ref: '#/components/schemas/ItemWithAllEscapingEdgeCases'
138+
139+
components:
140+
schemas:
141+
142+
# ── Issue 3: $ / " / \ in property names, descriptions, defaults, examples
143+
ItemWithAllEscapingEdgeCases:
144+
type: object
145+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
146+
properties:
147+
148+
# property name starts with $
149+
$id:
150+
type: string
151+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
152+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
153+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
154+
155+
# property name has $ in middle
156+
name$Value:
157+
type: string
158+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
159+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
160+
default: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
161+
162+
# just backslashes (edge case: odd vs even number)
163+
backslashValue:
164+
type: string
165+
description: "one-bs: \\; two-bs: \\\\"
166+
example: "one-bs: \\; two-bs: \\\\"
167+
default: "one-bs: \\; two-bs: \\\\"
168+
169+
# just dollar signs
170+
dollarValue:
171+
type: string
172+
description: "$one $$two $$$three"
173+
example: "$one $$two $$$three"
174+
default: "$one $$two $$$three"
175+
176+
# comment-closing sequence (the escapeUnsafeCharacters bug)
177+
commentCloseValue:
178+
type: string
179+
description: "starts /* comment and ends */"
180+
example: "starts /* comment and ends */"
181+
default: "starts /* comment and ends */"
182+
183+
# triple-quote sequence (would break """ Kotlin strings)
184+
tripleQuoteValue:
185+
type: string
186+
description: 'contains """ triple quotes'
187+
example: 'contains """ triple quotes'
188+
default: 'contains """ triple quotes'
189+
190+
# multiline description (YAML literal block)
191+
multilineDescription:
192+
type: string
193+
description: |
194+
Line one with $dollar and "quote" and \backslash
195+
Line two with */ comment-close and /* comment-open
196+
Line three with """ triple-quote
197+
example: "single line example"
198+
199+
# integer with default (should not be quoted in output)
200+
intWithDefault:
201+
type: integer
202+
format: int32
203+
description: "integer property, default should not be quoted"
204+
default: 42
205+
206+
# boolean with default
207+
boolWithDefault:
208+
type: boolean
209+
description: "boolean property"
210+
default: false
211+
212+
# nested object with $ properties
213+
details$Info:
214+
type: object
215+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
216+
properties:
217+
detail$One:
218+
type: string
219+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
220+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
221+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
222+
detail$Two:
223+
type: integer
224+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
225+
example: 42

samples/documentation/html/index.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ <h3 class="field-label">Request body</h3>
248248
<div class="field-items">
249249
<div class="param">Pet <a href="#Pet">Pet</a> (required)</div>
250250

251-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
251+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; Pet object that needs to be added to the store </div>
252252

253253
</div> <!-- field-items -->
254254

@@ -663,7 +663,7 @@ <h3 class="field-label">Request body</h3>
663663
<div class="field-items">
664664
<div class="param">Pet <a href="#Pet">Pet</a> (required)</div>
665665

666-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
666+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; Pet object that needs to be added to the store </div>
667667

668668
</div> <!-- field-items -->
669669

@@ -992,7 +992,7 @@ <h3 class="field-label">Request body</h3>
992992
<div class="field-items">
993993
<div class="param">Order <a href="#Order">Order</a> (required)</div>
994994

995-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
995+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; order placed for purchasing the pet </div>
996996

997997
</div> <!-- field-items -->
998998

@@ -1064,7 +1064,7 @@ <h3 class="field-label">Request body</h3>
10641064
<div class="field-items">
10651065
<div class="param">User <a href="#User">User</a> (required)</div>
10661066

1067-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
1067+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; Created user object </div>
10681068

10691069
</div> <!-- field-items -->
10701070

@@ -1100,7 +1100,7 @@ <h3 class="field-label">Request body</h3>
11001100
<div class="field-items">
11011101
<div class="param">User array[<a href="#User">User</a>] (required)</div>
11021102

1103-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
1103+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; List of user object </div>
11041104

11051105
</div> <!-- field-items -->
11061106

@@ -1136,7 +1136,7 @@ <h3 class="field-label">Request body</h3>
11361136
<div class="field-items">
11371137
<div class="param">User array[<a href="#User">User</a>] (required)</div>
11381138

1139-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
1139+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; List of user object </div>
11401140

11411141
</div> <!-- field-items -->
11421142

@@ -1354,7 +1354,7 @@ <h3 class="field-label">Request body</h3>
13541354
<div class="field-items">
13551355
<div class="param">User <a href="#User">User</a> (required)</div>
13561356

1357-
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
1357+
<div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; Updated user object </div>
13581358

13591359
</div> <!-- field-items -->
13601360

0 commit comments

Comments
 (0)