@strapi/plugin-documentation 4.10.0-beta.1 → 4.10.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.
@@ -2,37 +2,50 @@
2
2
 
3
3
  const path = require('path');
4
4
  const fs = require('fs-extra');
5
- const _ = require('lodash');
5
+ const { produce } = require('immer');
6
6
  const { getAbsoluteServerUrl } = require('@strapi/utils');
7
-
8
- const defaultPluginConfig = require('../config/default-plugin-config');
9
7
  const { builApiEndpointPath, buildComponentSchema } = require('./helpers');
10
8
 
9
+ const defaultOpenApiComponents = require('./utils/default-openapi-components');
10
+ const { getPluginsThatNeedDocumentation } = require('./utils/get-plugins-that-need-documentation');
11
+
11
12
  module.exports = ({ strapi }) => {
12
13
  const config = strapi.config.get('plugin.documentation');
13
-
14
- const registeredDocs = [];
14
+ const pluginsThatNeedDocumentation = getPluginsThatNeedDocumentation(config);
15
+ const overrideService = strapi.plugin('documentation').service('override');
15
16
 
16
17
  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);
18
+ /**
19
+ *
20
+ * @deprecated
21
+ * registerDoc is deprecated it will be removed in the next major release,
22
+ * use strapi.plugin('documentation').service('override').registerOverride() instead
23
+ * @param {object} doc - The openapi specifcation to override
24
+ * @param {object} options - The options to override the documentation
25
+ * @param {string} options.pluginOrigin - The name of the plugin that is overriding the documentation
26
+ * @param {string[]} options.excludeFromGeneration - The name of the plugin that is overriding the documentation
27
+ */
28
+ registerDoc(doc, options) {
29
+ strapi.log.warn(
30
+ "@strapi/plugin-documentation has deprecated registerDoc, use strapi.plugin('documentation').service('override').registerOverride() instead"
31
+ );
32
+ overrideService.registerOverride(doc, options);
25
33
  },
34
+
26
35
  getDocumentationVersion() {
27
- return _.get(config, 'info.version');
36
+ return config.info.version;
28
37
  },
29
38
 
30
39
  getFullDocumentationPath() {
31
40
  return path.join(strapi.dirs.app.extensions, 'documentation', 'documentation');
32
41
  },
33
42
 
43
+ /**
44
+ *
45
+ * @deprecated
46
+ * This method will be removed in the next major release
47
+ */
34
48
  getCustomDocumentationPath() {
35
- // ??
36
49
  return path.join(strapi.dirs.app.extensions, 'documentation', 'config', 'settings.json');
37
50
  },
38
51
 
@@ -46,7 +59,8 @@ module.exports = ({ strapi }) => {
46
59
  path.resolve(this.getFullDocumentationPath(), version, 'full_documentation.json')
47
60
  )
48
61
  );
49
- const generatedDate = _.get(doc, ['info', 'x-generation-date'], null);
62
+
63
+ const generatedDate = doc.info['x-generation-date'];
50
64
 
51
65
  return { version, generatedDate, url: '' };
