Dynamic change servers url - swagger-ui

I have web app that i deploy for multiple clients (lets say client1.com, client2.com and client3.com). I wrote REST API documentation using OA3 specification.
I have index.yaml which looks like:
openapi: 3.0.0
info:
title: REST API
description: REST API
version: 0.0.1
servers:
- url: http://client1.com/api
description: Something ...
tags: ...
and standard Swagger-ui index.html
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "docs/index.yaml",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset.slice(1)
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
// End Swagger UI call region
window.ui = ui;
My problem is that i need to change servers.url depending on where and for which client i deploy app, so that each client can test API on his own servers and i don't want clients to see each other and know about each other in REST API docs.
How can i "dynamically" change / set server.url ? One "walkaround" solution is to copy index.yaml for each client and hardcode servers.url, but im sure there is better way which i don't see / know about.
Edit #1: given duplicate answer does not help because when i set servers to
servers:
- url: /api
swagger ui still points to http://localhost:8080/api/ but my app url is http://localhost:8080/myapp/. I could set servers: - url: /myapp/api but this is hardcoded value in index.yaml and that does not work for me. I need like "configurable" server url.
But thanks anyway
Edit #2: my current walkaround solution is to process yaml with server side code. In index.yaml i have:
servers:
- url: ##INSERT_SERVERS_TAG_HEREapi
and i replace ##INSERT_SERVERS_TAG_HERE with myapp.client.com/. But i'm still looking for some better solution.

Related

Using rails asset in javascript

I'm using rails to build a site, and I want to embed a swagger doc in one of the pages. The swagger yaml file is stored in /app/assets/myfile.yaml. In the swagger embed code (javascript) I've tried a variety of approaches:
// in myswagger.html.erb
window.onload = function() {
const ui = SwaggerUIBundle({
url: "<%= asset_path('myfile.yaml') %>",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
})
}
I've also tried a bare path to /app/assets/myfile.yaml, document_path('myfile.yaml'), etc. But every time we end up with Fetch error Not Found /swagger.yaml.
What's the proper way to access this file and get it embedded in the javascript?
fast answer:
put the file yaml in your public folder
otherwise check what is generated at the browser level, by inspecting the page

Workbox offline mode works only on root path

Im working on my PWA application.
So I have one problem that I can't find any info how to fix.
I use workbox with webpack InjectManifest ( but also tried webpack offline-plugin ).
When I access my webpage at the root and go offline, I can see it's working perfectly. But when I change route to '/authorize' or basically any other route and go offline, it doesn't work.
Is there any requirement that it will work only in case that we are on root path? I can't find anything about it except for this: https://github.com/quasarframework/quasar-cli/issues/131
Ok found it.
So basically it all comes to routing.
https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
https://developers.google.com/web/tools/workbox/modules/workbox-strategies
In my case, I wanted to always serve content as for SPA so I had to add
workbox.routing.registerNavigationRoute('/index.html'); to my workbox config.
At the end it looks like this:
1) Webpack plugin:
const commonPlugins = [
new workboxPlugin.InjectManifest({
swSrc: './src/workbox-sw.js',
swDest: 'workbox-sw.js',
}),
];
2) Content of workbox-sw
/* globals workbox, self */
workbox.setConfig({
debug: true
});
workbox.core.setCacheNameDetails({
prefix: 'sneak-client',
suffix: 'v1',
precache: 'sneak-precache',
runtime: 'sneak-runtime-cache',
});
workbox.routing.registerNavigationRoute('/index.html');
workbox.precaching.precacheAndRoute(self.__precacheManifest);

What is the difference between SwaggerUIBundle, and SwaggerUi

