swagger-core icon indicating copy to clipboard operation
swagger-core copied to clipboard

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

Open joaonatalio opened this issue 4 years ago • 9 comments

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).

joaonatalio avatar Dec 05 '20 11:12 joaonatalio

bump

joaonatalio avatar Jun 17 '21 09:06 joaonatalio

any updates on this?

Ademord avatar Oct 27 '21 14:10 Ademord

any update?

JayAhn2 avatar Jun 02 '22 23:06 JayAhn2

We also are getting into this. Please SWAGGER TEAM, the issue has been properly reported, give us our daily portion of hope :-)

MonDeveloper avatar Jul 01 '22 12:07 MonDeveloper

Will we get this in 2023?

KetelsenKent avatar Jan 03 '23 07:01 KetelsenKent

Any chance to get it in 2024?

pic-Nick avatar Nov 02 '23 13:11 pic-Nick