@strapi/plugin-documentation 0.0.0-00a3f69152eb918683ed5c05bfed9c45495c0a87

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.
Files changed (73) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +151 -0
  3. package/__mocks__/strapi.js +41 -0
  4. package/__tests__/build-component-schema.test.js +271 -0
  5. package/admin/src/components/FieldActionWrapper/index.js +14 -0
  6. package/admin/src/components/PluginIcon/index.js +12 -0
  7. package/admin/src/index.js +80 -0
  8. package/admin/src/pages/PluginPage/index.js +199 -0
  9. package/admin/src/pages/PluginPage/tests/index.test.js +873 -0
  10. package/admin/src/pages/PluginPage/tests/server.js +23 -0
  11. package/admin/src/pages/SettingsPage/index.js +181 -0
  12. package/admin/src/pages/SettingsPage/tests/index.test.js +612 -0
  13. package/admin/src/pages/SettingsPage/tests/server.js +18 -0
  14. package/admin/src/pages/utils/api.js +31 -0
  15. package/admin/src/pages/utils/schema.js +11 -0
  16. package/admin/src/pages/utils/useReactQuery.js +46 -0
  17. package/admin/src/permissions.js +19 -0
  18. package/admin/src/pluginId.js +5 -0
  19. package/admin/src/translations/ar.json +20 -0
  20. package/admin/src/translations/cs.json +21 -0
  21. package/admin/src/translations/de.json +26 -0
  22. package/admin/src/translations/dk.json +39 -0
  23. package/admin/src/translations/en.json +39 -0
  24. package/admin/src/translations/es.json +39 -0
  25. package/admin/src/translations/fr.json +26 -0
  26. package/admin/src/translations/id.json +24 -0
  27. package/admin/src/translations/it.json +26 -0
  28. package/admin/src/translations/ko.json +39 -0
  29. package/admin/src/translations/ms.json +23 -0
  30. package/admin/src/translations/nl.json +21 -0
  31. package/admin/src/translations/pl.json +39 -0
  32. package/admin/src/translations/pt-BR.json +21 -0
  33. package/admin/src/translations/pt.json +21 -0
  34. package/admin/src/translations/ru.json +28 -0
  35. package/admin/src/translations/sk.json +24 -0
  36. package/admin/src/translations/sv.json +39 -0
  37. package/admin/src/translations/th.json +24 -0
  38. package/admin/src/translations/tr.json +20 -0
  39. package/admin/src/translations/uk.json +23 -0
  40. package/admin/src/translations/vi.json +24 -0
  41. package/admin/src/translations/zh-Hans.json +28 -0
  42. package/admin/src/translations/zh.json +39 -0
  43. package/admin/src/utils/getTrad.js +5 -0
  44. package/admin/src/utils/index.js +2 -0
  45. package/admin/src/utils/openWithNewTab.js +20 -0
  46. package/package.json +66 -0
  47. package/server/bootstrap.js +54 -0
  48. package/server/config/default-plugin-config.js +74 -0
  49. package/server/config/index.js +7 -0
  50. package/server/controllers/documentation.js +241 -0
  51. package/server/controllers/index.js +7 -0
  52. package/server/index.js +17 -0
  53. package/server/middlewares/documentation.js +25 -0
  54. package/server/middlewares/index.js +7 -0
  55. package/server/middlewares/restrict-access.js +24 -0
  56. package/server/public/index.html +70 -0
  57. package/server/public/login.html +145 -0
  58. package/server/register.js +11 -0
  59. package/server/routes/index.js +84 -0
  60. package/server/services/documentation.js +210 -0
  61. package/server/services/helpers/build-api-endpoint-path.js +185 -0
  62. package/server/services/helpers/build-component-schema.js +216 -0
  63. package/server/services/helpers/index.js +9 -0
  64. package/server/services/helpers/utils/clean-schema-attributes.js +235 -0
  65. package/server/services/helpers/utils/get-api-responses.js +105 -0
  66. package/server/services/helpers/utils/get-schema-data.js +32 -0
  67. package/server/services/helpers/utils/loop-content-type-names.js +53 -0
  68. package/server/services/helpers/utils/pascal-case.js +9 -0
  69. package/server/services/helpers/utils/query-params.js +95 -0
  70. package/server/services/helpers/utils/routes.js +10 -0
  71. package/server/services/index.js +7 -0
  72. package/strapi-admin.js +3 -0
  73. package/strapi-server.js +3 -0
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const documentation = require('./documentation');
4
+
5
+ module.exports = {
6
+ documentation,
7
+ };
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ module.exports = async (ctx, next) => {
4
+ const pluginStore = strapi.store({ type: 'plugin', name: 'documentation' });
5
+
6
+ const config = await pluginStore.get({ key: 'config' });
7
+
8
+ if (!config.restrictedAccess) {
9
+ return next();
10
+ }
11
+
12
+ if (!ctx.session.documentation || !ctx.session.documentation.logged) {
13
+ const querystring = ctx.querystring ? `?${ctx.querystring}` : '';
14
+
15
+ return ctx.redirect(
16
+ `${strapi.config.server.url}${
17
+ strapi.config.get('plugin.documentation.x-strapi-config').path
18
+ }/login${querystring}`
19
+ );
20
+ }
21
+
22
+ // Execute the action.
23
+ return next();
24
+ };
@@ -0,0 +1,70 @@
1
+ <!-- HTML for static distribution bundle build --><!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Swagger UI</title>
6
+ <link
7
+ rel="stylesheet"
8
+ type="text/css"
9
+ href="<%=backendUrl%>/plugins/documentation/swagger-ui.css"
10
+ />
11
+ <link
12
+ rel="icon"
13
+ type="image/png"
14
+ href="<%=backendUrl%>/plugins/documentation/favicon-32x32.png"
15
+ sizes="32x32"
16
+ />
17
+ <link
18
+ rel="icon"
19
+ type="image/png"
20
+ href="<%=backendUrl%>/plugins/documentation/favicon-16x16.png"
21
+ sizes="16x16"
22
+ />
23
+ <style>
24
+ html {
25
+ box-sizing: border-box;
26
+ overflow: -moz-scrollbars-vertical;
27
+ overflow-y: scroll;
28
+ }
29
+
30
+ *,
31
+ *:before,
32
+ *:after {
33
+ box-sizing: inherit;
34
+ }
35
+
36
+ body {
37
+ margin: 0;
38
+ background: #fafafa;
39
+ }
40
+ </style>
41
+ </head>
42
+
43
+ <body>
44
+ <div id="swagger-ui"></div>
45
+ <script class="custom-swagger-ui">
46
+ window.onload = function() {
47
+ const ui = SwaggerUIBundle({
48
+ url: "https://petstore.swagger.io/v2/swagger.json",
49
+ spec: <%=spec%>,
50
+ dom_id: '#swagger-ui',
51
+ docExpansion: "none",
52
+ deepLinking: true,
53
+ presets: [
54
+ SwaggerUIBundle.presets.apis,
55
+ SwaggerUIStandalonePreset,
56
+ ],
57
+ plugins: [
58
+ SwaggerUIBundle.plugins.DownloadUrl,
59
+ ],
60
+ layout: "StandaloneLayout",
61
+ });
62
+
63
+ window.ui = ui;
64
+ }
65
+ </script>
66
+
67
+ <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-bundle.js"></script>
68
+ <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-standalone-preset.js"></script>
69
+ </body>
70
+ </html>
@@ -0,0 +1,145 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Login - Documentation</title>
5
+ <link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" />
6
+ <style>
7
+ html {
8
+ font-size: 62.5%;
9
+ height: 100%;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ height: 100%;
16
+ margin: 0;
17
+ background-color: #ffffff;
18
+ font-family: 'Lato';
19
+ font-size: 1.4rem;
20
+ font-weight: 400;
21
+ text-rendering: optimizeLegibility;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
25
+
26
+ .login {
27
+ height: 100%;
28
+ background-color: #f6f9fc;
29
+ }
30
+
31
+ .login .login-form {
32
+ height: calc(100% - 70px);
33
+ padding: 68px 0 0;
34
+ text-align: center;
35
+ }
36
+
37
+ .login .login-form form {
38
+ position: relative;
39
+ max-width: 460px;
40
+ padding: 26px 30px;
41
+ margin: 55px auto 0;
42
+ background-color: #ffffff;
43
+ border-radius: 3px;
44
+ box-shadow: 0px 2px 4px rgba(91, 107, 174, 0.15);
45
+ text-align: center;
46
+ }
47
+
48
+ .login .login-form form:before {
49
+ position: absolute;
50
+ content: '';
51
+ top: 0px;
52
+ left: 0;
53
+ display: inline-block;
54
+ width: 100%;
55
+ height: 2px;
56
+ background-color: #2b66cc;
57
+ }
58
+
59
+ .login .login-form form .error {
60
+ display: block;
61
+ color: #ff4e00;
62
+ padding-bottom: 20px;
63
+ }
64
+
65
+ .login .login-form .sub-title {
66
+ margin-top: 35px;
67
+ font-size: 1.6rem;
68
+ font-weight: 400;
69
+ }
70
+
71
+ .login .login-form .logo {
72
+ max-height: 40px;
73
+ }
74
+
75
+ .login .login-form form label {
76
+ display: block;
77
+ margin-bottom: 18px;
78
+ width: 100%;
79
+ text-align: left;
80
+ font-weight: 600;
81
+ }
82
+
83
+ .login .login-form form input {
84
+ outline: none;
85
+ width: calc(100% - 30px);
86
+ height: 36px;
87
+ padding: 0 15px;
88
+ border: 1px solid #ececec;
89
+ border-radius: 2px;
90
+ margin-bottom: 20px;
91
+ line-height: 36px;
92
+ text-align: left;
93
+ }
94
+
95
+ .login .login-form form input[type='submit'] {
96
+ cursor: pointer;
97
+ display: inline-block;
98
+ width: auto;
99
+ margin: 12px auto 0;
100
+ padding: 0 75px;
101
+ background: transparent;
102
+ border-radius: 36px;
103
+ border: 1px solid #2b66cc;
104
+ color: #2b66cc;
105
+ text-transform: uppercase;
106
+ font-size: 1.4rem;
107
+ font-weight: 700;
108
+ transition: all 0.2s ease-out;
109
+ }
110
+
111
+ .login .login-form form input[type='submit']:hover {
112
+ background: #2b66cc;
113
+ color: #ffffff;
114
+ }
115
+ </style>
116
+ </head>
117
+ <body>
118
+ <div class="login">
119
+ <section class="login-form">
120
+ <div class="container">
121
+ <div class="row">
122
+ <div class="col-lg-6 col-lg-offset-3 col-md-12">
123
+ <img
124
+ alt="Strapi logo"
125
+ class="logo"
126
+ src="https://strapi.io/assets/images/logo_login.png"
127
+ />
128
+ <h2 class="sub-title">Enter the password to access the documentation.</h2>
129
+ <form method="post" action="<%=actionUrl%>">
130
+ <span class="error">Wrong password...</span>
131
+ <label>Password</label>
132
+ <input
133
+ type="password"
134
+ name="password"
135
+ placeholder="&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;"
136
+ />
137
+ <input type="submit" value="Login" />
138
+ </form>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </section>
143
+ </div>
144
+ </body>
145
+ </html>
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const registerDocumentationMiddleWare = require('./middlewares/documentation');
4
+
5
+ /**
6
+ * Register upload plugin
7
+ * @param {{ strapi: import('@strapi/strapi').Strapi }}
8
+ */
9
+ module.exports = async ({ strapi }) => {
10
+ await registerDocumentationMiddleWare({ strapi });
11
+ };
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ const restrictAccess = require('../middlewares/restrict-access');
4
+
5
+ module.exports = [
6
+ {
7
+ method: 'GET',
8
+ path: '/',
9
+ handler: 'documentation.index',
10
+ config: {
11
+ auth: false,
12
+ middlewares: [restrictAccess],
13
+ },
14
+ },
15
+ {
16
+ method: 'GET',
17
+ path: '/v:major(\\d+).:minor(\\d+).:patch(\\d+)',
18
+ handler: 'documentation.index',
19
+ config: {
20
+ auth: false,
21
+ middlewares: [restrictAccess],
22
+ },
23
+ },
24
+ {
25
+ method: 'GET',
26
+ path: '/login',
27
+ handler: 'documentation.loginView',
28
+ config: {
29
+ auth: false,
30
+ },
31
+ },
32
+ {
33
+ method: 'POST',
34
+ path: '/login',
35
+ handler: 'documentation.login',
36
+ config: {
37
+ auth: false,
38
+ },
39
+ },
40
+ {
41
+ method: 'GET',
42
+ path: '/getInfos',
43
+ handler: 'documentation.getInfos',
44
+ config: {
45
+ policies: [
46
+ { name: 'admin::hasPermissions', config: { actions: ['plugin::documentation.read'] } },
47
+ ],
48
+ },
49
+ },
50
+ {
51
+ method: 'POST',
52
+ path: '/regenerateDoc',
53
+ handler: 'documentation.regenerateDoc',
54
+ config: {
55
+ policies: [
56
+ {
57
+ name: 'admin::hasPermissions',
58
+ config: { actions: ['plugin::documentation.settings.regenerate'] },
59
+ },
60
+ ],
61
+ },
62
+ },
63
+ {
64
+ method: 'PUT',
65
+ path: '/updateSettings',
66
+ handler: 'documentation.updateSettings',
67
+ config: {
68
+ policies: [
69
+ {
70
+ name: 'admin::hasPermissions',
71
+ config: { actions: ['plugin::documentation.settings.update'] },
72
+ },
73
+ ],
74
+ },
75
+ },
76
+ {
77
+ method: 'DELETE',
78
+ path: '/deleteDoc/:version',
79
+ handler: 'documentation.deleteDoc',
80
+ config: {
81
+ policies: [],
82
+ },
83
+ },
84
+ ];
@@ -0,0 +1,210 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const _ = require('lodash');
6
+ const { getAbsoluteServerUrl } = require('@strapi/utils');
7
+
8
+ const defaultPluginConfig = require('../config/default-plugin-config');
9
+ const { builApiEndpointPath, buildComponentSchema } = require('./helpers');
10
+
11
+ module.exports = ({ strapi }) => {
12
+ const config = strapi.config.get('plugin.documentation');
13
+
14
+ const registeredDocs = [];
15
+
16
+ return {
17
+ registerDoc(doc) {
18
+ let registeredDoc = doc;
19
+ // parseYaml
20
+ if (typeof doc === 'string') {
21
+ registeredDoc = require('yaml').parse(registeredDoc);
22
+ }
23
+ // receive an object we can register it directly
24
+ registeredDocs.push(registeredDoc);
25
+ },
26
+ getDocumentationVersion() {
27
+ return _.get(config, 'info.version');
28
+ },
29
+
30
+ getFullDocumentationPath() {
31
+ return path.join(strapi.dirs.app.extensions, 'documentation', 'documentation');
32
+ },
33
+
34
+ getCustomDocumentationPath() {
35
+ // ??
36
+ return path.join(strapi.dirs.app.extensions, 'documentation', 'config', 'settings.json');
37
+ },
38
+
39
+ getDocumentationVersions() {
40
+ return fs
41
+ .readdirSync(this.getFullDocumentationPath())
42
+ .map((version) => {
43
+ try {
44
+ const doc = JSON.parse(
45
+ fs.readFileSync(
46
+ path.resolve(this.getFullDocumentationPath(), version, 'full_documentation.json')
47
+ )
48
+ );
49
+ const generatedDate = _.get(doc, ['info', 'x-generation-date'], null);
50
+
51
+ return { version, generatedDate, url: '' };
52
+ } catch (err) {
53
+ return null;
54
+ }
55
+ })
56
+ .filter((x) => x);
57
+ },
58
+
59
+ /**
60
+ * Returns settings stored in core-store
61
+ */
62
+ async getDocumentationAccess() {
63
+ const { restrictedAccess } = await strapi
64
+ .store({
65
+ environment: '',
66
+ type: 'plugin',
67
+ name: 'documentation',
68
+ key: 'config',
69
+ })
70
+ .get();
71
+
72
+ return { restrictedAccess };
73
+ },
74
+
75
+ /**
76
+ * @description - Gets the path for an api or plugin
77
+ *
78
+ * @param {object} api
79
+ * @property {string} api.name - Name of the api
80
+ * @property {string} api.getter - api | plugin
81
+ *
82
+ * @returns path to the api | plugin
83
+ */
84
+ getApiDocumentationPath(api) {
85
+ if (api.getter === 'plugin') {
86
+ return path.join(strapi.dirs.app.extensions, api.name, 'documentation');
87
+ }
88
+
89
+ return path.join(strapi.dirs.app.api, api.name, 'documentation');
90
+ },
91
+
92
+ async deleteDocumentation(version) {
93
+ const apis = this.getPluginAndApiInfo();
94
+ for (const api of apis) {
95
+ await fs.remove(path.join(this.getApiDocumentationPath(api), version));
96
+ }
97
+
98
+ await fs.remove(path.join(this.getFullDocumentationPath(), version));
99
+ },
100
+
101
+ getPluginAndApiInfo() {
102
+ const plugins = _.get(config, 'x-strapi-config.plugins');
103
+ const pluginsToDocument = plugins.map((plugin) => {
104
+ return {
105
+ name: plugin,
106
+ getter: 'plugin',
107
+ ctNames: Object.keys(strapi.plugin(plugin).contentTypes),
108
+ };
109
+ });
110
+
111
+ const apisToDocument = Object.keys(strapi.api).map((api) => {
112
+ return {
113
+ name: api,
114
+ getter: 'api',
115
+ ctNames: Object.keys(strapi.api[api].contentTypes),
116
+ };
117
+ });
118
+
119
+ return [...apisToDocument, ...pluginsToDocument];
120
+ },
121
+
122
+ async getCustomConfig() {
123
+ const customConfigPath = this.getCustomDocumentationPath();
124
+ const pathExists = await fs.pathExists(customConfigPath);
125
+ if (pathExists) {
126
+ return fs.readJson(customConfigPath);
127
+ }
128
+
129
+ return {};
130
+ },
131
+
132
+ /**
133
+ * @description - Creates the Swagger json files
134
+ */
135
+ async generateFullDoc(version = this.getDocumentationVersion()) {
136
+ let paths = {};
137
+ let schemas = {};
138
+ const apis = this.getPluginAndApiInfo();
139
+ for (const api of apis) {
140
+ const apiName = api.name;
141
+ const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
142
+
143
+ const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
144
+
145
+ const apiPath = builApiEndpointPath(api);
146
+
147
+ if (!apiPath) {
148
+ continue;
149
+ }
150
+
151
+ await fs.ensureFile(apiDocPath);
152
+ await fs.writeJson(apiDocPath, apiPath, { spaces: 2 });
153
+
154
+ const componentSchema = buildComponentSchema(api);
155
+
156
+ schemas = {
157
+ ...schemas,
158
+ ...componentSchema,
159
+ };
160
+
161
+ paths = { ...paths, ...apiPath };
162
+ }
163
+
164
+ const fullDocJsonPath = path.join(
165
+ this.getFullDocumentationPath(),
166
+ version,
167
+ 'full_documentation.json'
168
+ );
169
+
170
+ const defaultConfig = _.cloneDeep(defaultPluginConfig);
171
+
172
+ const serverUrl = getAbsoluteServerUrl(strapi.config);
173
+ const apiPath = strapi.config.get('api.rest.prefix');
174
+
175
+ _.set(defaultConfig, 'servers', [
176
+ {
177
+ url: `${serverUrl}${apiPath}`,
178
+ description: 'Development server',
179
+ },
180
+ ]);
181
+ _.set(defaultConfig, ['info', 'x-generation-date'], new Date().toISOString());
182
+ _.set(defaultConfig, ['info', 'version'], version);
183
+ _.merge(defaultConfig.components, { schemas });
184
+
185
+ const customConfig = await this.getCustomConfig();
186
+ const config = _.merge(defaultConfig, customConfig);
187
+
188
+ const finalDoc = { ...config, paths };
189
+
190
+ registeredDocs.forEach((doc) => {
191
+ // Add tags
192
+ finalDoc.tags = finalDoc.tags || [];
193
+ finalDoc.tags.push(...(doc.tags || []));
194
+
195
+ // Add Paths
196
+ _.assign(finalDoc.paths, doc.paths);
197
+
198
+ // Add components
199
+ _.forEach(doc.components || {}, (val, key) => {
200
+ finalDoc.components[key] = finalDoc.components[key] || {};
201
+
202
+ _.assign(finalDoc.components[key], val);
203
+ });
204
+ });
205
+
206
+ await fs.ensureFile(fullDocJsonPath);
207
+ await fs.writeJson(fullDocJsonPath, finalDoc, { spaces: 2 });
208
+ },
209
+ };
210
+ };