swagger-api / swagger-api/swagger-ui

readOnly schema attribute rendering in request/response bodies depends on expansion order

Open
#11,010 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
29k
Forks
9.3k
Avg merge
2d 23h
Merged PRs (30d)
25

Description

Q&A
  • OS: Windows
  • Browser: Chrome
  • Version: 151
  • Method of installation: npm
  • Swagger-UI version: 5.32.13
  • Swagger/OpenAPI version: OpenAPI 3.1.0
Content & configuration

Read-only (readOnly: true) property visibility in Swagger UI is not computed per operation/context. Instead it behaves like a single shared/global state that is set by the order in which operations are expanded, and it applies to every operation — even operations that reference different component schemas.

Example Swagger/OpenAPI definition:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Read-only repro",
    "version": "1.0.0",
    "description": "Minimal spec to reproduce Swagger UI read-only rendering. A single shared schema (Widget) with one readOnly property (id) and one regular property (name) is referenced by a POST request body and a GET response. Expected: the POST body hides `id` (read-only), the GET response shows it."
  },
  "paths": {
    "/widgets": {
      "post": {
        "summary": "Create widget (request body — read-only `id` should be HIDDEN)",
        "operationId": "createWidget",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Widget" }
            }
          }
        },
        "responses": {
          "201": { "description": "Created" }
        }
      }
    },
    "/widgets/{widgetId}": {
      "get": {
        "summary": "Get widget (response — read-only `id` should be SHOWN)",
        "operationId": "getWidget",
        "parameters": [
          {
            "name": "widgetId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Widget" }
              }
            }
          }
        }
      }
    },
    "/gadgets": {
      "post": {
        "summary": "Create gadget (separate schema — read-only `serial` should be HIDDEN)",
        "operationId": "createGadget",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Gadget" }
            }
          }
        },
        "responses": {
          "201": { "description": "Created" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Widget": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Regular writable attribute."
          },
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Read-only attribute — expected HIDDEN in POST body, SHOWN in GET response."
          }
        }
      },
      "Gadget": {
        "type": "object",
        "additionalProperties": false,
        "required": ["label"],
        "properties": {
          "label": {
            "type": "string",
            "description": "Regular writable attribute."
          },
          "serial": {
            "type": "string",
            "readOnly": true,
            "description": "Read-only attribute on a separate schema — expected HIDDEN in the POST body."
          }
        }
      }
    }
  }
}

Swagger-UI configuration options:

SwaggerUI({
  dom_id: '#swagger-ui-container',
  url: '<url to spec endpoint>',
})
Describe the bug you're encountering

Swagger UI 5.32.13 (OpenAPI 3.1) computes readOnly visibility as a single global state instead of per operation. The first operation expanded after page load sets read-only visibility for every operation:

  • Expand a GET (response) first → readOnly fields are shown everywhere, including POST request bodies (wrong).
  • Expand a POST (request) first → readOnly fields are hidden everywhere, including GET responses (wrong).

This leaks across unrelated schemas too: expanding POST /gadgets (schema Gadget) first, then GET /widgets (schema Widget), removes the read-only field from the GET response even though they share no schema.

To reproduce...
  1. Load the spec in Swagger UI. All operations collapsed by default.
  2. Case A:
    i. expand GET /widgets/{widgetId} first,
    ii. then expand POST /widgets and POST /gadgets. → id and serial appear in the POST request bodies (should be hidden).
  3. Reload. Case B:
    i. expand POST /widgets first,
    ii. then expand GET /widgets/{widgetId}. → id is missing from the GET response model (should be shown).
  4. Cross-schema:
    i. expand POST /gadgets (schema Gadget) first
    ii. then expand GET /widgets/{widgetId} (schema Widget). → the read-only id is removed from the GET response, even though the two operations use different schemas.
Expected behavior

Read-only visibility must be computed independently per rendered model, from that operation's own context:

  • Request body models → hide readOnly properties.
  • Response models → show readOnly properties.
  • The result must not depend on expansion order, nor on other operations, nor on other schemas.
Screenshots

Expanding GET first:
Image
Then POST with GET already expanded:
Image
The readOnly attribute is shown in both.

Expanding POST first:
Image
Then GET with POST already expanded:
Image
The readOnly attribute is hidden in both.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Load the supplied OpenAPI 3.1 definition in Swagger UI and reproduce both expansion orders, including the cross-schema case. Trace the model-rendering path responsible for readOnly visibility and verify that request bodies hide those properties while responses show them independently; done means the result no longer changes with expansion order or other operations.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
documentation, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.