@strapi/plugin-documentation 0.0.0-113ff29005

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 (72) 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/th.json +24 -0
  37. package/admin/src/translations/tr.json +20 -0
  38. package/admin/src/translations/uk.json +23 -0
  39. package/admin/src/translations/vi.json +24 -0
  40. package/admin/src/translations/zh-Hans.json +28 -0
  41. package/admin/src/translations/zh.json +24 -0
  42. package/admin/src/utils/getTrad.js +5 -0
  43. package/admin/src/utils/index.js +2 -0
  44. package/admin/src/utils/openWithNewTab.js +20 -0
  45. package/package.json +66 -0
  46. package/server/bootstrap.js +54 -0
  47. package/server/config/default-plugin-config.js +74 -0
  48. package/server/config/index.js +7 -0
  49. package/server/controllers/documentation.js +241 -0
  50. package/server/controllers/index.js +7 -0
  51. package/server/index.js +17 -0
  52. package/server/middlewares/documentation.js +25 -0
  53. package/server/middlewares/index.js +7 -0
  54. package/server/middlewares/restrict-access.js +24 -0
  55. package/server/public/index.html +70 -0
  56. package/server/public/login.html +145 -0
  57. package/server/register.js +11 -0
  58. package/server/routes/index.js +84 -0
  59. package/server/services/documentation.js +209 -0
  60. package/server/services/helpers/build-api-endpoint-path.js +185 -0
  61. package/server/services/helpers/build-component-schema.js +159 -0
  62. package/server/services/helpers/index.js +9 -0
  63. package/server/services/helpers/utils/clean-schema-attributes.js +212 -0
  64. package/server/services/helpers/utils/get-api-responses.js +105 -0
  65. package/server/services/helpers/utils/get-schema-data.js +32 -0
  66. package/server/services/helpers/utils/loop-content-type-names.js +53 -0
  67. package/server/services/helpers/utils/pascal-case.js +9 -0
  68. package/server/services/helpers/utils/query-params.js +84 -0
  69. package/server/services/helpers/utils/routes.js +10 -0
  70. package/server/services/index.js +7 -0
  71. package/strapi-admin.js +3 -0
  72. package/strapi-server.js +3 -0
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+
5
+ const cleanSchemaAttributes = require('./utils/clean-schema-attributes');
6
+ const loopContentTypeNames = require('./utils/loop-content-type-names');
7
+ const pascalCase = require('./utils/pascal-case');
8
+ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
9
+
10
+ /**
11
+ * @decription Get all open api schema objects for a given content type
12
+ *
13
+ * @param {object} apiInfo
14
+ * @property {string} apiInfo.uniqueName - Api name | Api name + Content type name
15
+ * @property {object} apiInfo.attributes - Attributes on content type
16
+ * @property {object} apiInfo.routeInfo - The routes for the api
17
+ *
18
+ * @returns {object} Open API schemas
19
+ */
20
+ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
21
+ // Store response and request schemas in an object
22
+ let schemas = {};
23
+ // Get all the route methods
24
+ const routeMethods = routeInfo.routes.map((route) => route.method);
25
+ // Check for localized paths
26
+ const hasLocalizationPath = routeInfo.routes.filter((route) =>
27
+ isLocalizedPath(route.path)
28
+ ).length;
29
+ // When the route methods contain any post or put requests
30
+ if (routeMethods.includes('POST') || routeMethods.includes('PUT')) {
31
+ const attributesToOmit = [
32
+ 'createdAt',
33
+ 'updatedAt',
34
+ 'publishedAt',
35
+ 'publishedBy',
36
+ 'updatedBy',
37
+ 'createdBy',
38
+ 'localizations',
39
+ ];
40
+ const attributesForRequest = _.omit(attributes, attributesToOmit);
41
+
42
+ // Get a list of required attribute names
43
+ const requiredAttributes = Object.entries(attributesForRequest).reduce((acc, attribute) => {
44
+ const [attributeKey, attributeValue] = attribute;
45
+
46
+ if (attributeValue.required) {
47
+ acc.push(attributeKey);
48
+ }
49
+
50
+ return acc;
51
+ }, []);
52
+
53
+ if (hasLocalizationPath) {
54
+ schemas = {
55
+ ...schemas,
56
+ [`${pascalCase(uniqueName)}LocalizationRequest`]: {
57
+ required: [...requiredAttributes, 'locale'],
58
+ type: 'object',
59
+ properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
60
+ },
61
+ };
62
+ }
63
+
64
+ // Build the request schema
65
+ schemas = {
66
+ ...schemas,
67
+ [`${pascalCase(uniqueName)}Request`]: {
68
+ type: 'object',
69
+ required: ['data'],
70
+ properties: {
71
+ data: {
72
+ required: requiredAttributes,
73
+ type: 'object',
74
+ properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
75
+ },
76
+ },
77
+ },
78
+ };
79
+ }
80
+
81
+ if (hasLocalizationPath) {
82
+ schemas = {
83
+ ...schemas,
84
+ [`${pascalCase(uniqueName)}LocalizationResponse`]: {
85
+ type: 'object',
86
+ properties: {
87
+ id: { type: 'string' },
88
+ ...cleanSchemaAttributes(attributes),
89
+ },
90
+ },
91
+ };
92
+ }
93
+
94
+ // Check for routes that need to return a list
95
+ const hasListOfEntities = routeInfo.routes.filter((route) => hasFindMethod(route.handler)).length;
96
+ if (hasListOfEntities) {
97
+ // Build the list response schema
98
+ schemas = {
99
+ ...schemas,
100
+ [`${pascalCase(uniqueName)}ListResponse`]: {
101
+ type: 'object',
102
+ properties: {
103
+ data: {
104
+ type: 'array',
105
+ items: {
106
+ type: 'object',
107
+ properties: {
108
+ id: { type: 'string' },
109
+ attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
110
+ },
111
+ },
112
+ },
113
+ meta: {
114
+ type: 'object',
115
+ properties: {
116
+ pagination: {
117
+ properties: {
118
+ page: { type: 'integer' },
119
+ pageSize: { type: 'integer', minimum: 25 },
120
+ pageCount: { type: 'integer', maximum: 1 },
121
+ total: { type: 'integer' },
122
+ },
123
+ },
124
+ },
125
+ },
126
+ },
127
+ },
128
+ };
129
+ }
130
+
131
+ // Build the response schema
132
+ schemas = {
133
+ ...schemas,
134
+ [`${pascalCase(uniqueName)}Response`]: {
135
+ type: 'object',
136
+ properties: {
137
+ data: {
138
+ type: 'object',
139
+ properties: {
140
+ id: { type: 'string' },
141
+ attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
142
+ },
143
+ },
144
+ meta: { type: 'object' },
145
+ },
146
+ },
147
+ };
148
+
149
+ return schemas;
150
+ };
151
+
152
+ const buildComponentSchema = (api) => {
153
+ // A reusable loop for building paths and component schemas
154
+ // Uses the api param to build a new set of params for each content type
155
+ // Passes these new params to the function provided
156
+ return loopContentTypeNames(api, getAllSchemasForContentType);
157
+ };
158
+
159
+ module.exports = buildComponentSchema;
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const builApiEndpointPath = require('./build-api-endpoint-path');
4
+ const buildComponentSchema = require('./build-component-schema');
5
+
6
+ module.exports = {
7
+ builApiEndpointPath,
8
+ buildComponentSchema,
9
+ };
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const getSchemaData = require('./get-schema-data');
5
+
6
+ /**
7
+ * @description - Converts types found on attributes to OpenAPI acceptable data types
8
+ *
9
+ * @param {object} attributes - The attributes found on a contentType
10
+ * @param {{ typeMap: Map, isRequest: boolean }} opts
11
+ * @returns Attributes using OpenAPI acceptable data types
12
+ */
13
+ const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = false } = {}) => {
14
+ const attributesCopy = _.cloneDeep(attributes);
15
+
16
+ for (const prop of Object.keys(attributesCopy)) {
17
+ const attribute = attributesCopy[prop];
18
+ if (attribute.default) {
19
+ delete attributesCopy[prop].default;
20
+ }
21
+
22
+ switch (attribute.type) {
23
+ case 'password': {
24
+ if (!isRequest) {
25
+ delete attributesCopy[prop];
26
+ break;
27
+ }
28
+
29
+ attributesCopy[prop] = { type: 'string', format: 'password', example: '*******' };
30
+ break;
31
+ }
32
+ case 'email': {
33
+ attributesCopy[prop] = { type: 'string', format: 'email' };
34
+ break;
35
+ }
36
+ case 'string':
37
+ case 'text':
38
+ case 'richtext': {
39
+ attributesCopy[prop] = { type: 'string' };
40
+ break;
41
+ }
42
+ case 'timestamp': {
43
+ attributesCopy[prop] = { type: 'string', format: 'timestamp', example: Date.now() };
44
+ break;
45
+ }
46
+ case 'time': {
47
+ attributesCopy[prop] = { type: 'string', format: 'time', example: '12:54.000' };
48
+ break;
49
+ }
50
+ case 'date': {
51
+ attributesCopy[prop] = { type: 'string', format: 'date' };
52
+ break;
53
+ }
54
+ case 'datetime': {
55
+ attributesCopy[prop] = { type: 'string', format: 'date-time' };
56
+ break;
57
+ }
58
+ case 'boolean': {
59
+ attributesCopy[prop] = { type: 'boolean' };
60
+ break;
61
+ }
62
+ case 'enumeration': {
63
+ attributesCopy[prop] = { type: 'string', enum: attribute.enum };
64
+ break;
65
+ }
66
+ case 'decimal':
67
+ case 'float': {
68
+ attributesCopy[prop] = { type: 'number', format: 'float' };
69
+ break;
70
+ }
71
+ case 'integer': {
72
+ attributesCopy[prop] = { type: 'integer' };
73
+ break;
74
+ }
75
+ case 'biginteger': {
76
+ attributesCopy[prop] = { type: 'string', pattern: '^\\d*$', example: '123456789' };
77
+ break;
78
+ }
79
+ case 'json': {
80
+ attributesCopy[prop] = {};
81
+ break;
82
+ }
83
+ case 'uid': {
84
+ attributesCopy[prop] = { type: 'string' };
85
+ break;
86
+ }
87
+ case 'component': {
88
+ const componentAttributes = strapi.components[attribute.component].attributes;
89
+
90
+ if (attribute.repeatable) {
91
+ attributesCopy[prop] = {
92
+ type: 'array',
93
+ items: {
94
+ type: 'object',
95
+ properties: {
96
+ ...(isRequest ? {} : { id: { type: 'string' } }),
97
+ ...cleanSchemaAttributes(componentAttributes, { typeMap, isRequest }),
98
+ },
99
+ },
100
+ };
101
+ } else {
102
+ attributesCopy[prop] = {
103
+ type: 'object',
104
+ properties: {
105
+ ...(isRequest ? {} : { id: { type: 'string' } }),
106
+ ...cleanSchemaAttributes(componentAttributes, {
107
+ typeMap,
108
+ isRequest,
109
+ }),
110
+ },
111
+ };
112
+ }
113
+ break;
114
+ }
115
+ case 'dynamiczone': {
116
+ const components = attribute.components.map((component) => {
117
+ const componentAttributes = strapi.components[component].attributes;
118
+ return {
119
+ type: 'object',
120
+ properties: {
121
+ ...(isRequest ? {} : { id: { type: 'string' } }),
122
+ __component: { type: 'string' },
123
+ ...cleanSchemaAttributes(componentAttributes, { typeMap, isRequest }),
124
+ },
125
+ };
126
+ });
127
+
128
+ attributesCopy[prop] = {
129
+ type: 'array',
130
+ items: {
131
+ anyOf: components,
132
+ },
133
+ };
134
+ break;
135
+ }
136
+ case 'media': {
137
+ const imageAttributes = strapi.contentType('plugin::upload.file').attributes;
138
+ const isListOfEntities = attribute.multiple;
139
+
140
+ if (isRequest) {
141
+ const oneOfType = {
142
+ oneOf: [{ type: 'integer' }, { type: 'string' }],
143
+ example: 'string or id',
144
+ };
145
+
146
+ attributesCopy[prop] = isListOfEntities ? { type: 'array', items: oneOfType } : oneOfType;
147
+ break;
148
+ }
149
+
150
+ attributesCopy[prop] = {
151
+ type: 'object',
152
+ properties: {
153
+ data: getSchemaData(isListOfEntities, cleanSchemaAttributes(imageAttributes)),
154
+ },
155
+ };
156
+ break;
157
+ }
158
+
159
+ case 'relation': {
160
+ const isListOfEntities = attribute.relation.includes('ToMany');
161
+
162
+ if (isRequest) {
163
+ const oneOfType = {
164
+ oneOf: [{ type: 'integer' }, { type: 'string' }],
165
+ example: 'string or id',
166
+ };
167
+
168
+ attributesCopy[prop] = isListOfEntities ? { type: 'array', items: oneOfType } : oneOfType;
169
+ break;
170
+ }
171
+
172
+ if (prop === 'localizations') {
173
+ attributesCopy[prop] = {
174
+ type: 'array',
175
+ items: { type: 'object', properties: {} },
176
+ };
177
+ break;
178
+ }
179
+
180
+ if (!attribute.target || typeMap.has(attribute.target)) {
181
+ attributesCopy[prop] = {
182
+ type: 'object',
183
+ properties: { data: getSchemaData(isListOfEntities, {}) },
184
+ };
185
+ break;
186
+ }
187
+
188
+ typeMap.set(attribute.target, true);
189
+ const targetAttributes = strapi.contentType(attribute.target).attributes;
190
+
191
+ attributesCopy[prop] = {
192
+ type: 'object',
193
+ properties: {
194
+ data: getSchemaData(
195
+ isListOfEntities,
196
+ cleanSchemaAttributes(targetAttributes, { typeMap, isRequest })
197
+ ),
198
+ },
199
+ };
200
+
201
+ break;
202
+ }
203
+ default: {
204
+ throw new Error(`Invalid type ${attribute.type} while generating open api schema.`);
205
+ }
206
+ }
207
+ }
208
+
209
+ return attributesCopy;
210
+ };
211
+
212
+ module.exports = cleanSchemaAttributes;
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ const pascalCase = require('./pascal-case');
4
+
5
+ /**
6
+ * @description - Builds the Swagger response object for a given api
7
+ *
8
+ * @param {object} name - Name of the api or plugin
9
+ * @param {object} route - The current route
10
+ * @param {boolean} isListOfEntities - Checks for a list of entitities
11
+ *
12
+ * @returns The Swagger responses
13
+ */
14
+ const getApiResponse = ({
15
+ uniqueName,
16
+ route,
17
+ isListOfEntities = false,
18
+ isLocalizationPath = false,
19
+ }) => {
20
+ const getSchema = () => {
21
+ if (route.method === 'DELETE') {
22
+ return {
23
+ type: 'integer',
24
+ format: 'int64',
25
+ };
26
+ }
27
+
28
+ if (isLocalizationPath) {
29
+ return { $ref: `#/components/schemas/${pascalCase(uniqueName)}LocalizationResponse` };
30
+ }
31
+
32
+ if (isListOfEntities) {
33
+ return { $ref: `#/components/schemas/${pascalCase(uniqueName)}ListResponse` };
34
+ }
35
+
36
+ return { $ref: `#/components/schemas/${pascalCase(uniqueName)}Response` };
37
+ };
38
+
39
+ const schema = getSchema();
40
+
41
+ return {
42
+ responses: {
43
+ 200: {
44
+ description: 'OK',
45
+ content: {
46
+ 'application/json': {
47
+ schema,
48
+ },
49
+ },
50
+ },
51
+ 400: {
52
+ description: 'Bad Request',
53
+ content: {
54
+ 'application/json': {
55
+ schema: {
56
+ $ref: '#/components/schemas/Error',
57
+ },
58
+ },
59
+ },
60
+ },
61
+ 401: {
62
+ description: 'Unauthorized',
63
+ content: {
64
+ 'application/json': {
65
+ schema: {
66
+ $ref: '#/components/schemas/Error',
67
+ },
68
+ },
69
+ },
70
+ },
71
+ 403: {
72
+ description: 'Forbidden',
73
+ content: {
74
+ 'application/json': {
75
+ schema: {
76
+ $ref: '#/components/schemas/Error',
77
+ },
78
+ },
79
+ },
80
+ },
81
+ 404: {
82
+ description: 'Not Found',
83
+ content: {
84
+ 'application/json': {
85
+ schema: {
86
+ $ref: '#/components/schemas/Error',
87
+ },
88
+ },
89
+ },
90
+ },
91
+ 500: {
92
+ description: 'Internal Server Error',
93
+ content: {
94
+ 'application/json': {
95
+ schema: {
96
+ $ref: '#/components/schemas/Error',
97
+ },
98
+ },
99
+ },
100
+ },
101
+ },
102
+ };
103
+ };
104
+
105
+ module.exports = getApiResponse;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @description Determines the format of the data response
5
+ *
6
+ * @param {boolean} isListOfEntities - Checks for a multiple entities
7
+ * @param {object} attributes - The attributes found on a contentType
8
+
9
+ * @returns object | array of attributes
10
+ */
11
+ module.exports = (isListOfEntities, attributes) => {
12
+ if (isListOfEntities) {
13
+ return {
14
+ type: 'array',
15
+ items: {
16
+ type: 'object',
17
+ properties: {
18
+ id: { type: 'string' },
19
+ attributes: { type: 'object', properties: attributes },
20
+ },
21
+ },
22
+ };
23
+ }
24
+
25
+ return {
26
+ type: 'object',
27
+ properties: {
28
+ id: { type: 'string' },
29
+ attributes: { type: 'object', properties: attributes },
30
+ },
31
+ };
32
+ };
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+
5
+ /**
6
+ * @description A reusable loop for building api endpoint paths and component schemas
7
+ *
8
+ * @param {object} api - Api information to pass to the callback
9
+ * @param {function} callback - Logic to execute for the given api
10
+ *
11
+ * @returns {object}
12
+ */
13
+ const loopContentTypeNames = (api, callback) => {
14
+ let result = {};
15
+ for (const contentTypeName of api.ctNames) {
16
+ // Get the attributes found on the api's contentType
17
+ const uid = `${api.getter}::${api.name}.${contentTypeName}`;
18
+ const { attributes, info: contentTypeInfo } = strapi.contentType(uid);
19
+
20
+ // Get the routes for the current api
21
+ const routeInfo =
22
+ api.getter === 'plugin'
23
+ ? strapi.plugin(api.name).routes['content-api']
24
+ : strapi.api[api.name].routes[contentTypeName];
25
+
26
+ // Continue to next iteration if routeInfo is undefined
27
+ if (!routeInfo) continue;
28
+
29
+ // Uppercase the first letter of the api name
30
+ const apiName = _.upperFirst(api.name);
31
+
32
+ // Create a unique name if the api name and contentType name don't match
33
+ const uniqueName =
34
+ api.name === contentTypeName ? apiName : `${apiName} - ${_.upperFirst(contentTypeName)}`;
35
+
36
+ const apiInfo = {
37
+ ...api,
38
+ routeInfo,
39
+ attributes,
40
+ uniqueName,
41
+ contentTypeInfo,
42
+ };
43
+
44
+ result = {
45
+ ...result,
46
+ ...callback(apiInfo),
47
+ };
48
+ }
49
+
50
+ return result;
51
+ };
52
+
53
+ module.exports = loopContentTypeNames;
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+
5
+ const pascalCase = (string) => {
6
+ return _.upperFirst(_.camelCase(string));
7
+ };
8
+
9
+ module.exports = pascalCase;
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ module.exports = [
4
+ {
5
+ name: 'sort',
6
+ in: 'query',
7
+ description: 'Sort by attributes ascending (asc) or descending (desc)',
8
+ deprecated: false,
9
+ required: false,
10
+ schema: {
11
+ type: 'string',
12
+ },
13
+ },
14
+ {
15
+ name: 'pagination[withCount]',
16
+ in: 'query',
17
+ description: 'Retun page/pageSize (default: true)',
18
+ deprecated: false,
19
+ required: false,
20
+ schema: {
21
+ type: 'boolean',
22
+ },
23
+ },
24
+ {
25
+ name: 'pagination[page]',
26
+ in: 'query',
27
+ description: 'Page number (default: 0)',
28
+ deprecated: false,
29
+ required: false,
30
+ schema: {
31
+ type: 'integer',
32
+ },
33
+ },
34
+ {
35
+ name: 'pagination[pageSize]',
36
+ in: 'query',
37
+ description: 'Page size (default: 25)',
38
+ deprecated: false,
39
+ required: false,
40
+ schema: {
41
+ type: 'integer',
42
+ },
43
+ },
44
+ {
45
+ name: 'pagination[start]',
46
+ in: 'query',
47
+ description: 'Offset value (default: 0)',
48
+ deprecated: false,
49
+ required: false,
50
+ schema: {
51
+ type: 'integer',
52
+ },
53
+ },
54
+ {
55
+ name: 'pagination[limit]',
56
+ in: 'query',
57
+ description: 'Number of entities to return (default: 25)',
58
+ deprecated: false,
59
+ required: false,
60
+ schema: {
61
+ type: 'integer',
62
+ },
63
+ },
64
+ {
65
+ name: 'fields',
66
+ in: 'query',
67
+ description: 'Fields to return (ex: title,author)',
68
+ deprecated: false,
69
+ required: false,
70
+ schema: {
71
+ type: 'string',
72
+ },
73
+ },
74
+ {
75
+ name: 'populate',
76
+ in: 'query',
77
+ description: 'Relations to return',
78
+ deprecated: false,
79
+ required: false,
80
+ schema: {
81
+ type: 'string',
82
+ },
83
+ },
84
+ ];
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const hasFindMethod = (handler) => handler.split('.').pop() === 'find';
4
+
5
+ const isLocalizedPath = (routePath) => routePath.includes('localizations');
6
+
7
+ module.exports = {
8
+ isLocalizedPath,
9
+ hasFindMethod,
10
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const documentation = require('./documentation');
4
+
5
+ module.exports = {
6
+ documentation,
7
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('./admin/src').default;
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('./server');