52
66
  } catch (err) {
@@ -99,8 +113,7 @@ module.exports = ({ strapi }) => {
99
113
  },
100
114
 
101
115
  getPluginAndApiInfo() {
102
- const plugins = _.get(config, 'x-strapi-config.plugins');
103
- const pluginsToDocument = plugins.map((plugin) => {
116
+ const pluginsToDocument = pluginsThatNeedDocumentation.map((plugin) => {
104
117
  return {
105
118
  name: plugin,
106
119
  getter: 'plugin',
@@ -119,92 +132,118 @@ module.exports = ({ strapi }) => {
119
132
  return [...apisToDocument, ...pluginsToDocument];
120
133
  },
121
134
 
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
135
  /**
133
136
  * @description - Creates the Swagger json files
134
137
  */
135
138
  async generateFullDoc(version = this.getDocumentationVersion()) {
136
- let paths = {};
137
- let schemas = {};
138
139
  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);
140
+ const apisThatNeedGeneratedDocumentation = apis.filter(
141
+ ({ name }) => !overrideService.excludedFromGeneration.includes(name)
142
+ );
146
143
 
147
- if (!apiPath) {
148
- continue;
144
+ // Initialize the generated documentation with defaults
145
+ let generatedDocumentation = produce(
146
+ {
147
+ ...config,
148
+ components: defaultOpenApiComponents,
149
+ },
150
+ (draft) => {
151
+ if (draft.servers.length === 0) {
152
+ // When no servers found set the defaults
153
+ const serverUrl = getAbsoluteServerUrl(strapi.config);
154
+ const apiPath = strapi.config.get('api.rest.prefix');
155
+ draft.servers = [
156
+ {
157
+ url: `${serverUrl}${apiPath}`,
158
+ description: 'Development server',
159
+ },
160
+ ];
161
+ }
162
+ // Set the generated date
163
+ draft.info['x-generation-date'] = new Date().toISOString();
164
+ // Set the plugins that need documentation
165
+ draft['x-strapi-config'].plugins = pluginsThatNeedDocumentation;
166
+ // Delete the mutateDocumentation key from the config so it doesn't end up in the spec
167
+ delete draft['x-strapi-config'].mutateDocumentation;
149
168
  }
169
+ );
170
+ // Generate the documentation for each api and update the generatedDocumentation
171
+ for (const api of apisThatNeedGeneratedDocumentation) {
172
+ const apiName = api.name;
150
173
 
174
+ const newApiPath = builApiEndpointPath(api);
175
+ const generatedSchemas = buildComponentSchema(api);
176
+
177
+ // TODO: To be confirmed, do we still need to write these files...?
178
+ const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
179
+ const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
151
180
  await fs.ensureFile(apiDocPath);
152
- await fs.writeJson(apiDocPath, apiPath, { spaces: 2 });
181
+ await fs.writeJson(apiDocPath, newApiPath, { spaces: 2 });
153
182
 
154
- const componentSchema = buildComponentSchema(api);
183
+ generatedDocumentation = produce(generatedDocumentation, (draft) => {
184
+ if (generatedSchemas) {
185
+ draft.components = {
186
+ schemas: { ...draft.components.schemas, ...generatedSchemas },
187
+ };
188
+ }
155
189
 
156
- schemas = {
157
- ...schemas,
158
- ...componentSchema,
159
- };
190
+ if (newApiPath) {
191
+ draft.paths = { ...draft.paths, ...newApiPath };
192
+ }
193
+ });
194
+ }
160
195
 
161
- paths = { ...paths, ...apiPath };
196
+ // When overrides are present update the generatedDocumentation
197
+ if (overrideService.registeredOverrides.length > 0) {
198
+ generatedDocumentation = produce(generatedDocumentation, (draft) => {
199
+ overrideService.registeredOverrides.forEach((override) => {
200
+ // Only run the overrrides when no override version is provided,
201
+ // or when the generated documentation version matches the override version
202
+ if (!override?.info?.version || override.info.version === version) {
203
+ if (override.tags) {
204
+ // Merge override tags with the generated tags
205
+ draft.tags = draft.tags || [];
206
+ draft.tags.push(...override.tags);
207
+ }
208
+
209
+ if (override.paths) {
210
+ // Merge override paths with the generated paths
211
+ // The override will add a new path or replace the value of an existing path
212
+ draft.paths = { ...draft.paths, ...override.paths };
213
+ }
214
+
215
+ if (override.components) {
216
+ Object.entries(override.components).forEach(([overrideKey, overrideValue]) => {
217
+ draft.components[overrideKey] = draft.components[overrideKey] || {};
218
+ // Merge override components with the generated components,
219
+ // The override will add a new component or replace the value of an existing component
220
+ draft.components[overrideKey] = {
221
+ ...draft.components[overrideKey],
222
+ ...overrideValue,
223
+ };
224
+ });
225
+ }
226
+ }
227
+ });
228
+ });
162
229
  }
163
230
 
231
+ // Escape hatch, allow the user to provide a mutateDocumentation function that can alter any part of
232
+ // the generated documentation before it is written to the file system
233
+ const userMutatesDocumentation = config['x-strapi-config'].mutateDocumentation;
234
+ const finalDocumentation = userMutatesDocumentation
235
+ ? produce(generatedDocumentation, userMutatesDocumentation)
236
+ : generatedDocumentation;
237
+
238
+ // Get the file path for the final documentation
164
239
  const fullDocJsonPath = path.join(
165
240
  this.getFullDocumentationPath(),
166
241
  version,
167
242
  'full_documentation.json'
168
243
  );
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
-
244
+ // Write the documentation to the file system
206
245
  await fs.ensureFile(fullDocJsonPath);
207
- await fs.writeJson(fullDocJsonPath, finalDoc, { spaces: 2 });
246
+ await fs.writeJson(fullDocJsonPath, finalDocumentation, { spaces: 2 });
208
247
  },
209
248
  };
210
249
  };
@@ -81,7 +81,7 @@ const getPathWithPrefix = (prefix, route) => {
81
81
  *
82
82
  * @returns {object}
83
83
  */
84
- const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
84
+ const getPaths = ({ routeInfo, uniqueName, contentTypeInfo, kind }) => {
85
85
  // Get the routes for the current content type
86
86
  const contentTypeRoutes = routeInfo.routes.filter((route) => {
87
87
  return (
@@ -98,10 +98,11 @@ const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
98
98
  const hasPathParams = route.path.includes('/:');
99
99
  const pathWithPrefix = getPathWithPrefix(routeInfo.prefix, route);
100
100
  const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
101
+
101
102
  const { responses } = getApiResponses({
102
103
  uniqueName,
103
104
  route,
104
- isListOfEntities,
105
+ isListOfEntities: kind !== 'singleType' && isListOfEntities,
105
106
  isLocalizationPath,
106
107
  });
107
108
 
@@ -7,6 +7,18 @@ const loopContentTypeNames = require('./utils/loop-content-type-names');
7
7
  const pascalCase = require('./utils/pascal-case');
8
8
  const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
9
9
 
10
+ const getRequiredAttributes = (allAttributes) => {
11
+ return Object.entries(allAttributes).reduce((acc, attribute) => {
12
+ const [attributeKey, attributeValue] = attribute;
13
+
14
+ if (attributeValue.required) {
15
+ acc.push(attributeKey);
16
+ }
17
+
18
+ return acc;
19
+ }, []);
20
+ };
21
+
10
22
  /**
11
23
  * @decription Get all open api schema objects for a given content type
12
24
  *
@@ -20,16 +32,17 @@ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
20
32
  const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
21
33
  // Store response and request schemas in an object
22
34
  let schemas = {};
23
- let componentSchemas = {};
35
+ let strapiComponentSchemas = {};
24
36
  // adds a ComponentSchema to the Schemas so it can be used as Ref
25
- const addComponentSchema = (schemaName, schema) => {
26
- if (!Object.keys(schema) || !Object.keys(schema.properties)) {
27
- return false;
28
- }
29
- componentSchemas = {
30
- ...componentSchemas,
37
+ const didAddStrapiComponentsToSchemas = (schemaName, schema) => {
38
+ if (!Object.keys(schema) || !Object.keys(schema.properties)) return false;
39
+
40
+ // Add the Strapi components to the schema
41
+ strapiComponentSchemas = {
42
+ ...strapiComponentSchemas,
31
43
  [schemaName]: schema,
32
44
  };
45
+
33
46
  return true;
34
47
  };
35
48
  // Get all the route methods
@@ -38,39 +51,31 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
38
51
  const hasLocalizationPath = routeInfo.routes.filter((route) =>
39
52
  isLocalizedPath(route.path)
40
53
  ).length;
41
- // When the route methods contain any post or put requests
42
- if (routeMethods.includes('POST') || routeMethods.includes('PUT')) {
43
- const attributesToOmit = [
44
- 'createdAt',
45
- 'updatedAt',
46
- 'publishedAt',
47
- 'publishedBy',
48
- 'updatedBy',
49
- 'createdBy',
50
- 'localizations',
51
- ];
52
- const attributesForRequest = _.omit(attributes, attributesToOmit);
53
-
54
- // Get a list of required attribute names
55
- const requiredAttributes = Object.entries(attributesForRequest).reduce((acc, attribute) => {
56
- const [attributeKey, attributeValue] = attribute;
57
-
58
- if (attributeValue.required) {
59
- acc.push(attributeKey);
60
- }
61
-
62
- return acc;
63
- }, []);
64
54
 
55
+ const attributesToOmit = [
56
+ 'createdAt',
57
+ 'updatedAt',
58
+ 'publishedAt',
59
+ 'publishedBy',
60
+ 'updatedBy',
61
+ 'createdBy',
62
+ 'localizations',
63
+ ];
64
+ const attributesForRequest = _.omit(attributes, attributesToOmit);
65
+ // Get a list of required attribute names
66
+ const requiredRequestAttributes = getRequiredAttributes(attributesForRequest);
67
+ // Build the request schemas when the route has POST or PUT methods
68
+ if (routeMethods.includes('POST') || routeMethods.includes('PUT')) {
69
+ // Build localization requests schemas
65
70
  if (hasLocalizationPath) {
66
71
  schemas = {
67
72
  ...schemas,
68
73
  [`${pascalCase(uniqueName)}LocalizationRequest`]: {
69
- required: [...requiredAttributes, 'locale'],
74
+ required: [...requiredRequestAttributes, 'locale'],
70
75
  type: 'object',
71
76
  properties: cleanSchemaAttributes(attributesForRequest, {
72
77
  isRequest: true,
73
- addComponentSchema,
78
+ didAddStrapiComponentsToSchemas,
74
79
  }),
75
80
  },
76
81
  };
@@ -84,11 +89,11 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
84
89
  required: ['data'],
85
90
  properties: {
86
91
  data: {
87
- required: requiredAttributes,
92
+ ...(requiredRequestAttributes.length && { required: requiredRequestAttributes }),
88
93
  type: 'object',
89
94
  properties: cleanSchemaAttributes(attributesForRequest, {
90
95
  isRequest: true,
91
- addComponentSchema,
96
+ didAddStrapiComponentsToSchemas,
92
97
  }),
93
98
  },
94
99
  },
@@ -96,14 +101,26 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
96
101
  };
97
102
  }
98
103
 
104
+ // Build the localization response schema
99
105
  if (hasLocalizationPath) {
100
106
  schemas = {
101
107
  ...schemas,
102
- [`${pascalCase(uniqueName)}LocalizationResponse`]: {
108
+ [`${pascalCase(uniqueName)}ResponseDataObjectLocalized`]: {
103
109
  type: 'object',
104
110
  properties: {
105
111
  id: { type: 'number' },
106
- ...cleanSchemaAttributes(attributes, { addComponentSchema }),
112
+ attributes: {
113
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}`,
114
+ },
115
+ },
116
+ },
117
+ [`${pascalCase(uniqueName)}LocalizationResponse`]: {
118
+ type: 'object',
119
+ properties: {
120
+ data: {
121
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}ResponseDataObjectLocalized`,
122
+ },
123
+ meta: { type: 'object' },
107
124
  },
108
125
  },
109
126
  };
@@ -112,6 +129,46 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
112
129
  // Check for routes that need to return a list
113
130
  const hasListOfEntities = routeInfo.routes.filter((route) => hasFindMethod(route.handler)).length;
114
131
  if (hasListOfEntities) {
132
+ // Buld the localized list response schema
133
+ if (hasLocalizationPath) {
134
+ schemas = {
135
+ ...schemas,
136
+ [`${pascalCase(uniqueName)}ListResponseDataItemLocalized`]: {
137
+ type: 'object',
138
+ properties: {
139
+ id: { type: 'number' },
140
+ attributes: {
141
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}`,
142
+ },
143
+ },
144
+ },
145
+ [`${pascalCase(uniqueName)}LocalizationListResponse`]: {
146
+ type: 'object',
147
+ properties: {
148
+ data: {
149
+ type: 'array',
150
+ items: {
151
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}ListResponseDataItemLocalized`,
152
+ },
153
+ },
154
+ meta: {
155
+ type: 'object',
156
+ properties: {
157
+ pagination: {
158
+ type: 'object',
159
+ properties: {
160
+ page: { type: 'integer' },
161
+ pageSize: { type: 'integer', minimum: 25 },
162
+ pageCount: { type: 'integer', maximum: 1 },
163
+ total: { type: 'integer' },
164
+ },
165
+ },
166
+ },
167
+ },
168
+ },
169
+ },
170
+ };
171
+ }
115
172
  // Build the list response schema
116
173
  schemas = {
117
174
  ...schemas,
@@ -120,27 +177,12 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
120
177
  properties: {
121
178
  id: { type: 'number' },
122
179
  attributes: {
123
- type: 'object',
124
- properties: cleanSchemaAttributes(attributes, {
125
- addComponentSchema,
126
- componentSchemaRefName: `#/components/schemas/${pascalCase(
127
- uniqueName
128
- )}ListResponseDataItemLocalized`,
129
- }),
130
- },
131
- },
132
- },
133
- [`${pascalCase(uniqueName)}ListResponseDataItemLocalized`]: {
134
- type: 'object',
135
- properties: {
136
- id: { type: 'number' },
137
- attributes: {
138
- type: 'object',
139
- properties: cleanSchemaAttributes(attributes, { addComponentSchema }),
180
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}`,
140
181
  },
141
182
  },
142
183
  },
143
184
  [`${pascalCase(uniqueName)}ListResponse`]: {
185
+ type: 'object',
144
186
  properties: {
145
187
  data: {
146
188
  type: 'array',
@@ -152,6 +194,7 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
152
194
  type: 'object',
153
195
  properties: {
154
196
  pagination: {
197
+ type: 'object',
155
198
  properties: {
156
199
  page: { type: 'integer' },
157
200
  pageSize: { type: 'integer', minimum: 25 },
@@ -166,35 +209,29 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
166
209
  };
167
210
  }
168
211
 
212
+ const requiredAttributes = getRequiredAttributes(attributes);
169
213
  // Build the response schema
170
214
  schemas = {
171
215
  ...schemas,
172
- [`${pascalCase(uniqueName)}ResponseDataObject`]: {
216
+ [`${pascalCase(uniqueName)}`]: {
173
217
  type: 'object',
174
- properties: {
175
- id: { type: 'number' },
176
- attributes: {
177
- type: 'object',
178
- properties: cleanSchemaAttributes(attributes, {
179
- addComponentSchema,
180
- componentSchemaRefName: `#/components/schemas/${pascalCase(
181
- uniqueName
182
- )}ResponseDataObjectLocalized`,
183
- }),
184
- },
185
- },
218
+ ...(requiredAttributes.length && { required: requiredAttributes }),
219
+ properties: cleanSchemaAttributes(attributes, {
220
+ didAddStrapiComponentsToSchemas,
221
+ componentSchemaRefName: `#/components/schemas/${pascalCase(uniqueName)}`,
222
+ }),
186
223
  },
187
- [`${pascalCase(uniqueName)}ResponseDataObjectLocalized`]: {
224
+ [`${pascalCase(uniqueName)}ResponseDataObject`]: {
188
225
  type: 'object',
189
226
  properties: {
190
227
  id: { type: 'number' },
191
228
  attributes: {
192
- type: 'object',
193
- properties: cleanSchemaAttributes(attributes, { addComponentSchema }),
229
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}`,
194
230
  },
195
231
  },
196
232
  },
197
233
  [`${pascalCase(uniqueName)}Response`]: {
234
+ type: 'object',
198
235
  properties: {
199
236
  data: {
200
237
  $ref: `#/components/schemas/${pascalCase(uniqueName)}ResponseDataObject`,
@@ -203,7 +240,8 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
203
240
  },
204
241
  },
205
242
  };
206
- return { ...schemas, ...componentSchemas };
243
+
244
+ return { ...schemas, ...strapiComponentSchemas };
207
245
  };
208
246
 
209
247
  const buildComponentSchema = (api) => {
@@ -7,7 +7,7 @@ const pascalCase = require('./pascal-case');
7
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
- * @param {{ typeMap: Map, isRequest: boolean, addComponentSchema: function, componentSchemaRefName: string }} opts
10
+ * @param {{ typeMap: Map, isRequest: boolean, didAddStrapiComponentsToSchemas: function, componentSchemaRefName: string }} opts
11
11
  * @returns Attributes using OpenAPI acceptable data types
12
12
  */
13
13
  const cleanSchemaAttributes = (
@@ -15,7 +15,7 @@ const cleanSchemaAttributes = (
15
15
  {
16
16
  typeMap = new Map(),
17
17
  isRequest = false,
18
- addComponentSchema = () => {},
18
+ didAddStrapiComponentsToSchemas = () => {},
19
19
  componentSchemaRefName = '',
20
20
  } = {}
21
21
  ) => {
@@ -107,7 +107,7 @@ const cleanSchemaAttributes = (
107
107
  const refComponentSchema = {
108
108
  $ref: `#/components/schemas/${pascalCase(attribute.component)}Component`,
109
109
  };
110
- const componentExists = addComponentSchema(
110
+ const componentExists = didAddStrapiComponentsToSchemas(
111
111
  `${pascalCase(attribute.component)}Component`,
112
112
  rawComponentSchema
113
113
  );
@@ -133,12 +133,17 @@ const cleanSchemaAttributes = (
133
133
  ...cleanSchemaAttributes(componentAttributes, {
134
134
  typeMap,
135
135
  isRequest,
136
- addComponentSchema,
136
+ didAddStrapiComponentsToSchemas,
137
137
  }),
138
138
  },
139
139
  };
140
- const refComponentSchema = { $ref: `#/components/schemas/${pascalCase(component)}` };
141
- const componentExists = addComponentSchema(pascalCase(component), rawComponentSchema);
140
+ const refComponentSchema = {
141
+ $ref: `#/components/schemas/${pascalCase(component)}Component`,
142
+ };
143
+ const componentExists = didAddStrapiComponentsToSchemas(
144
+ `${pascalCase(component)}Component`,
145
+ rawComponentSchema
146
+ );
142
147
  const finalComponentSchema = componentExists ? refComponentSchema : rawComponentSchema;
143
148
  return finalComponentSchema;
144
149
  });
@@ -168,7 +173,10 @@ const cleanSchemaAttributes = (
168
173
  attributesCopy[prop] = {
169
174
  type: 'object',
170
175
  properties: {
171
- data: getSchemaData(isListOfEntities, cleanSchemaAttributes(imageAttributes)),
176
+ data: getSchemaData(
177
+ isListOfEntities,
178
+ cleanSchemaAttributes(imageAttributes, { typeMap })
179
+ ),
172
180
  },
173
181
  };
174
182
  break;
@@ -15,7 +15,8 @@ const loopContentTypeNames = (api, callback) => {
15
15
  for (const contentTypeName of api.ctNames) {
16
16
  // Get the attributes found on the api's contentType
17
17
  const uid = `${api.getter}::${api.name}.${contentTypeName}`;
18
- const { attributes, info: contentTypeInfo } = strapi.contentType(uid);
18
+
19
+ const { attributes, info: contentTypeInfo, kind } = strapi.contentType(uid);
19
20
 
20
21
  // Get the routes for the current api
21
22
  const routeInfo =
@@ -39,6 +40,7 @@ const loopContentTypeNames = (api, callback) => {
39
40
  attributes,
40
41
  uniqueName,
41
42
  contentTypeInfo,
43
+ kind,
42
44
  };
43
45
 
44
46
  result = {
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const documentation = require('./documentation');
4
+ const override = require('./override');
4
5
 
5
6
  module.exports = {
6
7
  documentation,
8
+ override,
7
9
  };