Merge pull request 'feature/52-alternative_openapi_viewers' (#53) from feature/52-alternative_openapi_viewers into dev
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #53 closes #52
This commit is contained in:
commit
d3760f7b80
2
.gitignore
vendored
2
.gitignore
vendored
@ -132,5 +132,5 @@ build
|
|||||||
|
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite-jurnal
|
*.sqlite-jurnal
|
||||||
docs
|
/docs
|
||||||
lib
|
lib
|
@ -41,7 +41,6 @@
|
|||||||
"routing-controllers": "^0.9.0-alpha.6",
|
"routing-controllers": "^0.9.0-alpha.6",
|
||||||
"routing-controllers-openapi": "^2.1.0",
|
"routing-controllers-openapi": "^2.1.0",
|
||||||
"sqlite3": "^5.0.0",
|
"sqlite3": "^5.0.0",
|
||||||
"swagger-ui-express": "^4.1.5",
|
|
||||||
"typeorm": "^0.2.29",
|
"typeorm": "^0.2.29",
|
||||||
"typeorm-routing-controllers-extensions": "^0.2.0",
|
"typeorm-routing-controllers-extensions": "^0.2.0",
|
||||||
"typeorm-seeding": "^1.6.1",
|
"typeorm-seeding": "^1.6.1",
|
||||||
@ -55,9 +54,9 @@
|
|||||||
"@types/jest": "^26.0.16",
|
"@types/jest": "^26.0.16",
|
||||||
"@types/jsonwebtoken": "^8.5.0",
|
"@types/jsonwebtoken": "^8.5.0",
|
||||||
"@types/node": "^14.14.9",
|
"@types/node": "^14.14.9",
|
||||||
"@types/swagger-ui-express": "^4.1.2",
|
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
|
"cp-cli": "^2.0.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"nodemon": "^2.0.6",
|
"nodemon": "^2.0.6",
|
||||||
"rimraf": "^2.7.1",
|
"rimraf": "^2.7.1",
|
||||||
@ -69,11 +68,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon src/app.ts",
|
"dev": "nodemon src/app.ts",
|
||||||
"build": "tsc",
|
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
|
||||||
"docs": "typedoc --out docs src",
|
"docs": "typedoc --out docs src",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watchAll",
|
"test:watch": "jest --watchAll",
|
||||||
"test:ci": "start-server-and-test dev http://localhost:4010/api/openapi.json test",
|
"test:ci": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test",
|
||||||
"seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed",
|
"seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed",
|
||||||
"openapi:export": "ts-node src/openapi_export.ts"
|
"openapi:export": "ts-node src/openapi_export.ts"
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { validationMetadatasToSchemas } from "class-validator-jsonschema";
|
import { validationMetadatasToSchemas } from "class-validator-jsonschema";
|
||||||
import { Application } from "express";
|
import express, { Application } from "express";
|
||||||
|
import path from 'path';
|
||||||
import { getMetadataArgsStorage } from "routing-controllers";
|
import { getMetadataArgsStorage } from "routing-controllers";
|
||||||
import { routingControllersToSpec } from "routing-controllers-openapi";
|
import { routingControllersToSpec } from "routing-controllers-openapi";
|
||||||
import * as swaggerUiExpress from "swagger-ui-express";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader for everything openapi related - from creating the schema to serving it via a static route and swaggerUiExpress.
|
* Loader for everything openapi related - from creating the schema to serving it via a static route and swaggerUiExpress.
|
||||||
@ -45,18 +45,9 @@ export default async (app: Application) => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
app.get(["/api/docs/openapi.json", "/api/docs/swagger.json"], (req, res) => {
|
||||||
//Options for swaggerUiExpress
|
|
||||||
const options = {
|
|
||||||
explorer: true,
|
|
||||||
};
|
|
||||||
app.use(
|
|
||||||
"/api/docs",
|
|
||||||
swaggerUiExpress.serve,
|
|
||||||
swaggerUiExpress.setup(spec, options)
|
|
||||||
);
|
|
||||||
app.get(["/api/openapi.json", "/api/swagger.json"], (req, res) => {
|
|
||||||
res.json(spec);
|
res.json(spec);
|
||||||
});
|
});
|
||||||
|
app.use('/api/docs', express.static(path.join(__dirname, '../static/docs'), { index: "index.html", extensions: ['html'] }));
|
||||||
return app;
|
return app;
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,14 @@ const spec = routingControllersToSpec(
|
|||||||
"AuthToken": {
|
"AuthToken": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"scheme": "bearer",
|
"scheme": "bearer",
|
||||||
"bearerFormat": "JWT"
|
"bearerFormat": "JWT",
|
||||||
|
description: "A JWT based access token. Use /api/auth/login or /api/auth/refresh to get one."
|
||||||
|
},
|
||||||
|
"RefreshTokenCookie": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"in": "cookie",
|
||||||
|
"name": "lfk_backend__refresh_token",
|
||||||
|
description: "A cookie containing a JWT based refreh token. Attention: Doesn't work in swagger-ui. Use /api/auth/login or /api/auth/refresh to get one."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
156
src/static/docs/index.html
Normal file
156
src/static/docs/index.html
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>API Docs</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color: #fff;
|
||||||
|
--bg-secondary-color: #f3f3f6;
|
||||||
|
--color-primary: #14854f;
|
||||||
|
--color-lightGrey: #d2d6dd;
|
||||||
|
--color-grey: #747681;
|
||||||
|
--color-darkGrey: #3f4144;
|
||||||
|
--color-error: #d43939;
|
||||||
|
--color-success: #28bd14;
|
||||||
|
--grid-maxWidth: 120rem;
|
||||||
|
--grid-gutter: 2rem;
|
||||||
|
--font-size: 1.6rem;
|
||||||
|
--font-color: #333;
|
||||||
|
--font-family-sans: -apple-system, BlinkMacSystemFont, Avenir, "Avenir Next", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
|
--font-family-mono: monaco, "Consolas", "Lucida Console", monospace
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 62.5%;
|
||||||
|
line-height: 1.15;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
:after,
|
||||||
|
:before {
|
||||||
|
-webkit-box-sizing: inherit;
|
||||||
|
box-sizing: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
color: var(--font-color);
|
||||||
|
font-family: Segoe UI, Helvetica Neue, sans-serif;
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: 500;
|
||||||
|
margin: .35em 0 .7em
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-primary);
|
||||||
|
text-decoration: none
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover:not(.button) {
|
||||||
|
opacity: .75
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=color]):not([type=button]):not([type=reset]):not(:disabled):hover {
|
||||||
|
border-color: var(--color-grey)
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
color: #bdbfc4
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-placeholder {
|
||||||
|
color: #bdbfc4
|
||||||
|
}
|
||||||
|
|
||||||
|
:-ms-input-placeholder {
|
||||||
|
color: #bdbfc4
|
||||||
|
}
|
||||||
|
|
||||||
|
::-ms-input-placeholder {
|
||||||
|
color: #bdbfc4
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs a {
|
||||||
|
text-decoration: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs>a {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
-webkit-box-flex: 0;
|
||||||
|
-ms-flex: 0 1 auto;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
color: var(--color-darkGrey);
|
||||||
|
border-bottom: 2px solid var(--color-lightGrey);
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs>a:hover {
|
||||||
|
opacity: 1;
|
||||||
|
border-bottom: 2px solid var(--color-darkGrey)
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-vertical-align {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-center {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-center {
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="hero">
|
||||||
|
<div class="logo is-center is-vertical-align">
|
||||||
|
<h3>API Docs</h3>
|
||||||
|
</div>
|
||||||
|
<nav class="tabs is-center">
|
||||||
|
<a href="./redoc">ReDoc</a>
|
||||||
|
<a href="./swaggerui">SwaggerUI</a>
|
||||||
|
<a href="./rapidoc">RapiDoc</a>
|
||||||
|
<a href="./openapi.json">Raw Spec (json)</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
220
src/static/docs/rapidoc-min.js
vendored
Normal file
220
src/static/docs/rapidoc-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
src/static/docs/rapidoc.html
Normal file
12
src/static/docs/rapidoc.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script type="module" src="./rapidoc-min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<rapi-doc
|
||||||
|
spec-url="/api/docs/openapi.json"
|
||||||
|
> </rapi-doc>
|
||||||
|
</body>
|
||||||
|
</html>
|
18
src/static/docs/redoc.html
Normal file
18
src/static/docs/redoc.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ReDoc</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<redoc spec-url='/api/docs/openapi.json'></redoc>
|
||||||
|
<script src="./redoc.standalone.js"> </script>
|
||||||
|
</body>
|
||||||
|
</html>
|
103
src/static/docs/redoc.standalone.js
Normal file
103
src/static/docs/redoc.standalone.js
Normal file
File diff suppressed because one or more lines are too long
3
src/static/docs/swagger-ui-bundle.js
Normal file
3
src/static/docs/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
3
src/static/docs/swagger-ui-standalone-preset.js
Normal file
3
src/static/docs/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
8895
src/static/docs/swagger-ui.css
Normal file
8895
src/static/docs/swagger-ui.css
Normal file
File diff suppressed because it is too large
Load Diff
58
src/static/docs/swaggerui.html
Normal file
58
src/static/docs/swaggerui.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!-- 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" >
|
||||||
|
<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" charset="UTF-8"> </script>
|
||||||
|
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
// Begin Swagger UI call region
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "/api/docs/openapi.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>
|
34
src/tests/api_docs.spec.ts
Normal file
34
src/tests/api_docs.spec.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { config } from '../config';
|
||||||
|
const base = "http://localhost:" + config.internal_port
|
||||||
|
|
||||||
|
describe('GET /api/docs/openapi.json', () => {
|
||||||
|
it('OpenAPI Spec is availdable 200', async () => {
|
||||||
|
const res = await axios.get(base + '/api/docs/openapi.json');
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('GET /api/docs/swagger.json', () => {
|
||||||
|
it('OpenAPI Spec is availdable 200', async () => {
|
||||||
|
const res = await axios.get(base + '/api/docs/swagger.json');
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('GET /api/docs/swaggerui', () => {
|
||||||
|
it('swaggerui is availdable 200', async () => {
|
||||||
|
const res = await axios.get(base + '/api/docs/swaggerui');
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('GET /api/docs/redoc', () => {
|
||||||
|
it('redoc is availdable 200', async () => {
|
||||||
|
const res = await axios.get(base + '/api/docs/redoc');
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('GET /api/docs/rapidoc', () => {
|
||||||
|
it('rapidoc is availdable 200', async () => {
|
||||||
|
const res = await axios.get(base + '/api/docs/rapidoc');
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
});
|
||||||
|
});
|
@ -1,35 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import { config } from '../config';
|
|
||||||
const base = "http://localhost:" + config.internal_port
|
|
||||||
|
|
||||||
let access_token;
|
|
||||||
let axios_config;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
|
|
||||||
access_token = res.data["access_token"];
|
|
||||||
axios_config = {
|
|
||||||
headers: { "authorization": "Bearer " + access_token },
|
|
||||||
validateStatus: undefined
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /api/openapi.json', () => {
|
|
||||||
it('is http 200', async () => {
|
|
||||||
const res = await axios.get(base + '/api/openapi.json');
|
|
||||||
expect(res.status).toEqual(200);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('GET /', () => {
|
|
||||||
it('is http 404', async () => {
|
|
||||||
const res = await axios.get(base + '/', axios_config);
|
|
||||||
expect(res.status).toEqual(404);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('GET /api/teams', () => {
|
|
||||||
it('is http 200 && is json', async () => {
|
|
||||||
const res = await axios.get(base + '/api/teams', axios_config);
|
|
||||||
expect(res.status).toEqual(200);
|
|
||||||
expect(res.headers['content-type']).toContain("application/json")
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user