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

fqdn=true oneOf invalid refs (global / not relative) #4036

Closed
fkowal opened this issue Oct 4, 2021 · 2 comments
Closed

fqdn=true oneOf invalid refs (global / not relative) #4036

fkowal opened this issue Oct 4, 2021 · 2 comments

Comments

@fkowal
Copy link

fkowal commented Oct 4, 2021

when following the example from #3046 (comment)

to having the inheritance / sealed class support work
I noticed that swagger-ui is maxing requests 2 requests
.../api-docs/com.example.Cat and api-docs/com.example.Dog both return 404

both of these are cause by using fqdn=true and oneOf=[Cat::class, Dog::class] in the example below

for whatever reason the generated $ref's are "absolute/global" rather than relative

this is strange because "link's / refs" for discriminator.mapping are correct

I am running the latest org.springdoc:springdoc-openapi-ui:1.5.10 and kotlin packages

@Schema(
    oneOf = [Cat::class, Dog::class], // <- this is required for UI to display the 
    discriminatorProperty = "type",
    discriminatorMapping = [
        DiscriminatorMapping(value = "DOG", schema = Dog::class),
        DiscriminatorMapping(value = "CAT", schema = Cat::class)
    ],
    subTypes = [Cat::class, Dog::class]
)
sealed class Pet {...}
data class Dog(..) : Pet()
data class Cat(..) : Pet()

with fqdn=true

"com.example.Pet": {
        "required": [
          "type"
        ],
        "type": "object",
        "properties": {
        ...
        },
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "CAT": "#/components/schemas/com.example.Cat
            "DOT": "#/components/schemas/com.example.Dog"
          }
        },
        "oneOf": [
          {
            "$ref": "com.example.Cat" // this is where the error is cause <- this is not a relative ref
          },
          {
            "$ref": "com.example.Dog" // this is where the error is cause <- this is not a relative ref
          }
        ]
      },

with fqdn=false

"oneOf": [
          {
            "$ref": "#/components/schemas/Cat" // this is a relative link
          },
          {
            "$ref": "#/components/schemas/Dog" // this is a relativel link
          }
        ]
@fkowal fkowal changed the title fqdn=true invalid refs (global / not relative) fqdn=true oneOf invalid refs (global / not relative) Oct 4, 2021
@fuxao
Copy link

fuxao commented Feb 28, 2023

The problem is the class io.swagger.v3.oas.models.media.Schema in $ref setter method:

   public void set$ref(String $ref) {
        if ($ref != null && !$ref.startsWith("#") && ($ref.indexOf('.') == -1 && $ref.indexOf('/') == -1)) {
            $ref = Components.COMPONENTS_SCHEMAS_REF + $ref;
        }
        this.$ref = $ref;
   }

If you are using springdoc.use-fqn = true, then name of object contains "dots" and the if condition is not true. When io.swagger.v3.core.jackson.ModelResolver calls the schema $ref setter for oneOf, allOf and anyOf, the constant Components.COMPONENTS_SCHEMAS_REF is not used and therefore the $ref.indexOf('.') == -1 condition is evaluated as false.

List<Class<?>> oneOfFiltered = Stream.of(oneOf)
                    .distinct()
                    .filter(c -> !this.shouldIgnoreClass(c))
                    .filter(c -> !(c.equals(Void.class)))
                    .collect(Collectors.toList());
            oneOfFiltered.forEach(c -> {
                Schema oneOfRef = context.resolve(new AnnotatedType().type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                if (oneOfRef != null) {
                    if (StringUtils.isBlank(oneOfRef.getName())) {
                        composedSchema.addOneOfItem(oneOfRef);
                    } else {
                        composedSchema.addOneOfItem(new Schema().$ref(oneOfRef.getName()));
                    }
                    // remove shared properties defined in the parent
                    if (isSubtype(beanDesc.getClassInfo(), c)) {
                        removeParentProperties(composedSchema, oneOfRef);
                    }
                }

            });

On the other hand, this constant is used for the main model. This is the reason why springdoc.use-fqn = true is OK in the standard cases.

Schema refSchema = new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + model.getName());

We have a workaround for Spring Boot (2.7,x). It is possible to define a custom ModelResolver to replace "dot" with other character (for example "underscore").

@Configuration
public class OpenApiConfiguration {
    ...
    @Bean
    public ModelResolver customModelConverter() {
        return new ModelResolver(Json.mapper()) {
            @Override
            protected String decorateModelName(AnnotatedType type, String originalName) {
                String name = super.decorateModelName(type, originalName);
                if (!StringUtils.isBlank(originalName)) {
                    name = name.replaceAll("[.$]", "_");
                }
                return name;
            }
        };
    }
}

@frantuma
Copy link
Member

frantuma commented Mar 1, 2023

Thanks for reporting and analyzing this. Should be fixed by #4371

@frantuma frantuma closed this as completed Mar 1, 2023
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

3 participants