adapt-authoring-docs 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "adapt-authoring-docs",
3
+ "version": "0.0.1",
4
+ "description": "Tools for auto-generating documentation for the Adapt authoring tool",
5
+ "homepage": "https://github.com/adapt-security/adapt-authoring-docs",
6
+ "license": "GPL-3.0",
7
+ "type": "module",
8
+ "bin": {
9
+ "at-docgen": "./bin/docgen.js",
10
+ "at-docserve": "./bin/docserve.js"
11
+ },
12
+ "repository": "github:adapt-security/adapt-authoring-docs",
13
+ "dependencies": {
14
+ "comment-parser": "^1.4.1",
15
+ "docdash": "^2.0.2",
16
+ "docsify-cli": "^4.4.4",
17
+ "fs-extra": "^11.2.0",
18
+ "glob": "^11.0.0",
19
+ "http-server": "^14.1.1",
20
+ "jsdoc": "^4.0.3",
21
+ "swagger-ui": "^5.17.14"
22
+ },
23
+ "peerDependencies": {
24
+ "adapt-authoring-core": "github:adapt-security/adapt-authoring-core"
25
+ },
26
+ "devDependencies": {
27
+ "@semantic-release/commit-analyzer": "^13.0.1",
28
+ "@semantic-release/git": "^10.0.1",
29
+ "@semantic-release/github": "^12.0.2",
30
+ "@semantic-release/npm": "^13.1.2",
31
+ "@semantic-release/release-notes-generator": "^14.1.0",
32
+ "conventional-changelog-eslint": "^6.0.0",
33
+ "semantic-release": "^25.0.2",
34
+ "semantic-release-replace-plugin": "^1.2.7",
35
+ "standard": "^17.1.0"
36
+ },
37
+ "release": {
38
+ "plugins": [
39
+ [
40
+ "@semantic-release/commit-analyzer",
41
+ {
42
+ "preset": "eslint"
43
+ }
44
+ ],
45
+ [
46
+ "@semantic-release/release-notes-generator",
47
+ {
48
+ "preset": "eslint"
49
+ }
50
+ ],
51
+ "@semantic-release/npm",
52
+ "@semantic-release/github",
53
+ [
54
+ "@semantic-release/git",
55
+ {
56
+ "assets": [
57
+ "package.json"
58
+ ],
59
+ "message": "Chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
60
+ }
61
+ ]
62
+ ]
63
+ }
64
+ }
package/root/.nojekyll ADDED
File without changes
@@ -0,0 +1,116 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link rel="icon" href="assets/favicon.png" sizes="any">
8
+ <title>Documentation - Adapt authoring tool</title>
9
+ </head>
10
+ <body>
11
+ <div class="container"><img src="assets/logo.png" /></div>
12
+ <h1>Adapt authoring tool</h1>
13
+ <h2>Documentation</h2>
14
+ <div class="link-container">
15
+ <a class="big" href="manual">Developer guides</a>
16
+ </div>
17
+ <div class="link-container">
18
+ <a href="rest">REST API Reference</a>
19
+ <a href="backend">Server Code Reference</a>
20
+ <a href="frontend">UI Code Reference</a>
21
+ </div>
22
+ <div id="update-age" class="hide">Docs updated <span id="age"></span> ago.</div>
23
+ </body>
24
+ <style>
25
+ @import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700|Roboto+Mono);
26
+ body {
27
+ background: linear-gradient(to left bottom, #263944 0%,#0096bb 100%) !important;
28
+ font-family: 'Open Sans', sans-serif;
29
+ color: white;
30
+ width: 100vw;
31
+ height: 100vh;
32
+ text-align: center;
33
+ margin-top: 15vh;
34
+ overflow: hidden;
35
+ }
36
+ h1 {
37
+ font-size: 2.5rem;
38
+ font-weight: 300;
39
+ }
40
+ h2 {
41
+ font-size: 5em;
42
+ font-weight: 300;
43
+ }
44
+ .link-container {
45
+ margin-bottom: 2rem;
46
+ }
47
+ a {
48
+ border-radius: 3rem;
49
+ border: 1px solid white;
50
+ box-sizing: border-box;
51
+ color: white;
52
+ display: inline-block;
53
+ font-size: 1.1rem;
54
+ margin: .5rem 1rem;
55
+ padding: .75em 2rem;
56
+ text-decoration: none;
57
+ transition: all .15s ease;
58
+ }
59
+ a.big {
60
+ font-size: 1.4rem;;
61
+ }
62
+ a:hover {
63
+ background: white;
64
+ color: #14647b;
65
+ }
66
+ #update-age {
67
+ position: fixed;
68
+ padding: 20px;
69
+ text-align: center;
70
+ width: 100%;
71
+ bottom: 0;
72
+ font-size: 90%;
73
+ opacity: 1;
74
+ transition: opacity 0.3s;
75
+ }
76
+ #update-age.hide {
77
+ opacity: 0;
78
+ }
79
+ </style>
80
+ <script>
81
+ async function getUpdateDate() {
82
+ try {
83
+ const res = await fetch('https://api.github.com/repos/adapt-security/adapt-authoring-documentation/commits/master');
84
+ if(res.status > 299) {
85
+ throw new Error(res.statusText);
86
+ }
87
+ const data = await res.json();
88
+ const age = getAge(data.commit.committer.date);
89
+ document.getElementById('age').innerHTML = `${age.diff} ${age.unit}`;
90
+ document.getElementById('update-age').classList.remove("hide");
91
+ } catch(e) {
92
+ console.error(e);
93
+ }
94
+ }
95
+ function getAge(timestamp) {
96
+ const diffMs = new Date() - new Date(timestamp);
97
+ let divider = 1000;
98
+ const units = [
99
+ { duration: 1, unit: 'second' },
100
+ { duration: 60, unit: 'minute' },
101
+ { duration: 60, unit: 'hour' },
102
+ { duration: 24, unit: 'day' },
103
+ { duration: 30, unit: 'week' },
104
+ { duration: 12, unit: 'month' }
105
+ ];
106
+ return units.reduce((memo, data) => {
107
+ divider *= data.duration;
108
+ data.rawDiff = diffMs/divider;
109
+ data.diff = Math.round(data.rawDiff);
110
+ if(data.diff > 1) data.unit += 's';
111
+ return data.rawDiff > 1 ? data : memo;
112
+ }, {});
113
+ }
114
+ getUpdateDate();
115
+ </script>
116
+ </html>
@@ -0,0 +1,28 @@
1
+ <!doctype html>
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <link rel="icon" href="assets/favicon.png" sizes="any">
7
+ <script src="js/swagger-ui-bundle.js" crossorigin></script>
8
+ <script src="js/swagger-ui-standalone-preset.js" crossorigin></script>
9
+ <link rel="stylesheet" href="styles/swagger-ui.css" />
10
+ <link rel="stylesheet" href="styles/adapt.css">
11
+ </head>
12
+ <html>
13
+ <head>
14
+ <meta charset="UTF-8">
15
+ <title>Adapt authoring tool - REST API documentation</title>
16
+ </head>
17
+ <body>
18
+ <header>
19
+ <div class="container"><img src="assets/logo.png" /></div>
20
+ <h1>Adapt authoring tool</h1>
21
+ <h2>REST API Documentation</h2>
22
+ </header>
23
+ <div id="swagger"></div>
24
+ <script>
25
+ window.onload = () => SwaggerUIBundle({ url: 'api.json', dom_id: '#swagger' });
26
+ </script>
27
+ </body>
28
+ </html>
@@ -0,0 +1,111 @@
1
+ @import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700|Roboto+Mono);
2
+
3
+ body {
4
+ --default-font-family: 'Open Sans' !important;
5
+ --default-font-size: 13px;
6
+ --mono-font-family: 'Roboto Mono', monospace !important;
7
+ font-family: var(--default-font-family);
8
+ font-size: var(--default-font-size);
9
+ margin: 0;
10
+ }
11
+
12
+ h1, h2, h3, h4, h5, h6,
13
+ h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
14
+ font-family: var(--default-font-family);
15
+ font-weight: 300 !important;
16
+ }
17
+
18
+ .main a,
19
+ .auth-wrapper,
20
+ .try-out {
21
+ display: none !important;
22
+ }
23
+
24
+ header {
25
+ padding: 20px;
26
+ margin-bottom: 15px;
27
+ color: white;
28
+ text-align: center;
29
+ background: linear-gradient(to left bottom, #263944 0%,#0096bb 100%) !important;
30
+ }
31
+ header img {
32
+ height: 50px;
33
+ }
34
+ header h1 {
35
+ font-size: 200%;
36
+ margin: 10px;
37
+ }
38
+ header h2 {
39
+ font-size: initial;
40
+ margin: 0;
41
+ }
42
+
43
+ #swagger {
44
+ margin-bottom: 50px;
45
+ }
46
+
47
+ .swagger-ui .wrapper {
48
+ max-width: 1000px;
49
+ }
50
+ /* hide schemas UI */
51
+ .swagger-ui .wrapper:last-child {
52
+ display: none;
53
+ }
54
+
55
+ .swagger-ui .info {
56
+ margin: 0 !important;
57
+ text-align: center;
58
+ }
59
+ .swagger-ui .info hgroup.main {
60
+ margin-bottom: 0;
61
+ }
62
+ .swagger-ui .info .title {
63
+ display: inline-block;
64
+ font-size: initial !important;
65
+ }
66
+
67
+ .swagger-ui .main small {
68
+ position: initial !important;
69
+ border-radius: 3px !important;
70
+ vertical-align: initial !important;
71
+ }
72
+
73
+ .swagger-ui .opblock-description-wrapper p,
74
+ .swagger-ui .opblock-external-docs-wrapper p,
75
+ .swagger-ui .opblock-title_normal p,
76
+ .swagger-ui .opblock opblock-summary-method,
77
+ .swagger-ui .opblock .opblock-summary-description,
78
+ .swagger-ui .parameter__name.required {
79
+ font-family: var(--default-font-family);
80
+ font-size: var(--default-font-size);
81
+ }
82
+ .swagger-ui .opblock-body pre.microlight {
83
+ padding: 15px !important;
84
+ font-size: 85%;
85
+ font-weight: normal;
86
+ white-space: nowrap;
87
+ overflow-y: scroll;
88
+ }
89
+ .swagger-ui .opblock-body select {
90
+ font-size: 90%;
91
+ }
92
+
93
+ .swagger-ui .opblock .opblock-summary-path {
94
+ font-size: var(--default-font-size);
95
+ font-weight: bold;
96
+ }
97
+
98
+ .swagger-ui .opblock .opblock-summary-description {
99
+ text-align: right;
100
+ margin-right: 15px;
101
+ }
102
+
103
+ .swagger-ui .opblock .opblock-description span {
104
+ background-color: #8121d9;
105
+ color: white;
106
+ padding: 3px 5px;
107
+ margin-left: 5px;
108
+ border-radius: 3px;
109
+ font-family: monospace;
110
+ font-size: 85%;
111
+ }
@@ -0,0 +1,96 @@
1
+ import { fileURLToPath } from 'url'
2
+ import fs from 'fs/promises'
3
+ import path from 'path'
4
+
5
+ function resolvePath (relativePath) {
6
+ return fileURLToPath(new URL(relativePath, import.meta.url))
7
+ }
8
+ /**
9
+ *
10
+ */
11
+ export default async function swagger (app, configs, outputdir) {
12
+ const server = await app.waitForModule('server')
13
+ await app.onReady()
14
+ const spec = {
15
+ openapi: '3.0.3',
16
+ info: { version: app.pkg.version },
17
+ components: { schemas: await generateSchemaSpec(app) },
18
+ paths: generatePathSpec(app, server.api)
19
+ }
20
+ // generate UI
21
+ const dir = path.resolve(outputdir, 'rest')
22
+ const cssDir = path.resolve(dir, 'styles')
23
+ const jsDir = path.resolve(dir, 'js')
24
+ const distDir = 'node_modules/swagger-ui/dist'
25
+
26
+ await fs.mkdir(dir)
27
+ await fs.mkdir(cssDir)
28
+ await fs.mkdir(jsDir)
29
+
30
+ await Promise.all([
31
+ fs.cp(resolvePath('./index.html'), path.resolve(dir, 'index.html')),
32
+ fs.cp(path.join(distDir, 'swagger-ui.css'), path.resolve(cssDir, 'swagger-ui.css')),
33
+ fs.cp(resolvePath('./styles/adapt.css'), path.resolve(cssDir, 'adapt.css')),
34
+ fs.cp(resolvePath('../assets'), path.resolve(dir, 'assets'), { recursive: true }),
35
+ fs.cp(path.join(distDir, 'swagger-ui-bundle.js'), path.resolve(jsDir, 'swagger-ui-bundle.js')),
36
+ fs.cp(path.join(distDir, 'swagger-ui-standalone-preset.js'), path.resolve(jsDir, 'swagger-ui-standalone-preset.js')),
37
+ fs.writeFile(path.resolve(dir, 'api.json'), JSON.stringify(spec, null, 2))
38
+ ])
39
+ }
40
+
41
+ async function generateSchemaSpec (app) {
42
+ const jsonschema = await app.waitForModule('jsonschema')
43
+ const schemas = {}
44
+ await Promise.all(Object.keys(jsonschema.schemas).map(async s => {
45
+ schemas[s] = sanitiseSchema((await jsonschema.getSchema(s)).built)
46
+ }))
47
+ return schemas
48
+ }
49
+
50
+ function sanitiseSchema (schema) {
51
+ const props = schema.properties ?? schema
52
+ for (const prop in props) {
53
+ const s = props[prop]
54
+ if (s.type === 'object') props[prop] = sanitiseSchema(s)
55
+ if (s.isInternal || s.isReadOnly) delete props[prop]
56
+ }
57
+ return schema
58
+ }
59
+
60
+ function generatePathSpec (app, router, paths = {}) {
61
+ const perms = app.dependencyloader.instances['adapt-authoring-auth'].permissions.routes
62
+ router.routes.forEach(r => {
63
+ const parameters = r.route.split('/').filter(r => r.startsWith(':')).map(r => {
64
+ return {
65
+ name: r.replaceAll(/:|\?/g, ''),
66
+ in: 'path',
67
+ required: !r.endsWith('?')
68
+ }
69
+ })
70
+ const route = `${router.path}${r.route !== '/' ? r.route : ''}`
71
+ paths[route] = Object.keys(r.handlers).reduce((memo, method) => {
72
+ const meta = r.meta?.[method] || {}
73
+ const scopes = perms[method].find(p => route.match(p[0]))?.[1] || []
74
+ let description = r.internal ? 'ROUTE IS ONLY ACCESSIBLE FROM LOCALHOST.<br/><br/>' : ''
75
+ description += scopes.length
76
+ ? `Required scopes: ${scopes.map(s => `<span>${s}</span>`).join(' ')}`
77
+ : 'Route requires no authentication'
78
+
79
+ if (meta.description) description += `<br/><br/>${meta.description}`
80
+
81
+ return Object.assign(memo, {
82
+ [method]: {
83
+ ...meta,
84
+ tags: [router.path.split('/').slice(2).join(' ')],
85
+ description,
86
+ parameters: meta.parameters ? parameters.concat(meta.parameters) : parameters,
87
+ security: { roles: scopes }
88
+ }
89
+ })
90
+ }, {})
91
+ })
92
+ if (router.childRouters.length) {
93
+ router.childRouters.forEach(childRouter => generatePathSpec(app, childRouter, paths))
94
+ }
95
+ return Object.keys(paths).sort().reduce((m, k) => Object.assign(m, { [k]: paths[k] }), {})
96
+ }