@strapi/plugin-documentation 4.2.0-beta.1 → 4.2.0-beta.4

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,156 @@
1
+ 'use strict';
2
+ const _ = require('lodash');
3
+
4
+ const cleanSchemaAttributes = require('./utils/clean-schema-attributes');
5
+ const loopContentTypeNames = require('./utils/loop-content-type-names');
6
+ const pascalCase = require('./utils/pascal-case');
7
+ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
8
+
9
+ /**
10
+ * @decription Get all open api schema objects for a given content type
11
+ *
12
+ * @param {object} apiInfo
13
+ * @property {string} apiInfo.uniqueName - Api name | Api name + Content type name
14
+ * @property {object} apiInfo.attributes - Attributes on content type
15
+ * @property {object} apiInfo.routeInfo - The routes for the api
16
+ *
17
+ * @returns {object} Open API schemas
18
+ */
19
+ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
20
+ // Store response and request schemas in an object
21
+ let schemas = {};
22
+ // Get all the route methods
23
+ const routeMethods = routeInfo.routes.map(route => route.method);
24
+ // Check for localized paths
25
+ const hasLocalizationPath = routeInfo.routes.filter(route => isLocalizedPath(route.path)).length;
26
+ // When the route methods contain any post or put requests
27
+ if (routeMethods.includes('POST') || routeMethods.includes('PUT')) {
28
+ const attributesToOmit = [
29
+ 'createdAt',
30
+ 'updatedAt',
31
+ 'publishedAt',
32
+ 'publishedBy',
33
+ 'updatedBy',
34
+ 'createdBy',
35
+ 'localizations',
36
+ ];
37
+ const attributesForRequest = _.omit(attributes, attributesToOmit);
38
+
39
+ // Get a list of required attribute names
40
+ const requiredAttributes = Object.entries(attributesForRequest).reduce((acc, attribute) => {
41
+ const [attributeKey, attributeValue] = attribute;
42
+
43
+ if (attributeValue.required) {
44
+ acc.push(attributeKey);
45
+ }
46
+
47
+ return acc;
48
+ }, []);
49
+
50
+ if (hasLocalizationPath) {
51
+ schemas = {
52
+ ...schemas,
53
+ [`${pascalCase(uniqueName)}LocalizationRequest`]: {
54
+ required: [...requiredAttributes, 'locale'],
55
+ type: 'object',
56
+ properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
57
+ },
58
+ };
59
+ }
60
+
61
+ // Build the request schema
62
+ schemas = {
63
+ ...schemas,
64
+ [`${pascalCase(uniqueName)}Request`]: {
65
+ type: 'object',
66
+ required: ['data'],
67
+ properties: {
68
+ data: {
69
+ required: requiredAttributes,
70
+ type: 'object',
71
+ properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
72
+ },
73
+ },
74
+ },
75
+ };
76
+ }
77
+
78
+ if (hasLocalizationPath) {
79
+ schemas = {
80
+ ...schemas,
81
+ [`${pascalCase(uniqueName)}LocalizationResponse`]: {
82
+ type: 'object',
83
+ properties: {
84
+ id: { type: 'string' },
85
+ ...cleanSchemaAttributes(attributes),
86
+ },
87
+ },
88
+ };
89
+ }
90
+
91
+ // Check for routes that need to return a list
92
+ const hasListOfEntities = routeInfo.routes.filter(route => hasFindMethod(route.handler)).length;
93
+ if (hasListOfEntities) {
94
+ // Build the list response schema
95
+ schemas = {
96
+ ...schemas,
97
+ [`${pascalCase(uniqueName)}ListResponse`]: {
98
+ type: 'object',
99
+ properties: {
100
+ data: {
101
+ type: 'array',
102
+ items: {
103
+ type: 'object',
104
+ properties: {
105
+ id: { type: 'string' },
106
+ attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
107
+ },
108
+ },
109
+ },
110
+ meta: {
111
+ type: 'object',
112
+ properties: {
113
+ pagination: {
114
+ properties: {
115
+ page: { type: 'integer' },
116
+ pageSize: { type: 'integer', minimum: 25 },
117
+ pageCount: { type: 'integer', maximum: 1 },
118
+ total: { type: 'integer' },
119
+ },
120
+ },
121
+ },
122
+ },
123
+ },
124
+ },
125
+ };
126
+ }
127
+
128
+ // Build the response schema
129
+ schemas = {
130
+ ...schemas,
131
+ [`${pascalCase(uniqueName)}Response`]: {
132
+ type: 'object',
133
+ properties: {
134
+ data: {
135
+ type: 'object',
136
+ properties: {
137
+ id: { type: 'string' },
138
+ attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
139
+ },
140
+ },
141
+ meta: { type: 'object' },
142
+ },
143
+ },
144
+ };
145
+
146
+ return schemas;
147
+ };
148
+
149
+ const buildComponentSchema = api => {
150
+ // A reusable loop for building paths and component schemas
151
+ // Uses the api param to build a new set of params for each content type
152
+ // Passes these new params to the function provided
153
+ return loopContentTypeNames(api, getAllSchemasForContentType);
154
+ };
155
+
156
+ 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
+ };
@@ -4,13 +4,12 @@ const _ = require('lodash');
4
4
  const getSchemaData = require('./get-schema-data');
