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

@Schema(accessMode) doesn't work for objects that are contained by other objects

Open Shryne opened this issue 4 years ago • 3 comments

From springdoc/springdoc-openapi#1080:

Describe the bug @Schema(accessMode = READ_ONLY) (or WRITE_ONLY) doesn't work, if it's used on an object that is contained by another object in the same class:

@Getter
@Setter
public class Person {
    // This works. Name will only be visable in GET requests
    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
    private String name;

    // This doesn't work. Age will be visable in GET and POST requests. WRITE_ONLY doesn't work here as well
    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
    private Age age;
    
    // The class Mother has an Age instance variable. This is the reason why @Schema doesn't work for Person.age
    private Mother mother;
}

To Reproduce I have a minimal project here: https://github.com/Shryne/SwaggerAccess. Just start SwaggerAccessApplication.main and visit http://localhost:8090/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config#/. It will show:

GET /person

200 Response:
{
  "name": "string",
  "age": {
    "value": 0
  },
  "mother": {
    "bid": {
      "value": 0
    }
  }
}
POST /person

Request Body (example Value)
{
  "age": {
    "value": 0
  },
  "mother": {
    "bid": {
      "value": 0
    }
  }
}

Expected behavior name and age are read only values. name isn't shown in the POST request, as expected. Unfortunately, this doesn't apply to age. POST should look like this:

{
  "mother": {
    "bid": {
      "value": 0
    }
  }
}

And using this code:

System.out.println(
    ModelConverters
        .getInstance()
        .resolveAsResolvedSchema(new AnnotatedType(Person.class))
        .referencedSchemas
);
I get this output:
{Age=class Schema {
    type: object
    format: null
    $ref: null
    description: null
    title: null
    multipleOf: null
    maximum: null
    exclusiveMaximum: null
    minimum: null
    exclusiveMinimum: null
    maxLength: null
    minLength: null
    pattern: null
    maxItems: null
    minItems: null
    uniqueItems: null
    maxProperties: null
    minProperties: null
    required: null
    not: null
    properties: {value=class IntegerSchema {
        class Schema {
            type: integer
            format: int32
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: null
            additionalProperties: null
            nullable: null
            readOnly: null
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }}
    additionalProperties: null
    nullable: null
    readOnly: null
    writeOnly: null
    example: null
    externalDocs: null
    deprecated: null
    discriminator: null
    xml: null
}, Mother=class Schema {
    type: object
    format: null
    $ref: null
    description: null
    title: null
    multipleOf: null
    maximum: null
    exclusiveMaximum: null
    minimum: null
    exclusiveMinimum: null
    maxLength: null
    minLength: null
    pattern: null
    maxItems: null
    minItems: null
    uniqueItems: null
    maxProperties: null
    minProperties: null
    required: null
    not: null
    properties: {bid=class Schema {
        type: null
        format: null
        $ref: #/components/schemas/Age
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }}
    additionalProperties: null
    nullable: null
    readOnly: null
    writeOnly: null
    example: null
    externalDocs: null
    deprecated: null
    discriminator: null
    xml: null
}, Person=class Schema {
    type: object
    format: null
    $ref: null
    description: null
    title: null
    multipleOf: null
    maximum: null
    exclusiveMaximum: null
    minimum: null
    exclusiveMinimum: null
    maxLength: null
    minLength: null
    pattern: null
    maxItems: null
    minItems: null
    uniqueItems: null
    maxProperties: null
    minProperties: null
    required: null
    not: null
    properties: {name=class StringSchema {
        class Schema {
            type: string
            format: null
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: null
            additionalProperties: null
            nullable: null
            readOnly: true
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }, age=class Schema {
        type: null
        format: null
        $ref: #/components/schemas/Age
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }, mother=class Schema {
        type: null
        format: null
        $ref: #/components/schemas/Mother
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }}
    additionalProperties: null
    nullable: null
    readOnly: null
    writeOnly: null
    example: null
    externalDocs: null
    deprecated: null
    discriminator: null
    xml: null
}}

Furthermore, if I use @Schema(accessMode = Schema.AccessMode.READ_ONLY) on the mother instance variable, it won't be displayed in POST as expected, but age stays as it is.

Shryne avatar Mar 01 '21 10:03 Shryne

This issue here has a reproducible example provided as a spring boot based repo: https://github.com/springdoc/springdoc-openapi/issues/1135

nkavian avatar Apr 10 '21 18:04 nkavian

Currently I am using this workaround:

class A {
  var b = ReadOnlyB()
  var otherB = B()
}

open class B {}

@Schema(
    accessMode = Schema.AccessMode.READ_ONLY,
    title = "B (read only)"
)
class ReadOnlyB : B()

I create additional classes for every class that I want to be read (or write) only. Since they are other classes, swagger-core doesn't overwrite their access mode. The disadvantages of this method are: We have additional classes that do nothing and the user will see additional, unnecessary, copied classes in the generated documentation.
A fix of this issue would be nice.

Shryne avatar Apr 13 '21 11:04 Shryne

I have the same question, curious about what hasn't been fixed

imborntowin avatar Jun 01 '23 07:06 imborntowin