@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,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.1",
3
+ "version": "4.2.0-beta.4",
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.1",
28
- "@strapi/utils": "4.2.0-beta.1",
27
+ "@strapi/helper-plugin": "4.2.0-beta.4",
28
+ "@strapi/utils": "4.2.0-beta.4",
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": "4fa2804f35e15ed72dfd309fb5bb1c4dba18932f"
60
+ "gitHead": "5caff35a30e33b7a660eb946299f9e3d2d35b2b8"
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
  };
@@ -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');
@@ -108,7 +108,7 @@ module.exports = ({ strapi }) => {
108
108
  return [...apisToDocument, ...pluginsToDocument];
109
109
  },
110
110
 
111
- async getCustomSettings() {
111
+ async getCustomConfig() {
112
112
  const customConfigPath = this.getCustomDocumentationPath();
113
113
  const pathExists = await fs.pathExists(customConfigPath);
114
114
  if (pathExists) {
@@ -123,23 +123,31 @@ module.exports = ({ strapi }) => {
123
123
  */
124
124
  async generateFullDoc(version = this.getDocumentationVersion()) {
125
125
  let paths = {};
126
-
126
+ let schemas = {};
127
127
  const apis = this.getPluginAndApiInfo();
128
128
  for (const api of apis) {
129
129
  const apiName = api.name;
130
130
  const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
131
131
 
132
132
  const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
133
- const apiPathsObject = builApiEndpointPath(api);
134
133
 
135
- if (!apiPathsObject) {
134
+ const apiPath = builApiEndpointPath(api);
135
+
136
+ if (!apiPath) {
136
137
  continue;
137
138
  }
138
139
 
139
140
  await fs.ensureFile(apiDocPath);
140
- await fs.writeJson(apiDocPath, apiPathsObject, { spaces: 2 });
141
+ await fs.writeJson(apiDocPath, apiPath, { spaces: 2 });
141
142
 
142
- paths = { ...paths, ...apiPathsObject.paths };
143
+ const componentSchema = buildComponentSchema(api);
144
+
145
+ schemas = {
146
+ ...schemas,
147
+ ...componentSchema,
148
+ };
149
+
150
+ paths = { ...paths, ...apiPath };
143
151
  }
144
152
 
145
153
  const fullDocJsonPath = path.join(
@@ -148,27 +156,26 @@ module.exports = ({ strapi }) => {
148
156
  'full_documentation.json'
149
157
  );
150
158
 
151
- const defaultSettings = _.cloneDeep(defaultConfig);
159
+ const defaultConfig = _.cloneDeep(defaultPluginConfig);
152
160
 
153
161
  const serverUrl = getAbsoluteServerUrl(strapi.config);
154
162
  const apiPath = strapi.config.get('api.rest.prefix');
155
163
 
156
- _.set(defaultSettings, 'servers', [
164
+ _.set(defaultConfig, 'servers', [
157
165
  {
158
166
  url: `${serverUrl}${apiPath}`,
159
167
  description: 'Development server',
160
168
  },
161
169
  ]);
170
+ _.set(defaultConfig, ['info', 'x-generation-date'], new Date().toISOString());
171
+ _.set(defaultConfig, ['info', 'version'], version);
172
+ _.merge(defaultConfig.components, { schemas });
162
173
 
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);
174
+ const customConfig = await this.getCustomConfig();
175
+ const config = _.merge(defaultConfig, customConfig);
169
176
 
170
177
  await fs.ensureFile(fullDocJsonPath);
171
- await fs.writeJson(fullDocJsonPath, { ...settings, paths }, { spaces: 2 });
178
+ await fs.writeJson(fullDocJsonPath, { ...config, paths }, { spaces: 2 });
172
179
  },
173
180
  };
174
181
  };
@@ -0,0 +1,185 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const pathToRegexp = require('path-to-regexp');
5
+
6
+ const pascalCase = require('./utils/pascal-case');
7
+ const queryParams = require('./utils/query-params');
8
+ const loopContentTypeNames = require('./utils/loop-content-type-names');
9
+ const getApiResponses = require('./utils/get-api-responses');
10
+ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
11
+
12
+ /**
13
+ * @description Parses a route with ':variable'
14
+ *
15
+ * @param {string} routePath - The route's path property
16
+ * @returns {string}
17
+ */
18
+ const parsePathWithVariables = routePath => {
19
+ return pathToRegexp
20
+ .parse(routePath)
21
+ .map(token => {
22
+ if (_.isObject(token)) {
23
+ return token.prefix + '{' + token.name + '}';
24
+ }
25
+
26
+ return token;
27
+ })
28
+ .join('');
29
+ };
30
+
31
+ /**
32
+ * @description Builds the required object for a path parameter
33
+ *
34
+ * @param {string} routePath - The route's path property
35
+ *
36
+ * @returns {object } Swagger path params object
37
+ */
38
+ const getPathParams = routePath => {
39
+ return pathToRegexp
40
+ .parse(routePath)
41
+ .filter(token => _.isObject(token))
42
+ .map(param => {
43
+ return {
44
+ name: param.name,
45
+ in: 'path',
46
+ description: '',
47
+ deprecated: false,
48
+ required: true,
49
+ schema: { type: 'string' },
50
+ };
51
+ });
52
+ };
53
+
54
+ /**
55
+ *
56
+ * @param {string} prefix - The prefix found on the routes object
57
+ * @param {string} route - The current route
58
+ * @property {string} route.path - The current route's path
59
+ * @property {object} route.config - The current route's config object
60
+ *
61
+ * @returns {string}
62
+ */
63
+ const getPathWithPrefix = (prefix, route) => {
64
+ // When the prefix is set on the routes and
65
+ // the current route is not trying to remove it
66
+ if (prefix && !_.has(route.config, 'prefix')) {
67
+ // Add the prefix to the path
68
+ return prefix.concat(route.path);
69
+ }
70
+
71
+ // Otherwise just return path
72
+ return route.path;
73
+ };
74
+ /**
75
+ * @description Gets all paths based on routes
76
+ *
77
+ * @param {object} apiInfo
78
+ * @property {object} apiInfo.routeInfo - The api routes object
79
+ * @property {string} apiInfo.uniqueName - Content type name | Api name + Content type name
80
+ * @property {object} apiInfo.contentTypeInfo - The info object found on content type schemas
81
+ *
82
+ * @returns {object}
83
+ */
84
+ const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
85
+ // Get the routes for the current content type
86
+ const contentTypeRoutes = routeInfo.routes.filter(route => {
87
+ return (
88
+ route.path.includes(contentTypeInfo.pluralName) ||
89
+ route.path.includes(contentTypeInfo.singularName)
90
+ );
91
+ });
92
+
93
+ const paths = contentTypeRoutes.reduce((acc, route) => {
94
+ // TODO: Find a more reliable way to determine list of entities vs a single entity
95
+ const isListOfEntities = hasFindMethod(route.handler);
96
+ const isLocalizationPath = isLocalizedPath(route.path);
97
+ const methodVerb = route.method.toLowerCase();
98
+ const hasPathParams = route.path.includes('/:');
99
+ const pathWithPrefix = getPathWithPrefix(routeInfo.prefix, route);
100
+ const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
101
+ const { responses } = getApiResponses({
102
+ uniqueName,
103
+ route,
104
+ isListOfEntities,
105
+ isLocalizationPath,
106
+ });
107
+
108
+ const swaggerConfig = {
109
+ responses,
110
+ tags: [_.upperFirst(uniqueName)],
111
+ parameters: [],
112
+ operationId: `${methodVerb}${routePath}`,
113
+ };
114
+
115
+ if (isListOfEntities) {
116
+ swaggerConfig.parameters.push(...queryParams);
117
+ }
118
+
119
+ if (hasPathParams) {
120
+ const pathParams = getPathParams(route.path);
121
+ swaggerConfig.parameters.push(...pathParams);
122
+ }
123
+
124
+ if (['post', 'put'].includes(methodVerb)) {
125
+ const refName = isLocalizationPath ? 'LocalizationRequest' : 'Request';
126
+ const requestBody = {
127
+ required: true,
128
+ content: {
129
+ 'application/json': {
130
+ schema: {
131
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}${refName}`,
132
+ },
133
+ },
134
+ },
135
+ };
136
+
137
+ swaggerConfig.requestBody = requestBody;
138
+ }
139
+
140
+ _.set(acc, `${routePath}.${methodVerb}`, swaggerConfig);
141
+
142
+ return acc;
143
+ }, {});
144
+
145
+ return paths;
146
+ };
147
+
148
+ /**
149
+ * @decription Gets all open api paths object for a given content type
150
+ *
151
+ * @param {object} apiInfo
152
+ *
153
+ * @returns {object} Open API paths
154
+ */
155
+ const getAllPathsForContentType = apiInfo => {
156
+ let paths = {};
157
+
158
+ const pathsObject = getPaths(apiInfo);
159
+
160
+ paths = {
161
+ ...paths,
162
+ ...pathsObject,
163
+ };
164
+
165
+ return paths;
166
+ };
167
+
168
+ /**
169
+ * @description - Builds the Swagger paths object for each api
170
+ *
171
+ * @param {object} api - Information about the current api
172
+ * @property {string} api.name - The name of the api
173
+ * @property {string} api.getter - The getter for the api (api | plugin)
174
+ * @property {array} api.ctNames - The name of all contentTypes found on the api
175
+ *
176
+ * @returns {object}
177
+ */
178
+ const buildApiEndpointPath = api => {
179
+ // A reusable loop for building paths and component schemas
180
+ // Uses the api param to build a new set of params for each content type
181
+ // Passes these new params to the function provided
182
+ return loopContentTypeNames(api, getAllPathsForContentType);
183
+ };
184
+
185
+ module.exports = buildApiEndpointPath;