5
5
 
6
6
  /**
7
- * @description - Converts types found on attributes to OpenAPI specific data types
7
+ * @description - Converts types found on attributes to OpenAPI acceptable data types
8
8
  *
9
9
  * @param {object} attributes - The attributes found on a contentType
10
10
  * @param {{ typeMap: Map, isRequest: boolean }} opts
11
11
  * @returns Attributes using OpenAPI acceptable data types
12
12
  */
13
-
14
13
  const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = false } = {}) => {
15
14
  const attributesCopy = _.cloneDeep(attributes);
16
15
 
@@ -170,6 +169,14 @@ const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = fa
170
169
  break;
171
170
  }
172
171
 
172
+ if (prop === 'localizations') {
173
+ attributesCopy[prop] = {
174
+ type: 'array',
175
+ items: { type: 'object', properties: {} },
176
+ };
177
+ break;
178
+ }
179
+
173
180
  if (!attribute.target || typeMap.has(attribute.target)) {
174
181
  attributesCopy[prop] = {
175
182
  type: 'object',
@@ -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,52 @@
1
+ 'use strict';
2
+ const _ = require('lodash');
3
+
4
+ /**
5
+ * @description A reusable loop for building api endpoint paths and component schemas
6
+ *
7
+ * @param {object} api - Api information to pass to the callback
8
+ * @param {function} callback - Logic to execute for the given api
9
+ *
10
+ * @returns {object}
11
+ */
12
+ const loopContentTypeNames = (api, callback) => {
13
+ let result = {};
14
+ for (const contentTypeName of api.ctNames) {
15
+ // Get the attributes found on the api's contentType
16
+ const uid = `${api.getter}::${api.name}.${contentTypeName}`;
17
+ const { attributes, info: contentTypeInfo } = strapi.contentType(uid);
18
+
19
+ // Get the routes for the current api
20
+ const routeInfo =
21
+ api.getter === 'plugin'
22
+ ? strapi.plugin(api.name).routes['content-api']
23
+ : strapi.api[api.name].routes[contentTypeName];
24
+
25
+ // Continue to next iteration if routeInfo is undefined
26
+ if (!routeInfo) continue;
27
+
28
+ // Uppercase the first letter of the api name
29
+ const apiName = _.upperFirst(api.name);
30
+
31
+ // Create a unique name if the api name and contentType name don't match
32
+ const uniqueName =
33
+ api.name === contentTypeName ? apiName : `${apiName} - ${_.upperFirst(contentTypeName)}`;
34
+
35
+ const apiInfo = {
36
+ ...api,
37
+ routeInfo,
38
+ attributes,
39
+ uniqueName,
40
+ contentTypeInfo,
41
+ };
42
+
43
+ result = {
44
+ ...result,
45
+ ...callback(apiInfo),
46
+ };
47
+ }
48
+
49
+ return result;
50
+ };
51
+
52
+ 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,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
+ };
package/yarn-error.log ADDED
@@ -0,0 +1,95 @@
1
+ Arguments:
2
+ /Users/alex/.nvm/versions/node/v16.13.2/bin/node /Users/alex/.nvm/versions/node/v16.13.2/bin/yarn
3
+
4
+ PATH:
5
+ /Users/alex/.nvm/versions/node/v16.13.2/bin:/Users/alex/.nvm/versions/node/v18.1.0/bin:/Users/alex/.nvm/versions/node/v16.13.2/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/alex/.nvm/versions/node/v16.13.2/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/alex/.yarn/bin:/Users/alex/.yarn/bin
6
+
7
+ Yarn version:
8
+ 1.22.17
9
+
10
+ Node version:
11
+ 16.13.2
12
+
13
+ Platform:
14
+ darwin arm64
15
+
16
+ Trace:
17
+ SyntaxError: /Users/alex/dev/strapi/strapi/packages/plugins/documentation/package.json: Unexpected token < in JSON at position 1187
18
+ at JSON.parse (<anonymous>)
19
+ at /Users/alex/.nvm/versions/node/v16.13.2/lib/node_modules/yarn/lib/cli.js:1625:59
20
+ at Generator.next (<anonymous>)
21
+ at step (/Users/alex/.nvm/versions/node/v16.13.2/lib/node_modules/yarn/lib/cli.js:310:30)
22
+ at /Users/alex/.nvm/versions/node/v16.13.2/lib/node_modules/yarn/lib/cli.js:321:13
23
+
24
+ npm manifest:
25
+ {
26
+ "name": "@strapi/plugin-documentation",
27
+ "version": "4.1.12",
28
+ "description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/strapi/strapi.git",
32
+ "directory": "packages/plugins/documentation"
33
+ },
34
+ "license": "SEE LICENSE IN LICENSE",
35
+ "author": {
36
+ "name": "Strapi Solutions SAS",
37
+ "email": "hi@strapi.io",
38
+ "url": "https://strapi.io"
39
+ },
40
+ "maintainers": [
41
+ {
42
+ "name": "Strapi Solutions SAS",
43
+ "email": "hi@strapi.io",
44
+ "url": "https://strapi.io"
45
+ }
46
+ ],
47
+ "scripts": {
48
+ "test": "echo \"no tests yet\""
49
+ },
50
+ "dependencies": {
51
+ "@strapi/helper-plugin": "4.1.12",
52
+ "@strapi/utils": "4.1.12",
53
+ "bcryptjs": "2.4.3",
54
+ "cheerio": "^1.0.0-rc.5",
55
+ "fs-extra": "10.0.0",
56
+ "koa-static": "^5.0.0",
57
+ "lodash": "4.17.21",
58
+ "path-to-regexp": "6.2.0",
59
+ "pluralize": "8.0.0",
60
+ "react": "^17.0.2",
61
+ "react-copy-to-clipboard": "^5.0.3",
62
+ "react-dom": "^17.0.2",
63
+ "react-intl": "5.20.2",
64
+ "react-redux": "7.2.3",
65
+ "react-router": "^5.2.0",
66
+ "react-router-dom": "5.2.0",
67
+ "redux": "^4.0.1",
68
+ "reselect": "^4.0.0",
69
+ <<<<<<< HEAD
70
+ "swagger-ui-dist": "3.47.1",
71
+ "yaml": "1.10.2"
72
+ =======
73
+ "swagger-ui-dist": "4.11.1"
74
+ >>>>>>> master
75
+ },
76
+ "peerDependencies": {
77
+ "@strapi/strapi": "^4.0.0"
78
+ },
79
+ "engines": {
80
+ "node": ">=12.22.0 <=16.x.x",
81
+ "npm": ">=6.0.0"
82
+ },
83
+ "strapi": {
84
+ "displayName": "Documentation",
85
+ "name": "documentation",
86
+ "description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
87
+ "kind": "plugin"
88
+ }
89
+ }
90
+
91
+ yarn manifest:
92
+ No manifest
93
+
94
+ Lockfile:
95
+ No lockfile
@@ -1,25 +0,0 @@
1
- {
2
- "components": {
3
- "securitySchemes": {
4
- "bearerAuth": {
5
- "type": "http",
6
- "scheme": "bearer",
7
- "bearerFormat": "JWT"
8
- }
9
- },
10
- "schemas": {
11
- "Error": {
12
- "required": ["code", "message"],
13
- "properties": {
14
- "code": {
15
- "type": "integer",
16
- "format": "int32"
17
- },
18
- "message": {
19
- "type": "string"
20
- }
21
- }
22
- }
23
- }
24
- }
25
- }
@@ -1,134 +0,0 @@
1
- [
2
- {
3
- "name": "_limit",
4
- "in": "query",
5
- "required": false,
6
- "description": "Maximum number of results possible",
7
- "schema": {
8
- "type": "integer"
9
- },
10
- "deprecated": false
11
- },
12
- {
13
- "name": "_sort",
14
- "in": "query",
15
- "required": false,
16
- "description": "Sort according to a specific field.",
17
- "schema": {
18
- "type": "string"
19
- },
20
- "deprecated": false
21
- },
22
- {
23
- "name": "_start",
24
- "in": "query",
25
- "required": false,
26
- "description": "Skip a specific number of entries (especially useful for pagination)",
27
- "schema": {
28
- "type": "integer"
29
- },
30
- "deprecated": false
31
- },
32
- {
33
- "name": "=",
34
- "in": "query",
35
- "required": false,
36
- "description": "Get entries that matches exactly your input",
37
- "schema": {
38
- "type": "string"
39
- },
40
- "deprecated": false
41
- },
42
- {
43
- "name": "_ne",
44
- "in": "query",
45
- "required": false,
46
- "description": "Get records that are not equals to something",
47
- "schema": {
48
- "type": "string"
49
- },
50
- "deprecated": false
51
- },
52
- {
53
- "name": "_lt",
54
- "in": "query",
55
- "required": false,
56
- "description": "Get record that are lower than a value",
57
- "schema": {
58
- "type": "string"
59
- },
60
- "deprecated": false
61
- },
62
- {
63
- "name": "_lte",
64
- "in": "query",
65
- "required": false,
66
- "description": "Get records that are lower than or equal to a value",
67
- "schema": {
68
- "type": "string"
69
- },
70
- "deprecated": false
71
- },
72
- {
73
- "name": "_gt",
74
- "in": "query",
75
- "required": false,
76
- "description": "Get records that are greater than a value",
77
- "schema": {
78
- "type": "string"
79
- },
80
- "deprecated": false
81
- },
82
- {
83
- "name": "_gte",
84
- "in": "query",
85
- "required": false,
86
- "description": "Get records that are greater than or equal a value",
87
- "schema": {
88
- "type": "string"
89
- },
90
- "deprecated": false
91
- },
92
- {
93
- "name": "_contains",
94
- "in": "query",
95
- "required": false,
96
- "description": "Get records that contains a value",
97
- "schema": {
98
- "type": "string"
99
- },
100
- "deprecated": false
101
- },
102
- {
103
- "name": "_containss",
104
- "in": "query",
105
- "required": false,
106
- "description": "Get records that contains (case sensitive) a value",
107
- "schema": {
108
- "type": "string"
109
- },
110
- "deprecated": false
111
- },
112
- {
113
- "name": "_in",
114
- "in": "query",
115
- "required": false,
116
- "description": "Get records that matches any value in the array of values",
117
- "schema": {
118
- "type": "array",
119
- "items": { "type": "string" }
120
- },
121
- "deprecated": false
122
- },
123
- {
124
- "name": "_nin",
125
- "in": "query",
126
- "required": false,
127
- "description": "Get records that doesn't match any value in the array of values",
128
- "schema": {
129
- "type": "array",
130
- "items": { "type": "string" }
131
- },
132
- "deprecated": false
133
- }
134
- ]
@@ -1,11 +0,0 @@
1
- {
2
- "components": {
3
- "schemas": {
4
- "Foo": {
5
- "properties": {
6
- "bar": "string"
7
- }
8
- }
9
- }
10
- }
11
- }