I've seen both in samples I've found and haven't seen how they are different. Is the bundle needed if you are using this in an HTML page only (not using a single-page-app) or is that the one to use if you are using a single-page-app?
The Swagger UI docs discuss two ways to deploy swagger-ui.
traditional npm - swagger-ui
dependency-free module - swagger-ui-dist
I've seen examples like this one where SwaggerUIBundle is used on what appears to be a web page hosted in tomcat (python, or some other web server) example.
<script src="./swagger-ui-bundle.js"> </script>
// later
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
But also seen examples like this one that use SwaggerUi.
window.swaggerUi = new SwaggerUi({
url: "http://petstore.swagger.wordnik.com/api/api-docs",
dom_id: "swagger-ui-container",
A search returns things like:
swagger-ui-bundle.js - https://github.com/swagger-api/swagger-ui/issues/3978
SwaggerUi - https://stackoverflow.com/a/29497301/3281336
SwaggerUIBundle - https://github.com/swagger-api/swagger-ui/wiki/FAQ
This page Installation Distribution Channels NPM Registry says:
SwaggerUIBundle is equivalent to SwaggerUI.
But then explains the differences. So they are functionally equivalent but the one you choose will depend on how your webserver/website is serving up the swagger user interface page.
The first example with const ui = SwaggerUIBundle(... is for Swagger UI 3.x, which is the current version of Swagger UI. The second example with window.swaggerUi = new SwaggerUi(... is for the old Swagger UI 2.x. Credit #Helen for this info in this answer)
For more details read on...
SwaggerUI Explained
SwaggerUI is used in apps that can import npm modules. This includes React, Angular or other single-page-apps (SPAs) that include the webpack-like tooling to package the resources for delivery to the browser.
The webpage says this:
import SwaggerUI from 'swagger-ui'
swagger-ui is meant for consumption by JavaScript web projects that include module bundlers, such as Webpack, Browserify, and Rollup.
Here's an example of using the npm intalled module swagger-ui.
import SwaggerUI from 'swagger-ui'
// or use require, if you prefer
const SwaggerUI = require('swagger-ui')
SwaggerUI({
dom_id: '#myDomId'
})
SwaggerUIBundle Explained
SwaggerUIBundle is used when your app does not support importing npm modules (e.g., a java webapp).
The swagger user interface can be loaded by using the swagger index.html page (included in the swagger-ui-bundle) or by your own personal html page that includes the bundle file and uses the Javascript shown below:
The following comes from the website and is edited to highlight my above statements:
The [...] dist folder [has] swagger-ui-bundle.js, which is a build of Swagger-UI that includes all the code it needs to run in one file. The folder also has an index.html asset, to make it easy to serve Swagger-UI...
An example of how to use the SwaggerUIBundle is:
var SwaggerUIBundle = require('swagger-ui-dist').SwaggerUIBundle
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "StandaloneLayout"
})
The SwaggerUIBundle example is confusing
It is confusing because it says:
if you're in a JavaScript project that can't handle a traditional npm module, you could do something like this:
var SwaggerUIBundle = require('swagger-ui-dist').SwaggerUIBundle
which uses require() which is an npm module way of including the bundle.
A less confusing way to explain this would be to say:
If you are using Swagger in a non-module environment then you need to somehow get the swagger bundle javascript loaded into the browser page and then use the SwaggerUIBundle as shown below to make the swagger user interface render at the dom_id specified (in the example below it is swagger-ui).
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "StandaloneLayout"
})
The way you load the swagger-ui-bundle onto your page will depend on the technologies you are using. If you want you can load the page using a <script src="bundle.js"></script>. See https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html (which is in swagger-ui/dist/index.html).
If you are in a NodeJS express application you could load the swagger bundle onto the page using:
var SwaggerUIBundle = require('swagger-ui-dist').SwaggerUIBundle
How you get the swagger bundle javscript onto the page is up to you.
The first example with const ui = SwaggerUIBundle(... is for Swagger UI 3.x, which is the current version of Swagger UI.
The second example with window.swaggerUi = new SwaggerUi(... is for the old Swagger UI 2.x.
See here for the differences between 3.x and 2.x.

Can't read from file issue in Swagger UI

I have incorporated swagger-ui in my application.
When I try and see the swagger-ui I get the documentation of the API nicely but after some time it shows some error icon at the button.
The Error message is like below:
[{"level":"error","message":"Can't read from file
http://MYIP/swagger/docs/v1"}]
I am not sure what is causing it. If I refresh it works and shows error after few seconds.
I am guessing "http://MYIP/swagger/docs/v1" is not publicly accessible.
By default swagger ui uses an online validator: online.swagger.io. If it cannot access your swagger url then you will see that error message.
Possible solutions:
Disable validation:
config.EnableSwagger().EnableSwaggerUi(c => c.DisableValidator());
Make your site publicly accessible
Host the validator locally:
You can get the validator from: https://github.com/swagger-api/validator-badge#running-locally
You will also need to tell swaggerui the location of the validator
config.EnableSwagger().EnableSwaggerUi(c => c.SetValidatorUrl(<validator_url>));
To supplement the accepted answer...I just uncommented one line in the SwaggerConfig.cs. I only wanted to get rid of the red error on the main swagger page by disabling the validator.
// By default, swagger-ui will validate specs against swagger.io's online validator and display the result
// in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the
// feature entirely.
//c.SetValidatorUrl("http://localhost/validator");
c.DisableValidator();
If you are using files from swagger-ui github repo, then you can disable schema validation from your index.html file by setting validatorUrl to null in it:
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "/docs/open_api.json",
dom_id: '#swagger-ui',
validatorUrl : null, # <----- Add this line
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
If you using PHP Laravel framework with L5-Swagger just uncomment
'validatorUrl' => null,
from the config file /config/l5-swagger.php
Setting this.model.validatorUrl = null; in dist/swagger-ui.js worked for me ..
// Default validator
if(window.location.protocol === 'https:') {
//this.model.validatorUrl = 'https://online.swagger.io/validator';
this.model.validatorUrl = null;
} else {
//this.model.validatorUrl = 'http://online.swagger.io/validator';
this.model.validatorUrl = null;
}
To anynoe having similar issue when using Swashbuckle.OData:
I was having issues to integrated Swagger with our OData endpoints (using ODataController for API and Swashbuckle.OData NuGet package). I had to write our own document filter for it and add it:
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "OurSolution.API");
c.DocumentFilter<SwaggerDocumentFilter>();
//c.CustomProvider((defaultProvider) => new ODataSwaggerProvider(defaultProvider, c, GlobalConfiguration.Configuration));
c.IncludeXmlComments(GetXmlCommentsPath());
c.UseFullTypeNameInSchemaIds();
c.RootUrl(req => ConfigurationManager.AppSettings["AppUrl"]);
})
.EnableSwaggerUi(c =>
{
c.DisableValidator();
});
Apparently in order to avoid validation error I had to comment out line which is setting ODataSwaggerProvider along with turning off validator as mentioned in posts above. This makes usefulness of Swashbuckle.OData questionable yet I didn't test whatever it works with vanilla Swashbuckle.
Note: I used approach described on GitHub page for Swashbuckle.OData but it was not working: showing no possible endpoints at all. Maybe somebody knows better solution.

best way to tell swaggerui where the host is

When I build my swagger.json file I do not know which host to use. However I can work it out when my page that hosts swaggerui loads (in fact I might want to offer the user a choice). I hoped to see an options.host on the config for the swaggerUI object - I dont see one. Is there an existing way of doing this that I cant find or do I simply have to hack my way through the code and add this capability (pointers to the best place to do it would be welcome)
Swagger has a built-in json definition for host config, or can accept multiple inputs.
{
"swagger": "2.0",
"info": {
"title": "Why API",
"description": "Don't make that mistake again",
"version": "0.0.1"
},
"host": "127.0.0.1:3000",
"schemes": [
"https"
]
}
Or
"host": "test.mydomain.com:3000",
"schemes": [
"https"
],
Or you can have a dynamic host by defining a var and calling a hostname or machine name or other environment variables.
dynamic example
if (typeof this.host === 'undefined' || this.host === '') {
this.host = location.host;
}
if (location.port) {
this.host = this.host + ':' + location.port;
}
Here is what I do, since the loaded in document is just a JSON object:
var swaggerDoc = require('./api/swagger.json');
if (process.env.NODE_ENV === 'development') {
swaggerDoc.host="localhost:" + process.env.PORT
}
// Initialize the Swagger middleware
swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {
// Other initialization
}
This way you don't pollute your API specification with development environment configuration.
In recent versions of Swagger UI it's possible to do this, for example in onComplete:
window.swaggerUi.api.setHost("your.host:4242");
If you are hosting it on same app server, just remove the host key from the json and provide relative path in key "basePath". as -
"basePath": "/rest/createcampaign".
two ways
One modify swagger.js so that it accepts host option. swagger-UI passes options to swagger-js so that works. I submitted a pull to swagger-js with this fix
Second choice is that swagger-UI accepts a 'spec' parameter. This means that the hosting page can load the swagger.json file, JSON.parse it , set 'host' in it and then pass to swaggerUi constructor. This is harder for the caller but doesn't require code changes to swagger
There are 2 ways which you can follow:
Load the index.html and replace the https://petstore.swagger.io/v2/swagger.json with the url where your swagger.json is hosting.
you can expose the local swagger.json on the same server.
When you follow this approach make sure you include static files in the end of above steps.
If you don't want to expose swagger.json as an API, copy the sawgger.json in the dist folder of swagger. The index.html and swagger.json must be in same repository for this. It is inside the index.html of dist folder of swagger-ui-dist.
const ui = SwaggerUIBundle({
spec: location.host,
url: "swagger.json",
dom_id: "#swagger-ui",
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
layout: "StandaloneLayout"
});
// End Swagger UI call region
window.ui = ui;
};
Second way, host parameter in the swagger.yaml/swagger.json either make it empty
"host":""
or omit host parameter.
Swagger take the server's host as host where the swagger ui is hosted.
This is how I did this using the Java client:
DefaultApi api = new DefaultApi();
api.getApiClient().setBasePath("http://localhost:8080");
//call the API
if you use OpenApi 3.0
Variables can have arbitrary values, or may be restricted to an enum. In any case, a default value is required, which will be used if the client does not supply a value.
swagger doc
In the swagger-ui there will be the default value but the field is an input field so it is possible to customize it at runtime.
Swagger UI express itself is giving the following snippet it's getting the current host and publish dynamic with host
app.use('/api-docs', function(req, res, next){
swaggerDocument.host = req.get('host');
req.swaggerDoc = swaggerDocument;
next();
}, swaggerUi.serve, swaggerUi.setup());

Resources