Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Swagger UI (#6546) #6719

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ _period: 14 days
- [ ] … update to [GitLab](https://hub.docker.com/r/gitlab/gitlab-ce/tags) & [GitLab runner images](https://hub.docker.com/r/gitlab/gitlab-runner/tags) <sub>or no update available</sub>
- [ ] … update to [ClamAV image](https://hub.docker.com/r/clamav/clamav/tags) <sub>or no update available</sub>
- [ ] … update to [GitLab AMI](https://github.com/DataBiosphere/azul/blob/develop/OPERATOR.rst#updating-the-ami-for-gitlab-instances) <sub>or no update available</sub>
- [ ] … update to [Swagger UI](https://github.com/DataBiosphere/azul/blob/develop/OPERATOR.rst#updating-swagger-ui) <sub>or no update available</sub>
- [ ] Created tickets for any deferred updates to …
- [ ] … to next major or minor Python version <sub>or such ticket already exists</sub>
- [ ] … to next major Docker version <sub>or such ticket already exists</sub>
Expand Down
24 changes: 24 additions & 0 deletions OPERATOR.rst
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,30 @@ SSH into the instance, and run ``sudo yum update`` followed by ``sudo reboot``.
Wait for the GitLab web application to become available again and perform a
``git fetch`` from one of the Git repositories hosted on that instance.

Updating Swagger UI
^^^^^^^^^^^^^^^^^^^

Operators should regularly check for available updates to the Swagger UI. The
current version used by Azul is hardcoded in ``scripts/update_swagger.py``. The
upstream source is located here:

https://github.com/swagger-api/swagger-ui/tree/master/dist

Scheduled upgrade PR's should only include minor and hotfix updates to the
Swagger UI. If a new major version is available, open a new issue instead. To
perform the update, edit the ``tag`` variable in the ``update_swagger`` script
and run it. If there are nontrivial changes to the ``swagger-initializer.js``
file, cancel the update and open a new issue; otherwise, forward any changes to
that file to ``swagger-initializer.js.template.mustache``. Then commit the
changes to the script and any modified files in the ``swagger/`` directory. The
commit message must include the new tag, as well as a link to the upstream
source in the commit body, e.g.::

Update Swagger UI to v<release version> (#issue-number)

https://github.com/swagger-api/swagger-ui/tree/v<release version>/dist


Export AWS Inspector findings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
25 changes: 25 additions & 0 deletions lambdas/indexer/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@
},
"paths": {
"/": {
"get": {
"summary": "A redirect to the Swagger UI for interactive use of this REST API",
"tags": [
"Auxiliary"
],
"responses": {
"301": {
"description": "A redirect to the Swagger UI"
},
"504": {
"description": "\nRequest timed out. When handling this response, clients\nshould wait the number of seconds specified in the\n`Retry-After` header and then retry the request.\n"
}
}
}
},
"/static/index.html": {
"get": {
"summary": "A Swagger UI for interactive use of this REST API",
"tags": [
Expand All @@ -22,6 +38,15 @@
}
}
},
"/static/swagger-initializer.js": {
"get": {
"responses": {
"504": {
"description": "\nRequest timed out. When handling this response, clients\nshould wait the number of seconds specified in the\n`Retry-After` header and then retry the request.\n"
}
}
}
},
"/openapi": {
"get": {
"summary": "Return OpenAPI specifications for this REST API",
Expand Down
7 changes: 2 additions & 5 deletions lambdas/service/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,7 @@ def manifest_url(self,
}
)
def oauth2_redirect():
oauth2_redirect_html = app.load_static_resource('swagger', 'oauth2-redirect.html')
return Response(status_code=200,
headers={"Content-Type": "text/html"},
body=oauth2_redirect_html)
return app.swagger_resource('oauth2-redirect.html')


def validate_repository_search(entity_type: EntityType,
Expand Down Expand Up @@ -985,7 +982,7 @@ def repository_search_spec(*, post: bool):
return {
'summary': fd(f'''
Search an index for entities of interest
{", with filters provided in the request body" if post else ""}.
{', with filters provided in the request body' if post else ''}.
'''),
'deprecated': post,
'description':
Expand Down
25 changes: 25 additions & 0 deletions lambdas/service/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@
],
"paths": {
"/": {
"get": {
"summary": "A redirect to the Swagger UI for interactive use of this REST API",
"tags": [
"Auxiliary"
],
"responses": {
"301": {
"description": "A redirect to the Swagger UI"
},
"504": {
"description": "\nRequest timed out. When handling this response, clients\nshould wait the number of seconds specified in the\n`Retry-After` header and then retry the request.\n"
}
}
}
},
"/static/index.html": {
"get": {
"summary": "A Swagger UI for interactive use of this REST API",
"tags": [
Expand All @@ -44,6 +60,15 @@
}
}
},
"/static/swagger-initializer.js": {
"get": {
"responses": {
"504": {
"description": "\nRequest timed out. When handling this response, clients\nshould wait the number of seconds specified in the\n`Retry-After` header and then retry the request.\n"
}
}
}
},
"/openapi": {
"get": {
"summary": "Return OpenAPI specifications for this REST API",
Expand Down
54 changes: 54 additions & 0 deletions scripts/update_swagger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import logging
from pathlib import (
Path,
)

from furl import (
furl,
)

from azul import (
config,
require,
)
from azul.http import (
http_client,
)
from azul.logging import (
configure_script_logging,
)

log = logging.getLogger(__name__)
http = http_client(log)

repository_url = 'https://raw.githubusercontent.com/swagger-api/swagger-ui'
tag = 'v5.18.2'
files = [
'oauth2-redirect.html',
'swagger-ui.css',
'swagger-ui-bundle.js',
'swagger-ui-standalone-preset.js',
# We don't directly serve this file, but we maintain a verbatim copy from
# the upstream distribution for reference.
'swagger-initializer.js'
]

swagger_dir = Path(config.project_root) / 'swagger'


def download_file(name: str):
object_url = furl(repository_url) / tag / 'dist' / name
response = http.request('GET', str(object_url))
require(response.status == 200, name)
with open(swagger_dir / name, 'wb') as f:
f.write(response.data)


def main():
for file_name in files:
download_file(file_name)


if __name__ == '__main__':
configure_script_logging(log)
main()
45 changes: 37 additions & 8 deletions src/azul/chalice.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,12 +526,11 @@ def catalog(self) -> str:
def _controller(self, controller_cls: type[C], **kwargs) -> C:
return controller_cls(app=self, **kwargs)

def swagger_ui(self) -> Response:
swagger_ui_template = self.load_static_resource('swagger', 'swagger-ui.html.template.mustache')
def _expand_swagger_template(self, template: str) -> str:
base_url = self.base_url
redirect_url = furl(base_url).add(path='oauth2_redirect')
deployment_url = furl(base_url).add(path='openapi')
swagger_ui_html = chevron.render(swagger_ui_template, {
return chevron.render(template, {
'DEPLOYMENT_PATH': json.dumps(str(deployment_url.path)),
'OAUTH2_CLIENT_ID': json.dumps(config.google_oauth2_client_id),
'OAUTH2_REDIRECT_URL': json.dumps(str(redirect_url)),
Expand All @@ -540,9 +539,6 @@ def swagger_ui(self) -> Response:
for path, method in self.non_interactive_routes
])
})
return Response(status_code=200,
headers={'Content-Type': 'text/html'},
body=swagger_ui_html)

def swagger_resource(self, file) -> Response:
if os.sep in file:
Expand Down Expand Up @@ -677,7 +673,25 @@ def default_routes(self):
@self.route(
'/',
interactive=False,
cache_control=self._http_cache_for(60),
method_spec={
'summary': 'A redirect to the Swagger UI for interactive use of this REST API',
'tags': ['Auxiliary'],
'responses': {
'301': {
'description': 'A redirect to the Swagger UI'
}
}
}
)
def swagger_redirect():
return Response(status_code=301,
body='',
headers={'Location': str(self.base_url.set(path='static/index.html'))})

@self.route(
'/static/index.html',
interactive=False,
cache_control=self._http_cache_for(24 * 60 * 60),
cors=False,
method_spec={
'summary': 'A Swagger UI for interactive use of this REST API',
Expand All @@ -690,7 +704,22 @@ def default_routes(self):
}
)
def swagger_ui():
return self.swagger_ui()
return self.swagger_resource('index.html')

@self.route(
'/static/swagger-initializer.js',
interactive=False,
cache_control=self._http_cache_for(60),
cors=True,
method_spec={}
)
def swagger_initializer():
template = self.load_static_resource('swagger',
'swagger-initializer.js.template.mustache')
body = self._expand_swagger_template(template)
return Response(status_code=200,
body=body,
headers={'Content-Type': 'application/javascript'})

@self.route(
'/openapi',
Expand Down
19 changes: 19 additions & 0 deletions swagger/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>

<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>
42 changes: 26 additions & 16 deletions swagger/oauth2-redirect.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<!-- https://github.com/swagger-api/swagger-ui/releases/tag/v3.24.2 -->
<!doctype html>
<html lang="en-US">
<title>Swagger UI: OAuth2 Redirect</title>
<body onload="run()">
</body>
</html>
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
Expand All @@ -14,31 +13,32 @@
var isValid, qp, arr;

if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}

arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
return key === "" ? value : decodeURIComponent(value);
}
) : {}
) : {};

isValid = qp.state === sentState
isValid = qp.state === sentState;

if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}

Expand All @@ -47,7 +47,7 @@
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
Expand All @@ -58,12 +58,22 @@
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}

if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions swagger/swagger-initializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">

// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});

//</editor-fold>
};
Loading
Loading