@strapi/plugin-documentation 4.3.4 → 4.4.0-alpha.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.
@@ -79,8 +79,8 @@ describe('Build Component Schema', () => {
79
79
  }
80
80
 
81
81
  const schemaNames = Object.keys(schemas);
82
- const pluginListResponseValue = schemas['UsersPermissionsRoleListResponse'];
83
- const apiListResponseValue = schemas['RestaurantListResponse'];
82
+ const pluginListResponseValue = schemas.UsersPermissionsRoleListResponse;
83
+ const apiListResponseValue = schemas.RestaurantListResponse;
84
84
 
85
85
  const expectedShape = {
86
86
  type: 'object',
@@ -144,8 +144,8 @@ describe('Build Component Schema', () => {
144
144
  }
145
145
 
146
146
  const schemaNames = Object.keys(schemas);
147
- const pluginListResponseValue = schemas['UsersPermissionsRoleRequest'];
148
- const apiListResponseValue = schemas['RestaurantRequest'];
147
+ const pluginListResponseValue = schemas.UsersPermissionsRoleRequest;
148
+ const apiListResponseValue = schemas.RestaurantRequest;
149
149
 
150
150
  const expectedShape = {
151
151
  type: 'object',
@@ -192,8 +192,8 @@ describe('Build Component Schema', () => {
192
192
  }
193
193
 
194
194
  const schemaNames = Object.keys(schemas);
195
- const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationResponse'];
196
- const apiListResponseValue = schemas['RestaurantLocalizationResponse'];
195
+ const pluginListResponseValue = schemas.UsersPermissionsRoleLocalizationResponse;
196
+ const apiListResponseValue = schemas.RestaurantLocalizationResponse;
197
197
 
198
198
  const expectedShape = {
199
199
  type: 'object',
@@ -236,8 +236,8 @@ describe('Build Component Schema', () => {
236
236
  }
237
237
 
238
238
  const schemaNames = Object.keys(schemas);
239
- const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationRequest'];
240
- const apiListResponseValue = schemas['RestaurantLocalizationRequest'];
239
+ const pluginListResponseValue = schemas.UsersPermissionsRoleLocalizationRequest;
240
+ const apiListResponseValue = schemas.RestaurantLocalizationRequest;
241
241
 
242
242
  const expectedShape = {
243
243
  type: 'object',
@@ -22,7 +22,7 @@ export default {
22
22
  defaultMessage: 'Documentation',
23
23
  },
24
24
  permissions: pluginPermissions.main,
25
- Component: async () => {
25
+ async Component() {
26
26
  const component = await import(
27
27
  /* webpackChunkName: "documentation-page" */ './pages/PluginPage'
28
28
  );
@@ -44,7 +44,7 @@ export default {
44
44
  },
45
45
  id: 'documentation',
46
46
  to: `/settings/${pluginId}`,
47
- Component: async () => {
47
+ async Component() {
48
48
  const component = await import(
49
49
  /* webpackChunkName: "documentation-settings" */ './pages/SettingsPage'
50
50
  );
@@ -56,7 +56,7 @@ export default {
56
56
  },
57
57
  async registerTrads({ locales }) {
58
58
  const importedTrads = await Promise.all(
59
- locales.map(locale => {
59
+ locales.map((locale) => {
60
60
  return import(
61
61
  /* webpackChunkName: "documentation-translation-[request]" */ `./translations/${locale}.json`
62
62
  )
@@ -49,7 +49,7 @@ const PluginPage = () => {
49
49
  openWithNewTab(`${slash}${data?.prefix}/v${data?.currentVersion}`);
50
50
  };
51
51
 
52
- const handleRegenerateDoc = version => {
52
+ const handleRegenerateDoc = (version) => {
53
53
  regenerateDocMutation.mutate({ version, prefix: data?.prefix });
54
54
  };
55
55
 
@@ -64,7 +64,7 @@ const PluginPage = () => {
64
64
  setIsConfirmButtonLoading(false);
65
65
  };
66
66
 
67
- const handleClickDelete = version => {
67
+ const handleClickDelete = (version) => {
68
68
  setVersionToDelete(version);
69
69
  setShowConfirmDelete(!showConfirmDelete);
70
70
  };
@@ -123,7 +123,7 @@ const PluginPage = () => {
123
123
  <Tbody>
124
124
  {data.docVersions
125
125
  .sort((a, b) => (a.generatedDate < b.generatedDate ? 1 : -1))
126
- .map(doc => (
126
+ .map((doc) => (
127
127
  <Tr key={doc.version}>
128
128
  <Td width="50%">
129
129
  <Typography>{doc.version}</Typography>
@@ -23,7 +23,7 @@ const client = new QueryClient({
23
23
  },
24
24
  });
25
25
 
26
- const makeApp = history => (
26
+ const makeApp = (history) => (
27
27
  <Router history={history}>
28
28
  <ThemeProvider theme={lightTheme}>
29
29
  <QueryClientProvider client={client}>
@@ -36,7 +36,7 @@ const SettingsPage = () => {
36
36
  const { submitMutation, data, isLoading } = useReactQuery();
37
37
  const [passwordShown, setPasswordShown] = useState(false);
38
38
 
39
- const handleUpdateSettingsSubmit = body => {
39
+ const handleUpdateSettingsSubmit = (body) => {
40
40
  submitMutation.mutate({
41
41
  prefix: data?.prefix,
42
42
  body,
@@ -143,9 +143,9 @@ const SettingsPage = () => {
143
143
  }
144
144
  endAction={
145
145
  <FieldActionWrapper
146
- onClick={e => {
146
+ onClick={(e) => {
147
147
  e.stopPropagation();
148
- setPasswordShown(prev => !prev);
148
+ setPasswordShown((prev) => !prev);
149
149
  }}
150
150
  label={formatMessage(
151
151
  passwordShown
@@ -23,7 +23,7 @@ const client = new QueryClient({
23
23
  },
24
24
  });
25
25
 
26
- const makeApp = history => (
26
+ const makeApp = (history) => (
27
27
  <Router history={history}>
28
28
  <ThemeProvider theme={lightTheme}>
29
29
  <QueryClientProvider client={client}>
@@ -5,7 +5,7 @@ const deleteDoc = ({ prefix, version }) => {
5
5
  return request(`${prefix}/deleteDoc/${version}`, { method: 'DELETE' });
6
6
  };
7
7
 
8
- const fetchDocumentationVersions = async toggleNotification => {
8
+ const fetchDocumentationVersions = async (toggleNotification) => {
9
9
  try {
10
10
  const data = await request(`/${pluginId}/getInfos`, { method: 'GET' });
11
11
 
@@ -10,7 +10,7 @@ const useReactQuery = () => {
10
10
  fetchDocumentationVersions(toggleNotification)
11
11
  );
12
12
 
13
- const handleError = err => {
13
+ const handleError = (err) => {
14
14
  toggleNotification({
15
15
  type: 'warning',
16
16
  message: err.response.payload.message,
@@ -27,7 +27,7 @@ const useReactQuery = () => {
27
27
 
28
28
  const deleteMutation = useMutation(deleteDoc, {
29
29
  onSuccess: () => handleSuccess('info', 'notification.delete.success'),
30
- onError: error => handleError(error),
30
+ onError: (error) => handleError(error),
31
31
  });
32
32
 
33
33
  const submitMutation = useMutation(updateSettings, {
@@ -37,7 +37,7 @@ const useReactQuery = () => {
37
37
 
38
38
  const regenerateDocMutation = useMutation(regenerateDoc, {
39
39
  onSuccess: () => handleSuccess('info', 'notification.generate.success'),
40
- onError: error => handleError(error),
40
+ onError: (error) => handleError(error),
41
41
  });
42
42
 
43
43
  return { data, isLoading, deleteMutation, submitMutation, regenerateDocMutation };
@@ -1,5 +1,5 @@
1
1
  import pluginId from '../pluginId';
2
2
 
3
- const getTrad = id => `${pluginId}.${id}`;
3
+ const getTrad = (id) => `${pluginId}.${id}`;
4
4
 
5
5
  export default getTrad;
@@ -1,6 +1,6 @@
1
1
  import { startsWith } from 'lodash';
2
2
 
3
- const openWithNewTab = path => {
3
+ const openWithNewTab = (path) => {
4
4
  const url = (() => {
5
5
  if (startsWith(path, '/')) {
6
6
  return `${strapi.backendURL}${path}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-documentation",
3
- "version": "4.3.4",
3
+ "version": "4.4.0-alpha.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.3.4",
28
- "@strapi/utils": "4.3.4",
27
+ "@strapi/helper-plugin": "4.4.0-alpha.0",
28
+ "@strapi/utils": "4.4.0-alpha.0",
29
29
  "bcryptjs": "2.4.3",
30
30
  "cheerio": "^1.0.0-rc.12",
31
31
  "fs-extra": "10.0.0",
@@ -36,7 +36,7 @@
36
36
  "react": "^17.0.2",
37
37
  "react-copy-to-clipboard": "^5.1.0",
38
38
  "react-dom": "^17.0.2",
39
- "react-intl": "5.20.2",
39
+ "react-intl": "5.25.1",
40
40
  "react-redux": "7.2.8",
41
41
  "react-router": "^5.2.0",
42
42
  "react-router-dom": "5.2.0",
@@ -48,6 +48,10 @@
48
48
  "peerDependencies": {
49
49
  "@strapi/strapi": "^4.0.0"
50
50
  },
51
+ "devDependencies": {
52
+ "@testing-library/react": "11.2.7",
53
+ "msw": "0.42.3"
54
+ },
51
55
  "engines": {
52
56
  "node": ">=14.19.1 <=16.x.x",
53
57
  "npm": ">=6.0.0"
@@ -58,5 +62,5 @@
58
62
  "description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
59
63
  "kind": "plugin"
60
64
  },
61
- "gitHead": "28a2a00db8234ffcf644661c4c8092aa82f8c119"
65
+ "gitHead": "fc78298ae4f9b247d636beda568734d5f8ed7b3e"
62
66
  }
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable no-unreachable */
2
+
2
3
  'use strict';
3
4
 
4
5
  // Add permissions
@@ -49,8 +50,5 @@ module.exports = async ({ strapi }) => {
49
50
  pluginStore.set({ key: 'config', value: { restrictedAccess: false } });
50
51
  }
51
52
 
52
- await strapi
53
- .plugin('documentation')
54
- .service('documentation')
55
- .generateFullDoc();
53
+ await strapi.plugin('documentation').service('documentation').generateFullDoc();
56
54
  };
@@ -43,10 +43,7 @@ module.exports = {
43
43
  const version =
44
44
  major && minor && patch
45
45
  ? `${major}.${minor}.${patch}`
46
- : strapi
47
- .plugin('documentation')
48
- .service('documentation')
49
- .getDocumentationVersion();
46
+ : strapi.plugin('documentation').service('documentation').getDocumentationVersion();
50
47
 
51
48
  const openAPISpecsPath = path.join(
52
49
  strapi.dirs.app.extensions,
@@ -177,7 +174,7 @@ module.exports = {
177
174
 
178
175
  const service = strapi.service('plugin::documentation.documentation');
179
176
 
180
- const documentationVersions = service.getDocumentationVersions().map(el => el.version);
177
+ const documentationVersions = service.getDocumentationVersions().map((el) => el.version);
181
178
 
182
179
  if (_.isEmpty(version)) {
183
180
  return ctx.badRequest('Please provide a version.');
@@ -201,7 +198,7 @@ module.exports = {
201
198
 
202
199
  const service = strapi.service('plugin::documentation.documentation');
203
200
 
204
- const documentationVersions = service.getDocumentationVersions().map(el => el.version);
201
+ const documentationVersions = service.getDocumentationVersions().map((el) => el.version);
205
202
 
206
203
  if (_.isEmpty(version)) {
207
204
  return ctx.badRequest('Please provide a version.');
@@ -1,12 +1,27 @@
1
- <!-- HTML for static distribution bundle build --><!DOCTYPE html><html lang="en"><head>
2
- <meta charset="UTF-8">
1
+ <!-- HTML for static distribution bundle build --><!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
3
5
  <title>Swagger UI</title>
4
- <link rel="stylesheet" type="text/css" href="<%=backendUrl%>/plugins/documentation/swagger-ui.css">
5
- <link rel="icon" type="image/png" href="<%=backendUrl%>/plugins/documentation/favicon-32x32.png" sizes="32x32">
6
- <link rel="icon" type="image/png" href="<%=backendUrl%>/plugins/documentation/favicon-16x16.png" sizes="16x16">
6
+ <link
7
+ rel="stylesheet"
8
+ type="text/css"
9
+ href="<%=backendUrl%>/plugins/documentation/swagger-ui.css"
10
+ />
11
+ <link
12
+ rel="icon"
13
+ type="image/png"
14
+ href="<%=backendUrl%>/plugins/documentation/favicon-32x32.png"
15
+ sizes="32x32"
16
+ />
17
+ <link
18
+ rel="icon"
19
+ type="image/png"
20
+ href="<%=backendUrl%>/plugins/documentation/favicon-16x16.png"
21
+ sizes="16x16"
22
+ />
7
23
  <style>
8
- html
9
- {
24
+ html {
10
25
  box-sizing: border-box;
11
26
  overflow: -moz-scrollbars-vertical;
12
27
  overflow-y: scroll;
@@ -14,14 +29,12 @@
14
29
 
15
30
  *,
16
31
  *:before,
17
- *:after
18
- {
32
+ *:after {
19
33
  box-sizing: inherit;
20
34
  }
21
35
 
22
- body
23
- {
24
- margin:0;
36
+ body {
37
+ margin: 0;
25
38
  background: #fafafa;
26
39
  }
27
40
  </style>
@@ -51,7 +64,7 @@
51
64
  }
52
65
  </script>
53
66
 
54
- <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-bundle.js"> </script>
55
- <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-standalone-preset.js"> </script>
67
+ <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-bundle.js"></script>
68
+ <script src="<%=backendUrl%>/plugins/documentation/swagger-ui-standalone-preset.js"></script>
56
69
  </body>
57
70
  </html>
@@ -1,135 +1,145 @@
1
- <!DOCTYPE html><html><head>
2
- <title>Login - Documentation</title>
3
- <link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet">
4
- <style>
5
- html {
6
- font-size: 62.5%;
7
- height: 100%;
8
- margin: 0;
9
- padding: 0;
10
- }
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Login - Documentation</title>
5
+ <link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" />
6
+ <style>
7
+ html {
8
+ font-size: 62.5%;
9
+ height: 100%;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
11
13
 
12
- body {
13
- height: 100%;
14
- margin: 0;
15
- background-color: #ffffff;
16
- font-family: 'Lato';
17
- font-size: 1.4rem;
18
- font-weight: 400;
19
- text-rendering: optimizeLegibility;
20
- -webkit-font-smoothing: antialiased;
21
- -moz-osx-font-smoothing: grayscale;
22
- }
14
+ body {
15
+ height: 100%;
16
+ margin: 0;
17
+ background-color: #ffffff;
18
+ font-family: 'Lato';
19
+ font-size: 1.4rem;
20
+ font-weight: 400;
21
+ text-rendering: optimizeLegibility;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
23
25
 
24
- .login {
25
- height: 100%;
26
- background-color: #F6F9FC;
27
- }
26
+ .login {
27
+ height: 100%;
28
+ background-color: #f6f9fc;
29
+ }
28
30
 
29
- .login .login-form {
30
- height: calc(100% - 70px);
31
- padding: 68px 0 0;
32
- text-align: center;
33
- }
31
+ .login .login-form {
32
+ height: calc(100% - 70px);
33
+ padding: 68px 0 0;
34
+ text-align: center;
35
+ }
34
36
 
35
- .login .login-form form {
36
- position: relative;
37
- max-width: 460px;
38
- padding: 26px 30px;
39
- margin: 55px auto 0;
40
- background-color: #ffffff;
41
- border-radius: 3px;
42
- box-shadow: 0px 2px 4px rgba(91, 107, 174, .15);
43
- text-align: center;
44
- }
37
+ .login .login-form form {
38
+ position: relative;
39
+ max-width: 460px;
40
+ padding: 26px 30px;
41
+ margin: 55px auto 0;
42
+ background-color: #ffffff;
43
+ border-radius: 3px;
44
+ box-shadow: 0px 2px 4px rgba(91, 107, 174, 0.15);
45
+ text-align: center;
46
+ }
45
47
 
46
- .login .login-form form:before {
47
- position: absolute;
48
- content: '';
49
- top: 0px;
50
- left: 0;
51
- display: inline-block;
52
- width: 100%;
53
- height: 2px;
54
- background-color: #2B66CC;
55
- }
48
+ .login .login-form form:before {
49
+ position: absolute;
50
+ content: '';
51
+ top: 0px;
52
+ left: 0;
53
+ display: inline-block;
54
+ width: 100%;
55
+ height: 2px;
56
+ background-color: #2b66cc;
57
+ }
56
58
 
57
- .login .login-form form .error {
58
- display: block;
59
- color: #FF4E00;
60
- padding-bottom: 20px;
61
- }
59
+ .login .login-form form .error {
60
+ display: block;
61
+ color: #ff4e00;
62
+ padding-bottom: 20px;
63
+ }
62
64
 
63
- .login .login-form .sub-title {
64
- margin-top: 35px;
65
- font-size: 1.6rem;
66
- font-weight: 400;
67
- }
65
+ .login .login-form .sub-title {
66
+ margin-top: 35px;
67
+ font-size: 1.6rem;
68
+ font-weight: 400;
69
+ }
68
70
 
69
- .login .login-form .logo{
70
- max-height: 40px;
71
- }
71
+ .login .login-form .logo {
72
+ max-height: 40px;
73
+ }
72
74
 
73
- .login .login-form form label {
74
- display: block;
75
- margin-bottom: 18px;
76
- width: 100%;
77
- text-align: left;
78
- font-weight: 600;
79
- }
75
+ .login .login-form form label {
76
+ display: block;
77
+ margin-bottom: 18px;
78
+ width: 100%;
79
+ text-align: left;
80
+ font-weight: 600;
81
+ }
80
82
 
81
- .login .login-form form input {
82
- outline: none;
83
- width: calc(100% - 30px);
84
- height: 36px;
85
- padding: 0 15px;
86
- border: 1px solid #ECECEC;
87
- border-radius: 2px;
88
- margin-bottom: 20px;
89
- line-height: 36px;
90
- text-align: left;
91
- }
83
+ .login .login-form form input {
84
+ outline: none;
85
+ width: calc(100% - 30px);
86
+ height: 36px;
87
+ padding: 0 15px;
88
+ border: 1px solid #ececec;
89
+ border-radius: 2px;
90
+ margin-bottom: 20px;
91
+ line-height: 36px;
92
+ text-align: left;
93
+ }
92
94
 
93
- .login .login-form form input[type=submit] {
94
- cursor: pointer;
95
- display: inline-block;
96
- width: auto;
97
- margin: 12px auto 0;
98
- padding: 0 75px;
99
- background: transparent;
100
- border-radius: 36px;
101
- border: 1px solid #2B66CC;
102
- color: #2B66CC;
103
- text-transform: uppercase;
104
- font-size: 1.4rem;
105
- font-weight: 700;
106
- transition: all .2s ease-out;
107
- }
95
+ .login .login-form form input[type='submit'] {
96
+ cursor: pointer;
97
+ display: inline-block;
98
+ width: auto;
99
+ margin: 12px auto 0;
100
+ padding: 0 75px;
101
+ background: transparent;
102
+ border-radius: 36px;
103
+ border: 1px solid #2b66cc;
104
+ color: #2b66cc;
105
+ text-transform: uppercase;
106
+ font-size: 1.4rem;
107
+ font-weight: 700;
108
+ transition: all 0.2s ease-out;
109
+ }
108
110
 
109
- .login .login-form form input[type=submit]:hover {
110
- background: #2B66CC;
111
- color: #ffffff;
112
- }
113
- </style>
114
- </head>
115
- <body>
116
- <div class="login">
117
- <section class="login-form">
118
- <div class="container">
119
- <div class="row">
120
- <div class="col-lg-6 col-lg-offset-3 col-md-12">
121
- <img alt="Strapi logo" class="logo" src="https://strapi.io/assets/images/logo_login.png">
122
- <h2 class="sub-title">Enter the password to access the documentation.</h2>
123
- <form method="post" action="<%=actionUrl%>">
124
- <span class="error">Wrong password...</span>
125
- <label>Password</label>
126
- <input type="password" name="password" placeholder="&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;">
127
- <input type="submit" value="Login">
128
- </form>
129
- </div>
111
+ .login .login-form form input[type='submit']:hover {
112
+ background: #2b66cc;
113
+ color: #ffffff;
114
+ }
115
+ </style>
116
+ </head>
117
+ <body>
118
+ <div class="login">
119
+ <section class="login-form">
120
+ <div class="container">
121
+ <div class="row">
122
+ <div class="col-lg-6 col-lg-offset-3 col-md-12">
123
+ <img
124
+ alt="Strapi logo"
125
+ class="logo"
126
+ src="https://strapi.io/assets/images/logo_login.png"
127
+ />
128
+ <h2 class="sub-title">Enter the password to access the documentation.</h2>
129
+ <form method="post" action="<%=actionUrl%>">
130
+ <span class="error">Wrong password...</span>
131
+ <label>Password</label>
132
+ <input
133
+ type="password"
134
+ name="password"
135
+ placeholder="&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;"
136
+ />
137
+ <input type="submit" value="Login" />
138
+ </form>
130
139
  </div>
131
140
  </div>
132
- </section>
133
- </div>
134
-
135
- </body></html>
141
+ </div>
142
+ </section>
143
+ </div>
144
+ </body>
145
+ </html>
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+
2
3
  const restrictAccess = require('../middlewares/restrict-access');
3
4
 
4
5
  module.exports = [
@@ -38,7 +38,7 @@ module.exports = ({ strapi }) => {
38
38
  getDocumentationVersions() {
39
39
  return fs
40
40
  .readdirSync(this.getFullDocumentationPath())
41
- .map(version => {
41
+ .map((version) => {
42
42
  try {
43
43
  const doc = JSON.parse(
44
44
  fs.readFileSync(
@@ -52,7 +52,7 @@ module.exports = ({ strapi }) => {
52
52
  return null;
53
53
  }
54
54
  })
55
- .filter(x => x);
55
+ .filter((x) => x);
56
56
  },
57
57
 
58
58
  /**
@@ -99,7 +99,7 @@ module.exports = ({ strapi }) => {
99
99
 
100
100
  getPluginAndApiInfo() {
101
101
  const plugins = _.get(config, 'x-strapi-config.plugins');
102
- const pluginsToDocument = plugins.map(plugin => {
102
+ const pluginsToDocument = plugins.map((plugin) => {
103
103
  return {
104
104
  name: plugin,
105
105
  getter: 'plugin',
@@ -107,7 +107,7 @@ module.exports = ({ strapi }) => {
107
107
  };
108
108
  });
109
109
 
110
- const apisToDocument = Object.keys(strapi.api).map(api => {
110
+ const apisToDocument = Object.keys(strapi.api).map((api) => {
111
111
  return {
112
112
  name: api,
113
113
  getter: 'api',
@@ -186,7 +186,7 @@ module.exports = ({ strapi }) => {
186
186
 
187
187
  const finalDoc = { ...config, paths };
188
188
 
189
- registeredDocs.forEach(doc => {
189
+ registeredDocs.forEach((doc) => {
190
190
  // Add tags
191
191
  finalDoc.tags = finalDoc.tags || [];
192
192
  finalDoc.tags.push(...(doc.tags || []));
@@ -15,12 +15,12 @@ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
15
15
  * @param {string} routePath - The route's path property
16
16
  * @returns {string}
17
17
  */
18
- const parsePathWithVariables = routePath => {
18
+ const parsePathWithVariables = (routePath) => {
19
19
  return pathToRegexp
20
20
  .parse(routePath)
21
- .map(token => {
21
+ .map((token) => {
22
22
  if (_.isObject(token)) {
23
- return token.prefix + '{' + token.name + '}';
23
+ return `${token.prefix}{${token.name}}`;
24
24
  }
25
25
 
26
26
  return token;
@@ -35,11 +35,11 @@ const parsePathWithVariables = routePath => {
35
35
  *
36
36
  * @returns {object } Swagger path params object
37
37
  */
38
- const getPathParams = routePath => {
38
+ const getPathParams = (routePath) => {
39
39
  return pathToRegexp
40
40
  .parse(routePath)
41
- .filter(token => _.isObject(token))
42
- .map(param => {
41
+ .filter((token) => _.isObject(token))
42
+ .map((param) => {
43
43
  return {
44
44
  name: param.name,
45
45
  in: 'path',
@@ -83,7 +83,7 @@ const getPathWithPrefix = (prefix, route) => {
83
83
  */
84
84
  const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
85
85
  // Get the routes for the current content type
86
- const contentTypeRoutes = routeInfo.routes.filter(route => {
86
+ const contentTypeRoutes = routeInfo.routes.filter((route) => {
87
87
  return (
88
88
  route.path.includes(contentTypeInfo.pluralName) ||
89
89
  route.path.includes(contentTypeInfo.singularName)
@@ -152,7 +152,7 @@ const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
152
152
  *
153
153
  * @returns {object} Open API paths
154
154
  */
155
- const getAllPathsForContentType = apiInfo => {
155
+ const getAllPathsForContentType = (apiInfo) => {
156
156
  let paths = {};
157
157
 
158
158
  const pathsObject = getPaths(apiInfo);
@@ -175,7 +175,7 @@ const getAllPathsForContentType = apiInfo => {
175
175
  *
176
176
  * @returns {object}
177
177
  */
178
- const buildApiEndpointPath = api => {
178
+ const buildApiEndpointPath = (api) => {
179
179
  // A reusable loop for building paths and component schemas
180
180
  // Uses the api param to build a new set of params for each content type
181
181
  // Passes these new params to the function provided
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+
2
3
  const _ = require('lodash');
3
4
 
4
5
  const cleanSchemaAttributes = require('./utils/clean-schema-attributes');
@@ -19,10 +20,24 @@ const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
19
20
  const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
20
21
  // Store response and request schemas in an object
21
22
  let schemas = {};
23
+ let componentSchemas = {};
24
+ // 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,
31
+ [schemaName]: schema,
32
+ };
33
+ return true;
34
+ };
22
35
  // Get all the route methods
23
- const routeMethods = routeInfo.routes.map(route => route.method);
36
+ const routeMethods = routeInfo.routes.map((route) => route.method);
24
37
  // Check for localized paths
25
- const hasLocalizationPath = routeInfo.routes.filter(route => isLocalizedPath(route.path)).length;
38
+ const hasLocalizationPath = routeInfo.routes.filter((route) =>
39
+ isLocalizedPath(route.path)
40
+ ).length;
26
41
  // When the route methods contain any post or put requests
27
42
  if (routeMethods.includes('POST') || routeMethods.includes('PUT')) {
28
43
  const attributesToOmit = [
@@ -53,7 +68,10 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
53
68
  [`${pascalCase(uniqueName)}LocalizationRequest`]: {
54
69
  required: [...requiredAttributes, 'locale'],
55
70
  type: 'object',
56
- properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
71
+ properties: cleanSchemaAttributes(attributesForRequest, {
72
+ isRequest: true,
73
+ addComponentSchema,
74
+ }),
57
75
  },
58
76
  };
59
77
  }
@@ -68,7 +86,10 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
68
86
  data: {
69
87
  required: requiredAttributes,
70
88
  type: 'object',
71
- properties: cleanSchemaAttributes(attributesForRequest, { isRequest: true }),
89
+ properties: cleanSchemaAttributes(attributesForRequest, {
90
+ isRequest: true,
91
+ addComponentSchema,
92
+ }),
72
93
  },
73
94
  },
74
95
  },
@@ -82,29 +103,49 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
82
103
  type: 'object',
83
104
  properties: {
84
105
  id: { type: 'string' },
85
- ...cleanSchemaAttributes(attributes),
106
+ ...cleanSchemaAttributes(attributes, { addComponentSchema }),
86
107
  },
87
108
  },
88
109
  };
89
110
  }
90
111
 
91
112
  // Check for routes that need to return a list
92
- const hasListOfEntities = routeInfo.routes.filter(route => hasFindMethod(route.handler)).length;
113
+ const hasListOfEntities = routeInfo.routes.filter((route) => hasFindMethod(route.handler)).length;
93
114
  if (hasListOfEntities) {
94
115
  // Build the list response schema
95
116
  schemas = {
96
117
  ...schemas,
97
- [`${pascalCase(uniqueName)}ListResponse`]: {
118
+ [`${pascalCase(uniqueName)}ListResponseDataItem`]: {
119
+ type: 'object',
120
+ properties: {
121
+ id: { type: 'string' },
122
+ 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`]: {
98
134
  type: 'object',
135
+ properties: {
136
+ id: { type: 'string' },
137
+ attributes: {
138
+ type: 'object',
139
+ properties: cleanSchemaAttributes(attributes, { addComponentSchema }),
140
+ },
141
+ },
142
+ },
143
+ [`${pascalCase(uniqueName)}ListResponse`]: {
99
144
  properties: {
100
145
  data: {
101
146
  type: 'array',
102
147
  items: {
103
- type: 'object',
104
- properties: {
105
- id: { type: 'string' },
106
- attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
107
- },
148
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}ListResponseDataItem`,
108
149
  },
109
150
  },
110
151
  meta: {
@@ -128,25 +169,44 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
128
169
  // Build the response schema
129
170
  schemas = {
130
171
  ...schemas,
131
- [`${pascalCase(uniqueName)}Response`]: {
172
+ [`${pascalCase(uniqueName)}ResponseDataObject`]: {
132
173
  type: 'object',
133
174
  properties: {
134
- data: {
175
+ id: { type: 'string' },
176
+ attributes: {
135
177
  type: 'object',
136
- properties: {
137
- id: { type: 'string' },
138
- attributes: { type: 'object', properties: cleanSchemaAttributes(attributes) },
139
- },
178
+ properties: cleanSchemaAttributes(attributes, {
179
+ addComponentSchema,
180
+ componentSchemaRefName: `#/components/schemas/${pascalCase(
181
+ uniqueName
182
+ )}ResponseDataObjectLocalized`,
183
+ }),
184
+ },
185
+ },
186
+ },
187
+ [`${pascalCase(uniqueName)}ResponseDataObjectLocalized`]: {
188
+ type: 'object',
189
+ properties: {
190
+ id: { type: 'string' },
191
+ attributes: {
192
+ type: 'object',
193
+ properties: cleanSchemaAttributes(attributes, { addComponentSchema }),
194
+ },
195
+ },
196
+ },
197
+ [`${pascalCase(uniqueName)}Response`]: {
198
+ properties: {
199
+ data: {
200
+ $ref: `#/components/schemas/${pascalCase(uniqueName)}ResponseDataObject`,
140
201
  },
141
202
  meta: { type: 'object' },
142
203
  },
143
204
  },
144
205
  };
145
-
146
- return schemas;
206
+ return { ...schemas, ...componentSchemas };
147
207
  };
148
208
 
149
- const buildComponentSchema = api => {
209
+ const buildComponentSchema = (api) => {
150
210
  // A reusable loop for building paths and component schemas
151
211
  // Uses the api param to build a new set of params for each content type
152
212
  // Passes these new params to the function provided
@@ -2,18 +2,26 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const getSchemaData = require('./get-schema-data');
5
-
5
+ const pascalCase = require('./pascal-case');
6
6
  /**
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 }} opts
10
+ * @param {{ typeMap: Map, isRequest: boolean, addComponentSchema: function, componentSchemaRefName: string }} opts
11
11
  * @returns Attributes using OpenAPI acceptable data types
12
12
  */
13
- const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = false } = {}) => {
13
+ const cleanSchemaAttributes = (
14
+ attributes,
15
+ {
16
+ typeMap = new Map(),
17
+ isRequest = false,
18
+ addComponentSchema = () => {},
19
+ componentSchemaRefName = '',
20
+ } = {}
21
+ ) => {
14
22
  const attributesCopy = _.cloneDeep(attributes);
15
23
 
16
- for (const prop in attributesCopy) {
24
+ for (const prop of Object.keys(attributesCopy)) {
17
25
  const attribute = attributesCopy[prop];
18
26
  if (attribute.default) {
19
27
  delete attributesCopy[prop].default;
@@ -86,43 +94,53 @@ const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = fa
86
94
  }
87
95
  case 'component': {
88
96
  const componentAttributes = strapi.components[attribute.component].attributes;
89
-
97
+ const rawComponentSchema = {
98
+ type: 'object',
99
+ properties: {
100
+ ...(isRequest ? {} : { id: { type: 'string' } }),
101
+ ...cleanSchemaAttributes(componentAttributes, {
102
+ typeMap,
103
+ isRequest,
104
+ }),
105
+ },
106
+ };
107
+ const refComponentSchema = {
108
+ $ref: `#/components/schemas/${pascalCase(attribute.component)}Component`,
109
+ };
110
+ const componentExists = addComponentSchema(
111
+ `${pascalCase(attribute.component)}Component`,
112
+ rawComponentSchema
113
+ );
114
+ const finalComponentSchema = componentExists ? refComponentSchema : rawComponentSchema;
90
115
  if (attribute.repeatable) {
91
116
  attributesCopy[prop] = {
92
117
  type: 'array',
93
- items: {
94
- type: 'object',
95
- properties: {
96
- ...(isRequest ? {} : { id: { type: 'string' } }),
97
- ...cleanSchemaAttributes(componentAttributes, { typeMap, isRequest }),
98
- },
99
- },
118
+ items: finalComponentSchema,
100
119
  };
101
120
  } else {
102
- attributesCopy[prop] = {
103
- type: 'object',
104
- properties: {
105
- ...(isRequest ? {} : { id: { type: 'string' } }),
106
- ...cleanSchemaAttributes(componentAttributes, {
107
- typeMap,
108
- isRequest,
109
- }),
110
- },
111
- };
121
+ attributesCopy[prop] = finalComponentSchema;
112
122
  }
113
123
  break;
114
124
  }
115
125
  case 'dynamiczone': {
116
- const components = attribute.components.map(component => {
126
+ const components = attribute.components.map((component) => {
117
127
  const componentAttributes = strapi.components[component].attributes;
118
- return {
128
+ const rawComponentSchema = {
119
129
  type: 'object',
120
130
  properties: {
121
131
  ...(isRequest ? {} : { id: { type: 'string' } }),
122
132
  __component: { type: 'string' },
123
- ...cleanSchemaAttributes(componentAttributes, { typeMap, isRequest }),
133
+ ...cleanSchemaAttributes(componentAttributes, {
134
+ typeMap,
135
+ isRequest,
136
+ addComponentSchema,
137
+ }),
124
138
  },
125
139
  };
140
+ const refComponentSchema = { $ref: `#/components/schemas/${pascalCase(component)}` };
141
+ const componentExists = addComponentSchema(pascalCase(component), rawComponentSchema);
142
+ const finalComponentSchema = componentExists ? refComponentSchema : rawComponentSchema;
143
+ return finalComponentSchema;
126
144
  });
127
145
 
128
146
  attributesCopy[prop] = {
@@ -171,8 +189,13 @@ const cleanSchemaAttributes = (attributes, { typeMap = new Map(), isRequest = fa
171
189
 
172
190
  if (prop === 'localizations') {
173
191
  attributesCopy[prop] = {
174
- type: 'array',
175
- items: { type: 'object', properties: {} },
192
+ type: 'object',
193
+ properties: {
194
+ data: {
195
+ type: 'array',
196
+ items: componentSchemaRefName.length ? { $ref: componentSchemaRefName } : {},
197
+ },
198
+ },
176
199
  };
177
200
  break;
178
201
  }
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+
2
3
  const _ = require('lodash');
3
4
 
4
5
  /**
@@ -2,7 +2,7 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
 
5
- const pascalCase = string => {
5
+ const pascalCase = (string) => {
6
6
  return _.upperFirst(_.camelCase(string));
7
7
  };
8
8
 
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const hasFindMethod = handler => handler.split('.').pop() === 'find';
3
+ const hasFindMethod = (handler) => handler.split('.').pop() === 'find';
4
4
 
5
- const isLocalizedPath = routePath => routePath.includes('localizations');
5
+ const isLocalizedPath = (routePath) => routePath.includes('localizations');
6
6
 
7
7
  module.exports = {
8
8
  isLocalizedPath,