@strapi/plugin-documentation 4.2.0-beta.2 → 4.2.0

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.
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const strapi = {
4
+ plugins: {
5
+ 'users-permissions': {
6
+ contentTypes: {
7
+ role: {
8
+ attributes: {
9
+ name: {
10
+ type: 'string',
11
+ },
12
+ },
13
+ },
14
+ },
15
+ routes: {
16
+ 'content-api': {
17
+ routes: [],
18
+ },
19
+ },
20
+ },
21
+ },
22
+ api: {
23
+ restaurant: {
24
+ contentTypes: {
25
+ restaurant: {
26
+ attributes: {
27
+ name: {
28
+ type: 'string',
29
+ },
30
+ },
31
+ },
32
+ },
33
+ routes: {
34
+ restaurant: { routes: [] },
35
+ },
36
+ },
37
+ },
38
+ contentType: () => ({ info: {}, attributes: { test: { type: 'string' } } }),
39
+ };
40
+
41
+ module.exports = strapi;
@@ -0,0 +1,271 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const buildComponentSchema = require('../server/services/helpers/build-component-schema');
5
+ const strapi = require('../__mocks__/strapi');
6
+
7
+ describe('Build Component Schema', () => {
8
+ beforeEach(() => {
9
+ // Reset the mocked strapi instance
10
+ global.strapi = _.cloneDeep(strapi);
11
+ });
12
+
13
+ it('builds the Response schema', () => {
14
+ const apiMocks = [
15
+ {
16
+ name: 'users-permissions',
17
+ getter: 'plugin',
18
+ ctNames: ['role'],
19
+ },
20
+ { name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
21
+ ];
22
+
23
+ let schemas = {};
24
+ for (const mock of apiMocks) {
25
+ schemas = {
26
+ ...schemas,
27
+ ...buildComponentSchema(mock),
28
+ };
29
+ }
30
+
31
+ const schemaNames = Object.keys(schemas);
32
+ const [pluginResponseName, apiResponseName] = Object.keys(schemas);
33
+ const [pluginResponseValue, apiResponseValue] = Object.values(schemas);
34
+
35
+ const expectedShape = {
36
+ type: 'object',
37
+ properties: {
38
+ data: {
39
+ type: 'object',
40
+ properties: {
41
+ id: { type: 'string' },
42
+ attributes: { type: 'object', properties: { test: { type: 'string' } } },
43
+ },
44
+ },
45
+ meta: { type: 'object' },
46
+ },
47
+ };
48
+
49
+ expect(schemaNames.length).toBe(2);
50
+ expect(pluginResponseName).toBe('UsersPermissionsRoleResponse');
51
+ expect(apiResponseName).toBe('RestaurantResponse');
52
+ expect(pluginResponseValue).toStrictEqual(expectedShape);
53
+ expect(apiResponseValue).toStrictEqual(expectedShape);
54
+ });
55
+
56
+ it('builds the ResponseList schema', () => {
57
+ global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
58
+ { method: 'GET', path: '/test', handler: 'test.find' },
59
+ ];
60
+ global.strapi.api.restaurant.routes.restaurant.routes = [
61
+ { method: 'GET', path: '/test', handler: 'test.find' },
62
+ ];
63
+
64
+ const apiMocks = [
65
+ {
66
+ name: 'users-permissions',
67
+ getter: 'plugin',
68
+ ctNames: ['role'],
69
+ },
70
+ { name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
71
+ ];
72
+
73
+ let schemas = {};
74
+ for (const mock of apiMocks) {
75
+ schemas = {
76
+ ...schemas,
77
+ ...buildComponentSchema(mock),
78
+ };
79
+ }
80
+
81
+ const schemaNames = Object.keys(schemas);
82
+ const pluginListResponseValue = schemas['UsersPermissionsRoleListResponse'];
83
+ const apiListResponseValue = schemas['RestaurantListResponse'];
84
+
85
+ const expectedShape = {
86
+ type: 'object',
87
+ properties: {
88
+ data: {
89
+ type: 'array',
90
+ items: {
91
+ type: 'object',
92
+ properties: {
93
+ id: { type: 'string' },
94
+ attributes: { type: 'object', properties: { test: { type: 'string' } } },
95
+ },
96
+ },
97
+ },
98
+ meta: {
99
+ type: 'object',
100
+ properties: {
101
+ pagination: {
102
+ properties: {
103
+ page: { type: 'integer' },
104
+ pageSize: { type: 'integer', minimum: 25 },
105
+ pageCount: { type: 'integer', maximum: 1 },
106
+ total: { type: 'integer' },
107
+ },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ };
113
+
114
+ expect(schemaNames.length).toBe(4);
115
+ expect(schemaNames.includes('UsersPermissionsRoleListResponse')).toBe(true);
116
+ expect(schemaNames.includes('RestaurantListResponse')).toBe(true);
117
+ expect(pluginListResponseValue).toStrictEqual(expectedShape);
118
+ expect(apiListResponseValue).toStrictEqual(expectedShape);
119
+ });
120
+
121
+ it('builds the Request schema', () => {
122
+ global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
123
+ { method: 'POST', path: '/test', handler: 'test.create' },
124
+ ];
125
+ global.strapi.api.restaurant.routes.restaurant.routes = [
126
+ { method: 'POST', path: '/test', handler: 'test.create' },
127
+ ];
128
+
129
+ const apiMocks = [
130
+ {
131
+ name: 'users-permissions',
132
+ getter: 'plugin',
133
+ ctNames: ['role'],
134
+ },
135
+ { name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
136
+ ];
137
+
138
+ let schemas = {};
139
+ for (const mock of apiMocks) {
140
+ schemas = {
141
+ ...schemas,
142
+ ...buildComponentSchema(mock),
143
+ };
144
+ }
145
+
146
+ const schemaNames = Object.keys(schemas);
147
+ const pluginListResponseValue = schemas['UsersPermissionsRoleRequest'];
148
+ const apiListResponseValue = schemas['RestaurantRequest'];
149
+
150
+ const expectedShape = {
151
+ type: 'object',
152
+ required: ['data'],
153
+ properties: {
154
+ data: {
155
+ required: [],
156
+ type: 'object',
157
+ properties: { test: { type: 'string' } },
158
+ },
159
+ },
160
+ };
161
+
162
+ expect(schemaNames.length).toBe(4);
163
+ expect(schemaNames.includes('UsersPermissionsRoleRequest')).toBe(true);
164
+ expect(schemaNames.includes('RestaurantRequest')).toBe(true);
165
+ expect(pluginListResponseValue).toStrictEqual(expectedShape);
166
+ expect(apiListResponseValue).toStrictEqual(expectedShape);
167
+ });
168
+
169
+ it('builds the LocalizationResponse schema', () => {
170
+ global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
171
+ { method: 'GET', path: '/localizations', handler: 'test' },
172
+ ];
173
+ global.strapi.api.restaurant.routes.restaurant.routes = [
174
+ { method: 'GET', path: '/localizations', handler: 'test' },
175
+ ];
176
+
177
+ const apiMocks = [
178
+ {
179
+ name: 'users-permissions',
180
+ getter: 'plugin',
181
+ ctNames: ['role'],
182
+ },
183
+ { name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
184
+ ];
185
+
186
+ let schemas = {};
187
+ for (const mock of apiMocks) {
188
+ schemas = {
189
+ ...schemas,
190
+ ...buildComponentSchema(mock),
191
+ };
192
+ }
193
+
194
+ const schemaNames = Object.keys(schemas);
195
+ const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationResponse'];
196
+ const apiListResponseValue = schemas['RestaurantLocalizationResponse'];
197
+
198
+ const expectedShape = {
199
+ type: 'object',
200
+ properties: {
201
+ id: { type: 'string' },
202
+ test: { type: 'string' },
203
+ },
204
+ };
205
+
206
+ expect(schemaNames.length).toBe(4);
207
+ expect(schemaNames.includes('UsersPermissionsRoleLocalizationResponse')).toBe(true);
208
+ expect(schemaNames.includes('RestaurantLocalizationResponse')).toBe(true);
209
+ expect(pluginListResponseValue).toStrictEqual(expectedShape);
210
+ expect(apiListResponseValue).toStrictEqual(expectedShape);
211
+ });
212
+
213
+ it('builds the LocalizationRequest schema', () => {
214
+ global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
215
+ { method: 'POST', path: '/localizations', handler: 'test' },
216
+ ];
217
+ global.strapi.api.restaurant.routes.restaurant.routes = [
218
+ { method: 'POST', path: '/localizations', handler: 'test' },
219
+ ];
220
+
221
+ const apiMocks = [
222
+ {
223
+ name: 'users-permissions',
224
+ getter: 'plugin',
225
+ ctNames: ['role'],
226
+ },
227
+ { name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
228
+ ];
229
+
230
+ let schemas = {};
231
+ for (const mock of apiMocks) {
232
+ schemas = {
233
+ ...schemas,
234
+ ...buildComponentSchema(mock),
235
+ };
236
+ }
237
+
238
+ const schemaNames = Object.keys(schemas);
239
+ const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationRequest'];
240
+ const apiListResponseValue = schemas['RestaurantLocalizationRequest'];
241
+
242
+ const expectedShape = {
243
+ type: 'object',
244
+ required: ['locale'],
245
+ properties: { test: { type: 'string' } },
246
+ };
247
+
248
+ expect(schemaNames.length).toBe(8);
249
+ expect(schemaNames.includes('UsersPermissionsRoleLocalizationRequest')).toBe(true);
250
+ expect(schemaNames.includes('RestaurantLocalizationRequest')).toBe(true);
251
+ expect(pluginListResponseValue).toStrictEqual(expectedShape);
252
+ expect(apiListResponseValue).toStrictEqual(expectedShape);
253
+ });
254
+
255
+ it('creates the correct name given multiple content types', () => {
256
+ const apiMock = {
257
+ name: 'users-permissions',
258
+ getter: 'plugin',
259
+ ctNames: ['permission', 'role', 'user'],
260
+ };
261
+
262
+ const schemas = buildComponentSchema(apiMock);
263
+ const schemaNames = Object.keys(schemas);
264
+ const [permission, role, user] = schemaNames;
265
+
266
+ expect(schemaNames.length).toBe(3);
267
+ expect(permission).toBe('UsersPermissionsPermissionResponse');
268
+ expect(role).toBe('UsersPermissionsRoleResponse');
269
+ expect(user).toBe('UsersPermissionsUserResponse');
270
+ });
271
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-documentation",
3
- "version": "4.2.0-beta.2",
3
+ "version": "4.2.0",
4
4
  "description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,8 +24,8 @@
24
24
  "test": "echo \"no tests yet\""
25
25
  },
26
26
  "dependencies": {
27
- "@strapi/helper-plugin": "4.2.0-beta.2",
28
- "@strapi/utils": "4.2.0-beta.2",
27
+ "@strapi/helper-plugin": "4.2.0",
28
+ "@strapi/utils": "4.2.0",
29
29
  "bcryptjs": "2.4.3",
30
30
  "cheerio": "^1.0.0-rc.5",
31
31
  "fs-extra": "10.0.0",
@@ -42,13 +42,13 @@
42
42
  "react-router-dom": "5.2.0",
43
43
  "redux": "^4.0.1",
44
44
  "reselect": "^4.0.0",
45
- "swagger-ui-dist": "3.47.1"
45
+ "swagger-ui-dist": "4.11.1"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@strapi/strapi": "^4.0.0"
49
49
  },
50
50
  "engines": {
51
- "node": ">=12.22.0 <=16.x.x",
51
+ "node": ">=14.19.1 <=16.x.x",
52
52
  "npm": ">=6.0.0"
53
53
  },
54
54
  "strapi": {
@@ -57,5 +57,5 @@
57
57
  "description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
58
58
  "kind": "plugin"
59
59
  },
60
- "gitHead": "bff73257e7695d6f361c91dda8cc810a2bb70b6e"
60
+ "gitHead": "12c8ee3b2d95fe417de4d939db0311a0513bd8da"
61
61
  }
@@ -21,7 +21,7 @@ module.exports = {
21
21
  path: '/documentation',
22
22
  showGeneratedFiles: true,
23
23
  generateDefaultResponse: true,
24
- plugins: ['email', 'upload'],
24
+ plugins: ['email', 'upload', 'users-permissions'],
25
25
  },
26
26
  servers: [],
27
27
  externalDocs: {
@@ -41,5 +41,34 @@ module.exports = {
41
41
  bearerFormat: 'JWT',
42
42
  },
43
43
  },
44
+ schemas: {
45
+ Error: {
46
+ type: 'object',
47
+ required: ['error'],
48
+ properties: {
49
+ data: {
50
+ nullable: true,
51
+ oneOf: [{ type: 'object' }, { type: 'array', items: [] }],
52
+ },
53
+ error: {
54
+ type: 'object',
55
+ properties: {
56
+ status: {
57
+ type: 'integer',
58
+ },
59
+ name: {
60
+ type: 'string',
61
+ },
62
+ message: {
63
+ type: 'string',
64
+ },
65
+ details: {
66
+ type: 'object',
67
+ },
68
+ },
69
+ },
70
+ },
71
+ },
72
+ },
44
73
  },
45
74
  };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const defaultDocumentationConfig = require('./default-config');
3
+ const defaultPluginConfig = require('./default-plugin-config');
4
4
 
5
5
  module.exports = {
6
- default: defaultDocumentationConfig,
6
+ default: defaultPluginConfig,
7
7
  };
@@ -49,7 +49,7 @@ module.exports = {
49
49
  .getDocumentationVersion();
50
50
 
51
51
  const openAPISpecsPath = path.join(
52
- strapi.dirs.app.extensions,
52
+ strapi.dirs.extensions,
53
53
  'documentation',
54
54
  'documentation',
55
55
  version,
@@ -69,7 +69,7 @@ module.exports = {
69
69
 
70
70
  try {
71
71
  const layoutPath = path.resolve(
72
- strapi.dirs.app.extensions,
72
+ strapi.dirs.extensions,
73
73
  'documentation',
74
74
  'public',
75
75
  'index.html'
@@ -81,11 +81,7 @@ module.exports = {
81
81
  ctx.url = path.basename(`${ctx.url}/index.html`);
82
82
 
83
83
  try {
84
- const staticFolder = path.resolve(
85
- strapi.dirs.app.extensions,
86
- 'documentation',
87
- 'public'
88
- );
84
+ const staticFolder = path.resolve(strapi.dirs.extensions, 'documentation', 'public');
89
85
  return koaStatic(staticFolder)(ctx, next);
90
86
  } catch (e) {
91
87
  strapi.log.error(e);
@@ -120,7 +116,7 @@ module.exports = {
120
116
 
121
117
  try {
122
118
  const layoutPath = path.resolve(
123
- strapi.dirs.app.extensions,
119
+ strapi.dirs.extensions,
124
120
  'documentation',
125
121
  'public',
126
122
  'login.html'
@@ -131,7 +127,7 @@ module.exports = {
131
127
  ctx.url = path.basename(`${ctx.url}/login.html`);
132
128
 
133
129
  try {
134
- const staticFolder = path.resolve(strapi.dirs.app.extensions, 'documentation', 'public');
130
+ const staticFolder = path.resolve(strapi.dirs.extensions, 'documentation', 'public');
135
131
  return koaStatic(staticFolder)(ctx, next);
136
132
  } catch (e) {
137
133
  strapi.log.error(e);
@@ -5,8 +5,8 @@ const fs = require('fs-extra');
5
5
  const _ = require('lodash');
6
6
  const { getAbsoluteServerUrl } = require('@strapi/utils');
7
7
 
8
- const { builApiEndpointPath } = require('../utils/builders');
9
- const defaultConfig = require('../config/default-config');
8
+ const defaultPluginConfig = require('../config/default-plugin-config');
9
+ const { builApiEndpointPath, buildComponentSchema } = require('./helpers');
10
10
 
11
11
  module.exports = ({ strapi }) => {
12
12
  const config = strapi.config.get('plugin.documentation');
@@ -17,12 +17,11 @@ module.exports = ({ strapi }) => {
17
17
  },
18
18
 
19
19
  getFullDocumentationPath() {
20
- return path.join(strapi.dirs.app.extensions, 'documentation', 'documentation');
20
+ return path.join(strapi.dirs.extensions, 'documentation', 'documentation');
21
21
  },
22
22
 
23
23
  getCustomDocumentationPath() {
24
- // ??
25
- return path.join(strapi.dirs.app.extensions, 'documentation', 'config', 'settings.json');
24
+ return path.join(strapi.dirs.extensions, 'documentation', 'config', 'settings.json');
26
25
  },
27
26
 
28
27
  getDocumentationVersions() {
@@ -72,10 +71,10 @@ module.exports = ({ strapi }) => {
72
71
  */
73
72
  getApiDocumentationPath(api) {
74
73
  if (api.getter === 'plugin') {
75
- return path.join(strapi.dirs.app.extensions, api.name, 'documentation');
74
+ return path.join(strapi.dirs.extensions, api.name, 'documentation');
76
75
  }
77
76
 
78
- return path.join(strapi.dirs.app.api, api.name, 'documentation');
77
+ return path.join(strapi.dirs.api, api.name, 'documentation');
79
78
  },
80
79
 
81
80
  async deleteDocumentation(version) {
@@ -108,7 +107,7 @@ module.exports = ({ strapi }) => {
108
107
  return [...apisToDocument, ...pluginsToDocument];
109
108
  },
110
109
 
111
- async getCustomSettings() {
110
+ async getCustomConfig() {
112
111
  const customConfigPath = this.getCustomDocumentationPath();
113
112
  const pathExists = await fs.pathExists(customConfigPath);
114
113
  if (pathExists) {
@@ -123,23 +122,31 @@ module.exports = ({ strapi }) => {
123
122
  */
124
123
  async generateFullDoc(version = this.getDocumentationVersion()) {
125
124
  let paths = {};
126
-
125
+ let schemas = {};
127
126
  const apis = this.getPluginAndApiInfo();
128
127
  for (const api of apis) {
129
128
  const apiName = api.name;
130
129
  const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
131
130
 
132
131
  const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
133
- const apiPathsObject = builApiEndpointPath(api);
134
132
 
135
- if (!apiPathsObject) {
133
+ const apiPath = builApiEndpointPath(api);
134
+
135
+ if (!apiPath) {
136
136
  continue;
137
137
  }
138
138
 
139
139
  await fs.ensureFile(apiDocPath);
140
- await fs.writeJson(apiDocPath, apiPathsObject, { spaces: 2 });
140
+ await fs.writeJson(apiDocPath, apiPath, { spaces: 2 });
141
141
 
142
- paths = { ...paths, ...apiPathsObject.paths };
142
+ const componentSchema = buildComponentSchema(api);
143
+
144
+ schemas = {
145
+ ...schemas,
146
+ ...componentSchema,
147
+ };
148
+
149
+ paths = { ...paths, ...apiPath };
143
150
  }
144
151
 
145
152
  const fullDocJsonPath = path.join(
@@ -148,27 +155,26 @@ module.exports = ({ strapi }) => {
148
155
  'full_documentation.json'
149
156
  );
150
157
 
151
- const defaultSettings = _.cloneDeep(defaultConfig);
158
+ const defaultConfig = _.cloneDeep(defaultPluginConfig);
152
159
 
153
160
  const serverUrl = getAbsoluteServerUrl(strapi.config);
154
161
  const apiPath = strapi.config.get('api.rest.prefix');
155
162
 
156
- _.set(defaultSettings, 'servers', [
163
+ _.set(defaultConfig, 'servers', [
157
164
  {
158
165
  url: `${serverUrl}${apiPath}`,
159
166
  description: 'Development server',
160
167
  },
161
168
  ]);
169
+ _.set(defaultConfig, ['info', 'x-generation-date'], new Date().toISOString());
170
+ _.set(defaultConfig, ['info', 'version'], version);
171
+ _.merge(defaultConfig.components, { schemas });
162
172
 
163
- _.set(defaultSettings, ['info', 'x-generation-date'], new Date().toISOString());
164
- _.set(defaultSettings, ['info', 'version'], version);
165
-
166
- const customSettings = await this.getCustomSettings();
167
-
168
- const settings = _.merge(defaultSettings, customSettings);
173
+ const customConfig = await this.getCustomConfig();
174
+ const config = _.merge(defaultConfig, customConfig);
169
175
 
170
176
  await fs.ensureFile(fullDocJsonPath);
171
- await fs.writeJson(fullDocJsonPath, { ...settings, paths }, { spaces: 2 });
177
+ await fs.writeJson(fullDocJsonPath, { ...config, paths }, { spaces: 2 });
172
178
  },
173
179
  };
174
180
  };