Detecting default server url
Is your feature request related to a problem?
We have different server urls: localhost, review apps, staging and production. Everytime one uses swagger ui, they have to pick correct server first. Currently default server url is the first configured, this can be confusing since swagger is sending request somewhere else, getting one to think something is wrong with the current url.
Describe the solution you'd like
It would be intuitive if server url was picked to match the browser url from where swagger ui is served (if it does match).
What about maintaining a list of links to different environments, e.g.
- Production: http://petstore.swagger.io/?url=http://www.example.com/swagger-api-docs.yaml
- Staging: http://petstore.swagger.io/?url=http://staging.example.com/swagger-api-docs.yaml
- Dev: http://petstore.swagger.io/?url=http://dev.example.com/swagger-api-docs.yaml
It's hard to implement that, since review apps post different url for each PR, changing heroku github bot to append the correct url is probably not possible - I doubt it allows configuration.
What's prevent swagger ui to do the right thing? The algorithm would be:
- go over list of servers, top to bottom
- for each item, check if it matches prefix of browser url
- if they match, pick as default and stop
- if none of the urls in list matches, pick the first server as default
Are you using a shared instance like http://swagger-ui.your-project.com? If you do so, you can customize it.
If I'm getting this right, you can write your own logic to place these lines: https://github.com/swagger-api/swagger-ui/blob/v3.17.4/dist/index.html#L42-L54.
I'm not following what configuration exactly should change this. I have:
const ui = SwaggerUIBundle({
url: "https://custom-app-pr-61.herokuapp.com/api/v1/openapi.yaml",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
But regardless of url, it still picks http://localhost as default.
So you have multiple servers for several environment in the same api document?
In such case, yes, the first server will be selected by default: https://github.com/swagger-api/swagger-ui/blob/v3.17.4/src/core/plugins/oas3/components/servers.jsx#L25
You could write a wrapComponent plugin to override this behavior. Refs:
- https://github.com/swagger-api/swagger-ui/blob/v3.17.4/docs/customization/plugin-api.md#wrap-components
- https://github.com/swagger-api/swagger-ui/blob/v3.17.4/docs/usage/configuration.md#plugin-system
Or alternatively, wrap the selector: https://github.com/swagger-api/swagger-ui/blob/v3.17.4/src/core/plugins/oas3/selectors.js#L20
Couldn't wrap my head around this wrapping business, so I have a rather dirty hack in place to preselect a certain server from the list by programmatically triggering the select box.
Would appreciate a option to pre-select a server via maybe by the URL or a user-defined function that receives the servers array and returns a index to select.
I know this is an old issue but I ran into a similar one where I have a list of servers and want to make sure the appropriate one is only showing up in each environment. This is my list of servers in the swagger.json.
"servers": [
{
"url": "http://localhost:3000",
"description": "Local server",
"env": "LOCAL"
},
{
"url": "https://my-dev.fmr.com",
"description": "DEV Environment",
"env": "DEV"
},
{
"url": "https://my-qa.fmr.com",
"description": "QA Environment",
"env": "QA"
},
{
"url": "https://my-preprod.fmr.com",
"description": "PREPROD Environment",
"env": "PREPROD"
}
]
in the index.html page, I added the follwing function:
const detectEnvironment = () => {
const host = window.location.host.toLowerCase();
if (host.indexOf("localhost") > -1) {
return "LOCAL";
} else if (host.indexOf("dev") > -1) {
return "DEV";
} else if (host.toLowerCase().indexOf("qa") > -1) {
return "QA";
} else if (host.toLowerCase().indexOf("preprod") > -1) {
return "PREPROD";
}
};
and I finally added the following code in the onComplete portion of the SwaggerUiBundle.
const ui = SwaggerUIBundle({
url: "./swagger.json",
dom_id: "#swagger-ui",
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
layout: "StandaloneLayout",
onComplete: function () {
const env = detectEnvironment();
let spec = ui.specSelectors.specJson().toJS();
let servers = spec.servers.filter((item) => {
return item.env.toLowerCase() === env.toLowerCase();
});
spec.servers = servers;
ui.specActions.updateJsonSpec(spec);
},
});
I know it is probably crude but it does the trick.
You could make Swagger's default "/" server URL work for you if you explicitly add it as the first server in the list. Here's what my servers' list looks like - does the trick, and not even ugly, in my opinion, even somewhat intuitive :) servers: - url: "/" description: "This realm"
- url: "http://my.dev.host:3000" description: "Dev"
- url: "http://my.qa.host:3000" description: "QA"
- url: "http://my.prod.host:3000" description: "PROD"
in my case I am using FastApi, and I have an openapi.json that has more than one server (different hosts) and each path correctly specifies its own correct server.
However as mentioned above the UI gives me as default server for all the paths the first server in the servers list.
Really a shame, as openapi.json contemplates this case and allows correctly to define different servers for different paths.
@detkin82 solved the problem by adding a 'This realm' server at '/'. When the API is mounted at different end-points, for example https://prod.example.org/services/apis/myapi or https://dev.example.org/myapi, the '/' trick does not work.
For me, it worked to use a path relative to the api-docs path (the path where the swagger-ui is mounted)
servers:
- url: ../
description: This realm
- url: http://localhost:3000
description: Development server
- url: https://example.org/myapi
description: Public server
I had the same issue with multiple Server Urls and I've juste made my own wrapper to select the good one :
window.onload = function() {
const AutoSelectServerPlugin = function() {
return {
statePlugins: {
spec: {
wrapSelectors: {
servers: (oriSelector) => (state, ...args) => {
const servers = oriSelector(state, ...args);
if (!servers || servers.size === 0) return servers;
// chercher le serveur voulu dans la List Immutable
const matchedIndex = servers.findIndex(s => s.get('url') === window.location.origin);
if (matchedIndex >= 0) {
// déplacer le serveur voulu en premier
const matched = servers.get(matchedIndex);
const rest = servers.delete(matchedIndex);
return rest.unshift(matched); // retourne une nouvelle List Immutable
}
return servers;
}
}
}
}
};
};
window.ui = SwaggerUIBundle({
url: "/openapi.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl,
AutoSelectServerPlugin
],
});
};