Skip to content

Commit dff00c8

Browse files
authored
Fix nested map support (#22643)
Also includes a fix for enums :)
1 parent 0da98b0 commit dff00c8

File tree

6 files changed

+135
-7
lines changed

6 files changed

+135
-7
lines changed

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

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,13 @@ public CodegenModel fromModel(String name, Schema schema) {
458458
name = normalizeSchemaName(name);
459459
CodegenModel mdl = super.fromModel(name, schema);
460460

461-
// Detect integer enums - check both the schema type and the dataType
461+
// Detect numeric enums - check both the schema type and the dataType
462+
// Note: "number" type in OpenAPI can include integer values in enums
462463
if (mdl.isEnum) {
463464
String schemaType = schema != null ? schema.getType() : null;
464-
if ("integer".equals(schemaType) || "int".equals(mdl.dataType) || "int64".equals(mdl.dataType)) {
465+
if ("integer".equals(schemaType) || "number".equals(schemaType) ||
466+
"int".equals(mdl.dataType) || "int64".equals(mdl.dataType) ||
467+
"float".equals(mdl.dataType) || "float64".equals(mdl.dataType)) {
465468
mdl.vendorExtensions.put("x-is-integer-enum", true);
466469
}
467470
}
@@ -606,22 +609,38 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
606609
return objs;
607610
}
608611

612+
/**
613+
* Resolve a schema reference to its target schema.
614+
* This is needed to properly detect nested maps/arrays when the schema is a $ref.
615+
*/
616+
private Schema resolveSchema(Schema schema) {
617+
if (schema != null && schema.get$ref() != null) {
618+
Schema resolved = ModelUtils.getReferencedSchema(this.openAPI, schema);
619+
return resolved != null ? resolved : schema;
620+
}
621+
return schema;
622+
}
623+
609624
@Override
610625
public String getTypeDeclaration(Schema p) {
611-
if (ModelUtils.isArraySchema(p)) {
612-
Schema inner = ModelUtils.getSchemaItems(p);
626+
// Resolve the schema to check for nested maps/arrays - refs that point to map/array schemas
627+
Schema resolved = resolveSchema(p);
628+
629+
if (ModelUtils.isArraySchema(resolved)) {
630+
Schema inner = ModelUtils.getSchemaItems(resolved);
613631
if (inner == null) {
614632
return null;
615633
}
616634
return "seq[" + getTypeDeclaration(inner) + "]";
617-
} else if (ModelUtils.isMapSchema(p)) {
618-
Schema inner = ModelUtils.getAdditionalProperties(p);
635+
} else if (ModelUtils.isMapSchema(resolved)) {
636+
Schema inner = ModelUtils.getAdditionalProperties(resolved);
619637
if (inner == null) {
620638
inner = new StringSchema();
621639
}
622640
return "Table[string, " + getTypeDeclaration(inner) + "]";
623641
}
624642

643+
// For non-containers, use the original schema to preserve model names
625644
String schemaType = getSchemaType(p);
626645
if (typeMapping.containsKey(schemaType)) {
627646
return typeMapping.get(schemaType);
@@ -719,10 +738,17 @@ private String sanitizeNimIdentifier(String name) {
719738

720739
@Override
721740
public String toEnumVarName(String name, String datatype) {
741+
// Handle negative numbers by prefixing with "Neg" to avoid collisions
742+
// e.g., -1 and 1 would both become `1` without this, causing invalid syntax
743+
if (name.startsWith("-")) {
744+
name = "Neg" + name.substring(1);
745+
}
746+
722747
name = name.replace(" ", "_");
723748
name = StringUtils.camelize(name);
724749

725-
// starts with number or contains any character not allowed,see
750+
// starts with number or contains any character not allowed, see
751+
// https://nim-lang.org/docs/manual.html#lexical-analysis-identifiers-amp-keywords
726752
if (isValidIdentifier(name)) {
727753
return name;
728754
} else {

modules/openapi-generator/src/test/resources/3_0/nim/petstore.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,3 +1020,29 @@ components:
10201020
type: array
10211021
items:
10221022
$ref: '#/components/schemas/PetReview'
1023+
1024+
# Test numeric enum with negative values
1025+
DigestEmailFrequency:
1026+
description: Email digest frequency with negative value for disabled
1027+
type: number
1028+
enum: [-1, 0, 1, 2]
1029+
1030+
# Test nested maps (map of maps)
1031+
StringMap:
1032+
description: A simple string to string map
1033+
type: object
1034+
additionalProperties:
1035+
type: string
1036+
NestedStringMap:
1037+
description: A nested map (string to map of string to string)
1038+
type: object
1039+
additionalProperties:
1040+
$ref: '#/components/schemas/StringMap'
1041+
PetStatistics:
1042+
description: Statistics about a pet including nested map for health records
1043+
type: object
1044+
properties:
1045+
groomingHistory:
1046+
$ref: '#/components/schemas/StringMap'
1047+
healthRecords:
1048+
$ref: '#/components/schemas/NestedStringMap'

samples/client/petstore/nim/.openapi-generator/FILES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ petstore/apis/api_user.nim
77
petstore/models/model_any_type.nim
88
petstore/models/model_api_response.nim
99
petstore/models/model_category.nim
10+
petstore/models/model_digest_email_frequency.nim
1011
petstore/models/model_get_pet_reviews200response.nim
1112
petstore/models/model_get_pet_reviews_response_with_presence.nim
1213
petstore/models/model_get_pet_stats200response.nim
@@ -23,6 +24,7 @@ petstore/models/model_pet_positions.nim
2324
petstore/models/model_pet_priority.nim
2425
petstore/models/model_pet_review.nim
2526
petstore/models/model_pet_reviews_response.nim
27+
petstore/models/model_pet_statistics.nim
2628
petstore/models/model_record_string_before_string_or_null_after_string_or_null_value.nim
2729
petstore/models/model_tag.nim
2830
petstore/models/model_unfavorite_pet_request.nim

samples/client/petstore/nim/petstore.nim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# Models
1111
import petstore/models/model_api_response
1212
import petstore/models/model_category
13+
import petstore/models/model_digest_email_frequency
1314
import petstore/models/model_get_pet_reviews200response
1415
import petstore/models/model_get_pet_reviews_response_with_presence
1516
import petstore/models/model_get_pet_stats200response
@@ -25,13 +26,15 @@ import petstore/models/model_pet_positions
2526
import petstore/models/model_pet_priority
2627
import petstore/models/model_pet_review
2728
import petstore/models/model_pet_reviews_response
29+
import petstore/models/model_pet_statistics
2830
import petstore/models/model_record_string_before_string_or_null_after_string_or_null_value
2931
import petstore/models/model_tag
3032
import petstore/models/model_unfavorite_pet_request
3133
import petstore/models/model_user
3234

3335
export model_api_response
3436
export model_category
37+
export model_digest_email_frequency
3538
export model_get_pet_reviews200response
3639
export model_get_pet_reviews_response_with_presence
3740
export model_get_pet_stats200response
@@ -47,6 +50,7 @@ export model_pet_positions
4750
export model_pet_priority
4851
export model_pet_review
4952
export model_pet_reviews_response
53+
export model_pet_statistics
5054
export model_record_string_before_string_or_null_after_string_or_null_value
5155
export model_tag
5256
export model_unfavorite_pet_request
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#
2+
# OpenAPI Petstore
3+
#
4+
# This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
5+
# The version of the OpenAPI document: 1.0.0
6+
#
7+
# Generated by: https://openapi-generator.tech
8+
#
9+
10+
import json
11+
import tables
12+
import marshal
13+
import options
14+
15+
16+
type DigestEmailFrequency* {.pure.} = enum
17+
Neg1
18+
`0`
19+
`1`
20+
`2`
21+
22+
func `%`*(v: DigestEmailFrequency): JsonNode =
23+
result = case v:
24+
of DigestEmailFrequency.Neg1: %(-1)
25+
of DigestEmailFrequency.`0`: %(0)
26+
of DigestEmailFrequency.`1`: %(1)
27+
of DigestEmailFrequency.`2`: %(2)
28+
29+
func `$`*(v: DigestEmailFrequency): string =
30+
result = case v:
31+
of DigestEmailFrequency.Neg1: $(-1)
32+
of DigestEmailFrequency.`0`: $(0)
33+
of DigestEmailFrequency.`1`: $(1)
34+
of DigestEmailFrequency.`2`: $(2)
35+
proc to*(node: JsonNode, T: typedesc[DigestEmailFrequency]): DigestEmailFrequency =
36+
if node.kind != JInt:
37+
raise newException(ValueError, "Expected integer for enum DigestEmailFrequency, got " & $node.kind)
38+
let intVal = node.getInt()
39+
case intVal:
40+
of -1:
41+
return DigestEmailFrequency.Neg1
42+
of 0:
43+
return DigestEmailFrequency.`0`
44+
of 1:
45+
return DigestEmailFrequency.`1`
46+
of 2:
47+
return DigestEmailFrequency.`2`
48+
else:
49+
raise newException(ValueError, "Invalid enum value for DigestEmailFrequency: " & $intVal)
50+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#
2+
# OpenAPI Petstore
3+
#
4+
# This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
5+
# The version of the OpenAPI document: 1.0.0
6+
#
7+
# Generated by: https://openapi-generator.tech
8+
#
9+
10+
import json
11+
import tables
12+
import marshal
13+
import options
14+
15+
16+
type PetStatistics* = object
17+
## Statistics about a pet including nested map for health records
18+
groomingHistory*: Option[Table[string, string]] ## A simple string to string map
19+
healthRecords*: Option[Table[string, Table[string, string]]] ## A nested map (string to map of string to string)
20+

0 commit comments

Comments
 (0)