elements
elements copied to clipboard
$refs with relative paths are not properly resolved with async spec loading
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
@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.
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:
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:
- Specify the full path in your refs: example -
$ref: "https://raw.githubusercontent.com/anikitin/static-assets/main/oas-refs/test3-openapi.yml"
- use an openAPI bundler to preprocess the document so when you get the text, it has all of the references already bundled into it
@brendarearden , workarounds are clear but unfortunately not possible for us. I still think this is a bug because:
- When exactly the same URL is processed via static URL it works properly
- 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/
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.
This ticket has been labeled jira. A tracking ticket in Stoplight's Jira (STOP-618
) has been created.