Search code examples
openapiswagger-ui

changing the route list depending on the server port (swagger)


I would like to share routes that are already linked via tags via the "selected server".

{
  "openapi": "3.1.0",
  "info": {
    "title": "blue_traktor_api",
    "description": "This API handles various functionalities for USEPI, Icons, Dashboard, Settings, Patterns, Devices, Widgets, and more.",
    "version": "0.1.0",
    "contact": {
      "name": "API Support",
      "url": "",
      "email": ""
    }
  },
  "servers": [
    {
      "url": "http://localhost:8000",
      "description": "TCP server for main operations"
    },
    {
      "url": "ws://localhost:8080",
      "description": "WebSocket server for updated parameters"
    }
  ],
  "paths": {...},
  "components":{...}
}

here is the first part of my openal file.json where I list servers. it will look like this: enter image description here

enter image description here

and I also have several routes for example:

  "paths": {
    "/api/usepi/patterns/create": {
      "post": {
        "tags": ["patterns"],
        "summary": "Create a new pattern",
        "operationId": "createPattern",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RequestStructure_PatternRequest"
              },
              "examples": {
                "PatternExample": {
                  "summary": "An example of a PatternRequest",
                  "value": {
                    "data": {
                      "id": null,
                      "title": "test",
                      "description": "test desc",
                      "icon": "http://localhost:8000/api/icons/test.svg",
                      "createDate": null,
                      "updateDate": null,
                      "favourite": false,
                      "data": {
                        "ID": {},
                        "QR-Code": {},
                        "Position": {},
                        "DeviceParams": {}
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pattern created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoodResponse_Id"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }, ...
}

and there are still such routes, but with a different tag:

"/api/widgets/get": {
      "post": {
        "tags": ["widgets"],
        "summary": "Get widget details",
        "operationId": "getWidget",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RequestStructure_Id"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Widget details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoodResponse_GetWidgetResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },

I would like that when selecting a server, only the routes associated with it are shown. for example, if http://localhost:8000 then /api/usepi/patterns/create ...

where I need to, I also got rid of the try it button and the parameter block in this way:

<script>
window.onload = function() {
    const ui = SwaggerUIBundle({
      url: "http://localhost:8000/openai.json",
      dom_id: '#swagger-ui',
      deepLinking: true,
      presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIStandalonePreset
      ],
      plugins: [
        SwaggerUIBundle.plugins.DownloadUrl
      ],
      layout: "StandaloneLayout"
    });

    window.ui = ui;

    function hideTryItOutButtons() {
      const pathsToDisable = [

        '#operations-patterns-createPattern > div.no-margin > div > div.opblock-section > div.opblock-section-header > div.try-out > button',
        '#operations-patterns-createPattern > div.no-margin > div > div.opblock-section > div.opblock-section-header',
        '#operations-patterns-createPattern > div.no-margin > div > div.opblock-section > div.parameters-container', ];

      pathsToDisable.forEach(selector => {
        const button = document.querySelector(selector);
        if (button) {
          button.style.display = 'none';
        }
      });
    }
    const observer = new MutationObserver(hideTryItOutButtons);
    observer.observe(document.getElementById('swagger-ui'), { childList: true, subtree: true });

    hideTryItOutButtons();
  };
</script>

This is what it looks like:

enter image description here

how can I make the display of only the necessary routes (like filter), if (as far as I remember) you can't do subtags for tags? show routes when selecting a server, also grouping by tags*


Solution

  • I solved this problem so that I didn't have to work on the error where, when hiding the list and switching to another option from the list of servers, the element disappears altogether.

    "paths": {
        "/api/usepi/patterns/create": {
          "post": {
            "tags": ["patterns"],
            "summary": "http://localhost:8000",
            "operationId": "createPattern",
            "requestBody": {
              "required": true,
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/RequestStructure_PatternRequest"
                  },
                  "examples": {
                    "PatternExample": {
                      "summary": "An example of a PatternRequest",
                      "value": {
                        "data": {
                          "id": null,
                          "title": "test",
                          "description": "test desc",
                          "icon": "http://localhost:8000/api/icons/Cтатическое оборудование/Статическое оборудование.svg",
                          "createDate": null,
                          "updateDate": null,
                          "favourite": false,
                          "data": {
                            "ID": {},
                            "QR-Code": {},
                            "Position": {},
                            "DeviceParams": {}
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "responses": {
              "200": {
                "description": "Pattern created.",
                "content": {
                  "application/json": {
                    "schema": {
                      "$ref": "#/components/schemas/GoodResponse_Id"
                    }
                  }
                }
              },
              "500": {
                "description": "Internal Server Error",
                "content": {
                  "application/json": {
                    "schema": {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  }
                }
              }
            }
          }
        },
    

    in the "summary" I write down a variant from the list to determine which element of the list the route belongs to. Next, I look for matches in the list when drawing and delete what does not match the selected list value:

    enter image description here

    here is how it is implemented:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Swagger UI</title>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.10.3/swagger-ui.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
      <style>
        body { background-color: #ffffff; font-family: Arial, sans-serif; }
      </style>
    </head>
    <body>
    <div id="swagger-ui"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.10.3/swagger-ui-bundle.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.10.3/swagger-ui-standalone-preset.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
      window.onload = function() {
        const ui = SwaggerUIBundle({
          url: "http://localhost:8000/openai.json",
          dom_id: '#swagger-ui',
          deepLinking: true,
          presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIStandalonePreset
          ],
          plugins: [
            SwaggerUIBundle.plugins.DownloadUrl
          ],
          layout: "StandaloneLayout"
        });
        window.ui = ui;
    
        let isFiltering = false;
    
        function filterElements() {
          const selectElement = document.querySelector("#swagger-ui>section>div.swagger-ui>div:nth-child(2)>div.scheme-container>section>div>div>div>label>select");
          if (selectElement) {
            const selectedValue = selectElement.options[selectElement.selectedIndex].textContent.split(' - ')[0];
            console.log("Selected value from dropdown:", selectedValue);
    
            const parentCatalogs = document.querySelectorAll("#swagger-ui>section>div.swagger-ui>div:nth-child(2)>div:nth-child(4)>section>div>span> div > h3");
            console.log("Parent catalogs found:", parentCatalogs.length);
    
            parentCatalogs.forEach(parent => {
              const parentElement = parent.closest('div.opblock-tag-section');
              if (parentElement) {
                let childVisible = false;
                const childElements = parentElement.querySelectorAll("[id^='operations-']:not([id^='operations-tag'])");
                childElements.forEach(childElement => {
                  const summaryControl = childElement.querySelector("button.opblock-summary-control > div > div");
                  if (summaryControl) {
                    const summaryText = summaryControl.textContent.trim();
                    if (summaryText === selectedValue) {
                      childElement.style.display = '';
                      childVisible = true;
                      console.log(`---/Child Element ID: ${childElement.id} = ${summaryText}`);
                    } else {
                      childElement.style.display = 'none';
                      console.log(`Hiding element with Child Element ID: ${childElement.id} as it does not match the selected value`);
                    }
                  } else {
                    console.log(`Summary control not found for child element: ${childElement.id}`);
                  }
    
                  const elementsToRemove = childElement.querySelectorAll('.opblock-summary-description');
                  elementsToRemove.forEach(element => {
                    element.style.display = "none";
                  });
    
                  if (childElement.querySelectorAll('.no-margin').length > 0) {
                    const opblock = childElement
    
                    setTimeout(() => {
                      const elementToClear = opblock.querySelectorAll('.opblock-summary-path')
                      elementToClear.forEach(el => {
                        const dataPath = el.getAttribute('data-path').split('/').at(-1)
                        try { opblock.querySelectorAll('.opblock-description-wrapper')[0].remove() } catch {}
                        if (dataPath !== 'list' && dataPath !== 'icons' && dataPath !== '{path}') {
                          opblock.querySelectorAll('.opblock-section-header')[0].remove()
                        }
                      })
                    }, 30)
                  }
                });
    
                if (isFiltering) {
                  if (!childVisible) {
                    parentElement.style.display = 'none';
                    console.log(`Hiding parent element as all child elements are hidden: ${parent.textContent}`);
                  } else {
                    parentElement.style.display = '';
                  }
                }
              } else {
                console.log("Parent element not found for:", parent.textContent);
              }
            });
          } else {
            console.log("Select element not found.");
          }
        }
    
        document.addEventListener('change', function(event) {
          const selectElement = document.querySelector("#swagger-ui > section > div.swagger-ui > div:nth-child(2) > div.scheme-container > section > div > div > div > label > select");
          if (event.target === selectElement) {
            isFiltering = true;
            filterElements();
            isFiltering = false;
          }
        });
    
        document.addEventListener('click', () => {
            filterElements();
        });
        const observer = new MutationObserver(() => {
          const selectElement = document.querySelector("#swagger-ui > section > div.swagger-ui > div:nth-child(2) > div.scheme-container > section > div > div > div > label > select");
          if (selectElement) {
            isFiltering = true;
            filterElements();
            isFiltering = false;
            observer.disconnect();
          }
        });
    
        observer.observe(document.querySelector('#swagger-ui'), { childList: true, subtree: true });
      };
    </script>
    </body>
    </html>
    
    

    next question: The collapse is not displayed when the selected element is changed