stoplightio / elements

Build beautiful, interactive API Docs with embeddable React or Web Components, powered by OpenAPI and Markdown.
https://stoplight.io/open-source/elements/
Apache License 2.0
1.74k stars 201 forks source link

$refs with relative paths are not properly resolved with async spec loading #2578

Open anikitin opened 5 months ago

anikitin commented 5 months ago

Context

Our OpenAPI files contain references to common schemas defined in separate YaML files

Current Behavior

If I have "$ref" in OpenAPI document with a relative path, it is not properly resolved.

It is actually resolved based on current HTML page address instead of OpenAPI document (that contains this $ref) address. Browser console shows HTTP 404.

Expected Behavior

$ref with relative paths should be properly resolved based on rendered OpenAPI document address (ReDoc and Swagger UI do it properly)

Environment

I used the version from CDN https://unpkg.com/@stoplight/elements/web-components.min.js

brendarearden commented 5 months ago

@anikitin please provide an example of the openAPI document that contains the relative ref, along with the file that it is referencing so that we can reproduce this issue. Could you also provide an example of the html you are using.

anikitin commented 5 months ago

Sure. After experiments it seems that it only affects async loading, not the static way of specifying "apiDescriptionUrl".

This is the example of OpenAPI file I want to render: https://raw.githubusercontent.com/anikitin/static-assets/main/oas-refs/test1-openapi.yml

This is my HTML:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
</head>

<body>

    <elements-api id="docs" router="hash" layout="sidebar" hideTryIt="true" hideExport="true" hideSchemas="true">
    </elements-api>

    <script>
        (async () => {
            const docs = document.getElementById('docs');
            const text = await fetch('https://raw.githubusercontent.com/anikitin/static-assets/main/oas-refs/test1-openapi.yml').then(res => res.text())
            docs.apiDescriptionDocument = text;
        })();
    </script>

</body>
</html>

This is what I see in browser:

image

brendarearden commented 4 months ago

Thank you so much for providing that additional information!

Our suspicion is that the way you are getting the document is causing the issue

            const text = await fetch('https://raw.githubusercontent.com/anikitin/static-assets/main/oas-refs/test1-openapi.yml').then(res => res.text())
            docs.apiDescriptionDocument = text;

Once the test1-openapi.yml file is returned, elements has no context of how to resolve

schema:
     $ref: "test2-openapi.yml#/components/schemas/Schema2"

or

Schema1RefTo3:
     $ref: "test3-openapi.yml#/components/schemas/Schema3"

Potential workarounds:

anikitin commented 4 months ago

@brendarearden , workarounds are clear but unfortunately not possible for us. I still think this is a bug because:

1) When exactly the same URL is processed via static URL it works properly 2) Other libraries like ReDoc can handle it.

I understand that it can be related to async implementation with "fetch" method. This example was taken from Stoplight documentation. But then, to resolve this issue, there should be some another way to feed an OpenAPI document at runtime that preserves the path and allows handling relative links.

I wonder how Stoplight demo app handles this with dynamically loaded document: https://elements-demo.stoplight.io/

anikitin commented 4 months ago

OK, I have figured out that I can just set apiDescriptionUrl programmatically in JS instead of doing async loading.

So this issue can be closed, but I recommend adding an example to https://docs.stoplight.io/docs/elements/a71d7fcfefcd6-elements-in-html that demonstrates how to set apiDescriptionUrl in JavaScript. It is very typical scenario where the URL is selected dynamically, and it is important to load it properly with all external $refs. Current provided examples of dynamic loading don't support it. properly.

github-actions[bot] commented 3 months ago

This ticket has been labeled jira. A tracking ticket in Stoplight's Jira (STOP-618) has been created.