Fix nested map support (#22643)

Also includes a fix for enums :)
This commit is contained in:
Devon
2026-01-12 00:42:16 -05:00
committed by GitHub
parent 0da98b06f8
commit dff00c86d6
6 changed files with 135 additions and 7 deletions

View File

@@ -458,10 +458,13 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
name = normalizeSchemaName(name);
CodegenModel mdl = super.fromModel(name, schema);
// Detect integer enums - check both the schema type and the dataType
// Detect numeric enums - check both the schema type and the dataType
// Note: "number" type in OpenAPI can include integer values in enums
if (mdl.isEnum) {
String schemaType = schema != null ? schema.getType() : null;
if ("integer".equals(schemaType) || "int".equals(mdl.dataType) || "int64".equals(mdl.dataType)) {
if ("integer".equals(schemaType) || "number".equals(schemaType) ||
"int".equals(mdl.dataType) || "int64".equals(mdl.dataType) ||
"float".equals(mdl.dataType) || "float64".equals(mdl.dataType)) {
mdl.vendorExtensions.put("x-is-integer-enum", true);
}
}
@@ -606,22 +609,38 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
return objs;
}
/**
* Resolve a schema reference to its target schema.
* This is needed to properly detect nested maps/arrays when the schema is a $ref.
*/
private Schema resolveSchema(Schema schema) {
if (schema != null && schema.get$ref() != null) {
Schema resolved = ModelUtils.getReferencedSchema(this.openAPI, schema);
return resolved != null ? resolved : schema;
}
return schema;
}
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
Schema inner = ModelUtils.getSchemaItems(p);
// Resolve the schema to check for nested maps/arrays - refs that point to map/array schemas
Schema resolved = resolveSchema(p);
if (ModelUtils.isArraySchema(resolved)) {
Schema inner = ModelUtils.getSchemaItems(resolved);
if (inner == null) {
return null;
}
return "seq[" + getTypeDeclaration(inner) + "]";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
} else if (ModelUtils.isMapSchema(resolved)) {
Schema inner = ModelUtils.getAdditionalProperties(resolved);
if (inner == null) {
inner = new StringSchema();
}
return "Table[string, " + getTypeDeclaration(inner) + "]";
}
// For non-containers, use the original schema to preserve model names
String schemaType = getSchemaType(p);
if (typeMapping.containsKey(schemaType)) {
return typeMapping.get(schemaType);
@@ -719,10 +738,17 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public String toEnumVarName(String name, String datatype) {
// Handle negative numbers by prefixing with "Neg" to avoid collisions
// e.g., -1 and 1 would both become `1` without this, causing invalid syntax
if (name.startsWith("-")) {
name = "Neg" + name.substring(1);
}
name = name.replace(" ", "_");
name = StringUtils.camelize(name);
// starts with number or contains any character not allowed,see
// starts with number or contains any character not allowed, see
// https://nim-lang.org/docs/manual.html#lexical-analysis-identifiers-amp-keywords
if (isValidIdentifier(name)) {
return name;
} else {

View File

@@ -1020,3 +1020,29 @@ components:
type: array
items:
$ref: '#/components/schemas/PetReview'
# Test numeric enum with negative values
DigestEmailFrequency:
description: Email digest frequency with negative value for disabled
type: number
enum: [-1, 0, 1, 2]
# Test nested maps (map of maps)
StringMap:
description: A simple string to string map
type: object
additionalProperties:
type: string
NestedStringMap:
description: A nested map (string to map of string to string)
type: object
additionalProperties:
$ref: '#/components/schemas/StringMap'
PetStatistics:
description: Statistics about a pet including nested map for health records
type: object
properties:
groomingHistory:
$ref: '#/components/schemas/StringMap'
healthRecords:
$ref: '#/components/schemas/NestedStringMap'

View File

@@ -7,6 +7,7 @@ petstore/apis/api_user.nim
petstore/models/model_any_type.nim
petstore/models/model_api_response.nim
petstore/models/model_category.nim
petstore/models/model_digest_email_frequency.nim
petstore/models/model_get_pet_reviews200response.nim
petstore/models/model_get_pet_reviews_response_with_presence.nim
petstore/models/model_get_pet_stats200response.nim
@@ -23,6 +24,7 @@ petstore/models/model_pet_positions.nim
petstore/models/model_pet_priority.nim
petstore/models/model_pet_review.nim
petstore/models/model_pet_reviews_response.nim
petstore/models/model_pet_statistics.nim
petstore/models/model_record_string_before_string_or_null_after_string_or_null_value.nim
petstore/models/model_tag.nim
petstore/models/model_unfavorite_pet_request.nim

View File

@@ -10,6 +10,7 @@
# Models
import petstore/models/model_api_response
import petstore/models/model_category
import petstore/models/model_digest_email_frequency
import petstore/models/model_get_pet_reviews200response
import petstore/models/model_get_pet_reviews_response_with_presence
import petstore/models/model_get_pet_stats200response
@@ -25,6 +26,7 @@ import petstore/models/model_pet_positions
import petstore/models/model_pet_priority
import petstore/models/model_pet_review
import petstore/models/model_pet_reviews_response
import petstore/models/model_pet_statistics
import petstore/models/model_record_string_before_string_or_null_after_string_or_null_value
import petstore/models/model_tag
import petstore/models/model_unfavorite_pet_request
@@ -32,6 +34,7 @@ import petstore/models/model_user
export model_api_response
export model_category
export model_digest_email_frequency
export model_get_pet_reviews200response
export model_get_pet_reviews_response_with_presence
export model_get_pet_stats200response
@@ -47,6 +50,7 @@ export model_pet_positions
export model_pet_priority
export model_pet_review
export model_pet_reviews_response
export model_pet_statistics
export model_record_string_before_string_or_null_after_string_or_null_value
export model_tag
export model_unfavorite_pet_request

View File

@@ -0,0 +1,50 @@
#
# OpenAPI Petstore
#
# This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
# The version of the OpenAPI document: 1.0.0
#
# Generated by: https://openapi-generator.tech
#
import json
import tables
import marshal
import options
type DigestEmailFrequency* {.pure.} = enum
Neg1
`0`
`1`
`2`
func `%`*(v: DigestEmailFrequency): JsonNode =
result = case v:
of DigestEmailFrequency.Neg1: %(-1)
of DigestEmailFrequency.`0`: %(0)
of DigestEmailFrequency.`1`: %(1)
of DigestEmailFrequency.`2`: %(2)
func `$`*(v: DigestEmailFrequency): string =
result = case v:
of DigestEmailFrequency.Neg1: $(-1)
of DigestEmailFrequency.`0`: $(0)
of DigestEmailFrequency.`1`: $(1)
of DigestEmailFrequency.`2`: $(2)
proc to*(node: JsonNode, T: typedesc[DigestEmailFrequency]): DigestEmailFrequency =
if node.kind != JInt:
raise newException(ValueError, "Expected integer for enum DigestEmailFrequency, got " & $node.kind)
let intVal = node.getInt()
case intVal:
of -1:
return DigestEmailFrequency.Neg1
of 0:
return DigestEmailFrequency.`0`
of 1:
return DigestEmailFrequency.`1`
of 2:
return DigestEmailFrequency.`2`
else:
raise newException(ValueError, "Invalid enum value for DigestEmailFrequency: " & $intVal)

View File

@@ -0,0 +1,20 @@
#
# OpenAPI Petstore
#
# This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
# The version of the OpenAPI document: 1.0.0
#
# Generated by: https://openapi-generator.tech
#
import json
import tables
import marshal
import options
type PetStatistics* = object
## Statistics about a pet including nested map for health records
groomingHistory*: Option[Table[string, string]] ## A simple string to string map
healthRecords*: Option[Table[string, Table[string, string]]] ## A nested map (string to map of string to string)