swagger-api / swagger-core

Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
http://swagger.io
Apache License 2.0
7.36k stars 2.17k forks source link

BUG @Schema#allowableValues adds enums but does not remove existing enum references when resolving enum as references and using open api 3.1.0 #4688

Open Lorenzo-Bracci opened 3 weeks ago

Lorenzo-Bracci commented 3 weeks ago

Problem

When generating OpenAPI 3.1.0 spec and setting ModelResolver#enumsAsRef to true the @Schema#allowableValues annotation on an enum property is not processed correctly. The expected behaviour is that the generated property has no enum schema reference but instead uses the enum values which have been specified through @Schema#allowableValues. Instead both the enums values specified through allowableValues and the original enum schema reference are generated. Note that this is not the same issue as #2949 because when using open api 3.1.0 but not resolving enums as reference that issues is fixed (i.e the entries specified in allwoableValues override the existing enum definition rather than adding to it).

Expected behaviour

@Schema#allowableValue overrides any schema reference to enums when generating OAS3.1

Reproducer

The issues is reproduced in the following test:

    @Test(description = "Shows that allowableValues from @Schema is not processed correctly when using oas 3.1.0 and resolving enums as refs")
    public void testModelUsingAllowableValuesWhenGeneratingEnumAsRefForOas31() {

        // by default resolve enums as reference types
        ModelResolver.enumsAsRef = true;

        String expectedYaml = "PojoUsingAllowableValues:\n" +
                "  properties:\n" +
                "    enumField:\n" +
                "      type: string\n" +
                // here we expect to have no $ref to the original enum schema (i.e MyEnum) but instead a reference is generated
                "      enum:\n" +
                "      - A\n" +
                "      - B\n";

        // fails as in addition to the enum values also a reference to the original enum is added
        Map<String, io.swagger.v3.oas.models.media.Schema> stringSchemaMap = ModelConverters.getInstance(true).readAll(PojoUsingAllowableValues.class);
        SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml);
    }

    private static final class PojoUsingAllowableValues {
        @Schema(allowableValues = {"A", "B"})
        private MyEnum enumField;

        public MyEnum getEnumField() {
            return enumField;
        }

        public void setEnumField(MyEnum enumField) {
            this.enumField = enumField;
        }
    }

    private enum MyEnum {
        A, B, C;
    }

Investigation with proposed solution

Looking at the AnnotationsUtils.getSchemaFromAnnotation code (which is where information to @Schema annotations are reconciled with oas 3.1.0 models generated for properties, see ModelResolver#719), it looks like when allowableValues are processed (AnnotationsUtils#806), any information related to references should be removed. That would mean that the code would look something like the following:

if (schema.allowableValues().length > 0) {
        schemaObject.setEnum(Arrays.asList(schema.allowableValues()));
        // remove any existing reference as otherwise it would be conflicting with the enum entries
        schemaObject.set$ref(null);
}

Would this solution be valid or is there any scenario which I am missing where one would want both enum and $ref set while processing @Schema#allowableValues?