Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom ModelResolver and TypeNameResolver results in unexpected behaviour #2574

Open
razum90 opened this issue Apr 15, 2024 · 0 comments
Open

Comments

@razum90
Copy link

razum90 commented Apr 15, 2024

Hi!

I am using:

  • Spring Boot 3.2.4
  • Java 21
  • springdoc-openapi-starter-webmvc-ui 2.5.0

When implementing a custom ModelResolver and TypeNameResolver the behaviour of entities wrapped in a ResponseEntity changes.

Here is my implementation:

@Configuration
public class SwaggerAutoConfiguration {

    @Bean
    public CustomConverter customConverter(ObjectMapper objectMapper) {
        return new CustomConverter(objectMapper);
    }

    static class CustomTypeNameResolver extends TypeNameResolver {
        public CustomTypeNameResolver() {
            setUseFqn(true);
        }
    }

    public static class CustomConverter extends ModelResolver {

        public CustomConverter(ObjectMapper mapper) {
            super(mapper, new CustomTypeNameResolver());
        }
    }
}

I am using the above setup because I will have logic to modify the model names.

I would expect the above only to set useFqn to true, and the rest would stay the same. However that does not seem like the case for entities wrapped a ResponseEntity. Below is an example.

    @Operation(summary = "...")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200"
            ),
            @ApiResponse(
                    responseCode = "207",
                    description = "Partial success"
            ),
            @ApiResponse(
                    responseCode = "409",
                    description = "Failure"
            )
    })
    @PostMapping(path = AUTHENTICATE_PAYMENT)
    public ResponseEntity<AuthenticatePaymentResponse> authenticatePayment(
            @Valid @RequestBody AuthenticatePaymentRequest authenticatePaymentRequest,
            SomeOtherParameters someOtherParameters
    ) {
        ...
    }

After adding the configuration, the response body changes in Swagger for all defined ApiResponses on the endpoint like so:

{
  "headers": {
    "acceptLanguage": [
      {
        "range": "string",
        "weight": 0
      }
    ],
    "acceptPatch": [
      {
        "type": "string",
        "subtype": "string",
        "parameters": {
          "additionalProp1": "string",
          "additionalProp2": "string",
          "additionalProp3": "string"
        },
        "qualityValue": 0,
        "wildcardType": true,
        "wildcardSubtype": true,
        "subtypeSuffix": "string",
        "charset": {
          "registered": true
        },
        "concrete": true
      }
    ],
    "accessControlAllowCredentials": true,
    "accessControlExposeHeaders": [
      "string"
    ],
    "accessControlRequestHeaders": [
      "string"
    ],
    "accessControlRequestMethod": {},
    "acceptLanguageAsLocales": [
      {
        "language": "string",
        "displayName": "string",
        "country": "string",
        "variant": "string",
        "script": "string",
        "unicodeLocaleAttributes": [
          "string"
        ],
        "unicodeLocaleKeys": [
          "string"
        ],
        "displayLanguage": "string",
        "displayScript": "string",
        "displayCountry": "string",
        "displayVariant": "string",
        "extensionKeys": [
          "string"
        ],
        "iso3Language": "string",
        "iso3Country": "string"
      }
    ],
    "accessControlAllowHeaders": [
      "string"
    ],
    "accessControlAllowMethods": [
      {}
    ],
    "accessControlAllowOrigin": "string",
    "accessControlMaxAge": 0,
    "acceptCharset": [
      {
        "registered": true
      }
    ],
    "connection": [
      "string"
    ],
    "ifNoneMatch": [
      "string"
    ],
    "ifUnmodifiedSince": 0,
    "etag": "string",
    "expires": 0,
    "ifMatch": [
      "string"
    ],
    "allow": [
      {}
    ],
    "accept": [
      {
        "type": "string",
        "subtype": "string",
        "parameters": {
          "additionalProp1": "string",
          "additionalProp2": "string",
          "additionalProp3": "string"
        },
        "qualityValue": 0,
        "wildcardType": true,
        "wildcardSubtype": true,
        "subtypeSuffix": "string",
        "charset": {
          "registered": true
        },
        "concrete": true
      }
    ],
    "origin": "string",
    "pragma": "string",
    "range": [
      {}
    ],
    "upgrade": "string",
    "vary": [
      "string"
    ],
    "contentDisposition": {
      "type": "string",
      "name": "string",
      "filename": "string",
      "charset": {
        "registered": true
      },
      "size": 0,
      "creationDate": "2024-04-15T19:18:37.770Z",
      "modificationDate": "2024-04-15T19:18:37.770Z",
      "readDate": "2024-04-15T19:18:37.770Z",
      "attachment": true,
      "formData": true,
      "inline": true
    },
    "contentLanguage": {
      "language": "string",
      "displayName": "string",
      "country": "string",
      "variant": "string",
      "script": "string",
      "unicodeLocaleAttributes": [
        "string"
      ],
      "unicodeLocaleKeys": [
        "string"
      ],
      "displayLanguage": "string",
      "displayScript": "string",
      "displayCountry": "string",
      "displayVariant": "string",
      "extensionKeys": [
        "string"
      ],
      "iso3Language": "string",
      "iso3Country": "string"
    },
    "cacheControl": "string",
    "host": {
      "hostString": "string",
      "address": {
        "hostAddress": "string",
        "address": [
          "string"
        ],
        "hostName": "string",
        "linkLocalAddress": true,
        "multicastAddress": true,
        "anyLocalAddress": true,
        "loopbackAddress": true,
        "siteLocalAddress": true,
        "mcglobal": true,
        "mcnodeLocal": true,
        "mclinkLocal": true,
        "mcsiteLocal": true,
        "mcorgLocal": true,
        "canonicalHostName": "string"
      },
      "port": 0,
      "unresolved": true,
      "hostName": "string"
    },
    "location": "string",
    "contentType": {
      "type": "string",
      "subtype": "string",
      "parameters": {
        "additionalProp1": "string",
        "additionalProp2": "string",
        "additionalProp3": "string"
      },
      "qualityValue": 0,
      "wildcardType": true,
      "wildcardSubtype": true,
      "subtypeSuffix": "string",
      "charset": {
        "registered": true
      },
      "concrete": true
    },
    "contentLength": 0,
    "ifModifiedSince": 0,
    "empty": true,
    "lastModified": 0,
    "date": 0,
    "additionalProp1": [
      "string"
    ],
    "additionalProp2": [
      "string"
    ],
    "additionalProp3": [
      "string"
    ]
  },
  "body": <my-model>,
  "statusCodeValue": 0,
  "statusCode": {
    "is1xxInformational": true,
    "is2xxSuccessful": true,
    "is3xxRedirection": true,
    "is4xxClientError": true,
    "is5xxServerError": true,
    "error": true
  }
}

I would expect the defined configuration to work exactly the same as setting the below properties:

springdoc:
  use-fqn: true

Am I missing something here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant