Include Swagger UI

This commit is contained in:
Joel Collins 2020-01-07 14:19:52 +00:00
parent 5c6708cddd
commit b2824251ae
17 changed files with 433 additions and 47 deletions

View file

@ -4,6 +4,7 @@ import time
import atexit
import logging
import os
import pkg_resources
from flask import Flask, jsonify, send_file
@ -59,8 +60,9 @@ else:
app, labthing = create_app(
__name__,
prefix="/api/v2",
description="Test LabThing-based API for OpenFlexure Microscope",
title=f"OpenFlexure Microscope {api_microscope.name}",
description="Test LabThing-based API for OpenFlexure Microscope",
version=pkg_resources.get_distribution("openflexure_microscope").version,
)
# Use custom JSON encoder

View file

@ -10,6 +10,7 @@ from .spec import view2path
from .views.extensions import ExtensionList
from .views.tasks import TaskList, TaskResource
from .views.docs import docs_blueprint, APISpecResource, W3CThingDescriptionResource
from openflexure_microscope.common.labthings_core.utilities import get_docstring
@ -106,11 +107,12 @@ class LabThing(object):
# Add root representation
self.app.add_url_rule(self._complete_url("/", ""), "rootrep", self.rootrep)
# Add thing description
self.app.add_url_rule(self._complete_url("/td", ""), "td", self.td)
# self.app.add_url_rule(self._complete_url("/td", ""), "td", self.td)
# Add swagger spec
self.app.add_url_rule(
self._complete_url("/swagger", ""), "swagger", self.swagger
)
# self.app.add_url_rule(
# self._complete_url("/swagger", ""), "swagger", self.swagger
# )
self.app.register_blueprint(docs_blueprint, url_prefix=self.url_prefix)
# Add extension overview
self.add_resource(
@ -270,35 +272,6 @@ class LabThing(object):
return endpoint in self.endpoints
### Description
def td(self):
"""
W3C-style Thing Description
"""
props = {}
for key, prop in self.properties.items():
props[key] = {}
props[key]["title"] = prop.__name__
props[key]["description"] = get_docstring(prop)
props[key]["links"] = [{"href": self.url_for(prop, _external=True)}]
actions = {}
for key, prop in self.actions.items():
actions[key] = {}
actions[key]["title"] = prop.__name__
actions[key]["description"] = get_docstring(prop)
actions[key]["links"] = [{"href": self.url_for(prop, _external=True)}]
td = {
"id": url_for("td", _external=True),
"title": self.title,
"description": self.description,
"properties": props,
"actions": actions,
}
return jsonify(td)
def rootrep(self):
"""
Root representation
@ -311,14 +284,13 @@ class LabThing(object):
"description": self.description,
"links": {
"thingDescription": {
"href": url_for("td", _external=True),
"description": get_docstring(self.td),
"href": url_for("labthings_docs.w3c_td", _external=True),
"description": get_docstring(W3CThingDescriptionResource),
"methods": ["GET"],
},
"swagger": {
"href": url_for("swagger", _external=True),
"description": get_docstring(self.swagger),
"methods": ["GET"],
"swaggerUI": {
"href": url_for("labthings_docs.swagger_ui", _external=True),
**description_from_view(APISpecResource),
},
"extensions": {
"href": self.url_for(ExtensionList, _external=True),
@ -332,9 +304,3 @@ class LabThing(object):
}
return jsonify(rr)
def swagger(self):
"""
OpenAPI v3 documentation
"""
return jsonify(self.spec.to_dict())

View file

@ -29,7 +29,7 @@ def create_app(
# Create a LabThing
labthing = LabThing(
app, prefix=prefix, title=title, description=description, version=version
app, prefix=prefix, title=title, description=description, version=str(version)
)
# Store references to added-in handlers

View file

@ -0,0 +1,79 @@
from flask import abort, url_for, jsonify, render_template, Blueprint
from openflexure_microscope.common.labthings_core.utilities import get_docstring
from ...resource import Resource
from ...find import current_labthing
import os
class APISpecResource(Resource):
"""
OpenAPI v3 documentation
"""
def get(self):
"""
OpenAPI v3 documentation
"""
return jsonify(current_labthing().spec.to_dict())
class SwaggerUIResource(Resource):
"""
Swagger UI documentation
"""
def get(self):
return render_template("swagger-ui.html")
class W3CThingDescriptionResource(Resource):
"""
W3C-style Thing Description
"""
def get(self):
props = {}
for key, prop in current_labthing().properties.items():
props[key] = {}
props[key]["title"] = prop.__name__
props[key]["description"] = get_docstring(prop)
props[key]["links"] = [
{"href": current_labthing().url_for(prop, _external=True)}
]
actions = {}
for key, prop in current_labthing().actions.items():
actions[key] = {}
actions[key]["title"] = prop.__name__
actions[key]["description"] = get_docstring(prop)
actions[key]["links"] = [
{"href": current_labthing().url_for(prop, _external=True)}
]
td = {
"id": url_for("labthings_docs.w3c_td", _external=True),
"title": current_labthing().title,
"description": current_labthing().description,
"properties": props,
"actions": actions,
}
return jsonify(td)
docs_blueprint = Blueprint(
"labthings_docs", __name__, static_folder="./static", template_folder="./templates"
)
docs_blueprint.add_url_rule(
"/swagger", view_func=APISpecResource.as_view("swagger_json")
)
docs_blueprint.add_url_rule(
"/swagger-ui", view_func=SwaggerUIResource.as_view("swagger_ui")
)
docs_blueprint.add_url_rule(
"/td", view_func=W3CThingDescriptionResource.as_view("w3c_td")
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

View file

@ -0,0 +1,60 @@
<!-- 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="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const 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"
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!doctype html>
<html lang="en-US">
<title>Swagger UI: OAuth2 Redirect</title>
<body onload="run()">
</body>
</html>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
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)
}
) : {}
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !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"
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
</script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="{{ url_for('labthings_docs.static', filename='favicon-32x32.png') }}"
sizes="32x32" />
<link rel="icon" type="image/png" href="{{ url_for('labthings_docs.static', filename='favicon-16x16.png') }}"
sizes="16x16" />
<link href="{{ url_for('labthings_docs.static', filename='swagger-ui.css') }}" rel="stylesheet" type="text/css" />
</head>
<body class="swagger-section">
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
<script src="{{ url_for('labthings_docs.static', filename='swagger-ui-bundle.js') }}" type="text/javascript"></script>
<script src="{{ url_for('labthings_docs.static', filename='swagger-ui-standalone-preset.js') }}"
type="text/javascript"></script>
<script type="text/javascript">
var ui = SwaggerUIBundle({
url: "{{ url_for('labthings_docs.swagger_json') }}",
dom_id: '#swagger-ui-container',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "BaseLayout"
})
window.ui = ui
</script>
</body>
</html>