Skip to content

Maps that use enums as keys don't explicitly declare them in the resulting swagger def. #969

@joaonatalio

Description

@joaonatalio

Bug Description

In some parts of my API I have Maps that use enums as keys, such as uploadedLanguages example below:

@Schema(name = "DocumentUploads", description = "Model that aggregates document metadata by uploaded languages")
data class DocumentUploads(var metadata: DocumentMetadata,
var uploadedLanguages: Map<MimeType, Set<LanguageCode>>)

@Schema(name = "MimeType", description = "Type of currently supported file types on Seller Center.", enumAsRef = true)
enum class MimeType(val mediaType: MediaType) {
    PDF(MediaType.APPLICATION_PDF), HTML(MediaType(MediaType.APPLICATION_XHTML_XML, Charsets.UTF_8));
}

This generates the default "Map" spec where keys are not known and can vary.

"DocumentUploads": {
	"type": "object",
	"properties": {
		"metadata": {
			"$ref": "#/components/schemas/DocumentMetadata"
		},
		"uploadedLanguages": {
			"type": "object",
			"additionalProperties": {
				"uniqueItems": true,
				"type": "array",
				"items": {
					"$ref": "#/components/schemas/LanguageCode"
				}
			}
		}
	},
	"description": "Model that aggregates document metadata by uploaded languages"
}

However, since I need the resulting definition to explicitly declare possible keys as properties, I had to resort to this hack where I declare a fake swagger object with explicit properties.
This is really bad since it's hacky, hard to maintain and actually forces the required array in the resulting definition which could lead to some issues if I reuse this object as a payload input for the API.

@Schema(name = "DocumentUploads", description = "Model that aggregates document metadata by uploaded languages")
data class DocumentUploads(var metadata: DocumentMetadata,
                           @field:Schema(implementation = LanguagesByMimeType::class) var uploadedLanguages: Map<MimeType, Set<LanguageCode>>)

// Dummy object only used by springdoc.
@Schema(name = "LanguagesByMimeType", description = "Shows languages by mime type")
data class LanguagesByMimeType(@field:Schema(name="PDF", nullable = false, required = false) var pdf: Set<LanguageCode>,
                               @field:Schema(name="HTML", nullable = false, required = false)var html: Set<LanguageCode>)

Resulting in:

"DocumentUploads": {
	"required": ["metadata", "uploadedLanguages"],
	"type": "object",
	"properties": {
		"metadata": {
			"$ref": "#/components/schemas/DocumentMetadata"
		},
		"uploadedLanguages": {
			"$ref": "#/components/schemas/LanguagesByMimeType"
		}
	},
	"description": "Model that aggregates document metadata by uploaded languages"
}

"LanguagesByMimeType": {
	"required": ["HTML", "PDF"],
	"type": "object",
	"properties": {
		"PDF": {
			"uniqueItems": true,
			"type": "array",
			"items": {
				"$ref": "#/components/schemas/LanguageCode"
			}
		},
		"HTML": {
			"uniqueItems": true,
			"type": "array",
			"items": {
				"$ref": "#/components/schemas/LanguageCode"
			}
		}
	},
	"description": "Shows languages by mime type"
}

As a side note, this hack also has a bug. If I declare properties as upper-case as expected by the enum (eg. var PDF: Set<LanguageCode>) the resulting definition duplicates properties for some reason - camelcase and uppercase.

Proposed Solution

I would like to propose an alternative to handle such situations. It's understandable that springdoc can't automatically inspect map keys and determine if they're enums. But we could similarly help springdoc lib in figuring this part out via this sample property in @Schema:

@Schema(name = "DocumentUploads", description = "Model that aggregates document metadata by uploaded languages")
data class DocumentUploads(var metadata: DocumentMetadata,
                           @field:Schema(enumMap=true) var uploadedLanguages: Map<MimeType, Set<LanguageCode>>)

Resulting in:

"DocumentUploads": {
	"required": ["metadata", "uploadedLanguages"],
	"type": "object",
	"properties": {
		"metadata": {
			"$ref": "#/components/schemas/DocumentMetadata"
		},
		"uploadedLanguages": {
	       		"type": "object",
	   		"properties": {
	        		"PDF": {
	        			"uniqueItems": true,
	        			"type": "array",
	        			"items": {
	        				"$ref": "#/components/schemas/LanguageCode"
	        			}
	        		},
	        		"HTML": {
	        			"uniqueItems": true,
	        			"type": "array",
	        			"items": {
	        				"$ref": "#/components/schemas/LanguageCode"
	        			}
	        		}
	        	}
		}
	},
	"description": "Model that aggregates document metadata by uploaded languages"
}

The enumMap flag that provides this hint could be using in conjunction with requiredProperties for handling some requirements of the resulting object (eg. imagine you use this map payload as an input and you always expect "PDF" to be present in the map).

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis doesn't seem right

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions