@strapi/content-type-builder 5.9.0 → 5.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.
Files changed (210) hide show
  1. package/dist/admin/chunks/ListView-CDnrvVrV.mjs +1184 -0
  2. package/dist/admin/chunks/ListView-CDnrvVrV.mjs.map +1 -0
  3. package/dist/admin/chunks/ListView-CQwvSbZH.js +1186 -0
  4. package/dist/admin/chunks/ListView-CQwvSbZH.js.map +1 -0
  5. package/dist/admin/chunks/ar-Df0f0-PT.js +52 -0
  6. package/dist/admin/chunks/ar-Df0f0-PT.js.map +1 -0
  7. package/dist/admin/chunks/ar-sRW9VFC-.mjs +49 -0
  8. package/dist/admin/chunks/ar-sRW9VFC-.mjs.map +1 -0
  9. package/dist/admin/chunks/cs-BpQ26jiq.mjs +136 -0
  10. package/dist/{_chunks/cs-ChL4LaFY.mjs.map → admin/chunks/cs-BpQ26jiq.mjs.map} +1 -1
  11. package/dist/admin/chunks/cs-DeTwqc7p.js +140 -0
  12. package/dist/{_chunks/cs-Ci3js5EC.js.map → admin/chunks/cs-DeTwqc7p.js.map} +1 -1
  13. package/dist/admin/chunks/de-BJkS06jF.js +194 -0
  14. package/dist/{_chunks/de-DnlblIOh.js.map → admin/chunks/de-BJkS06jF.js.map} +1 -1
  15. package/dist/admin/chunks/de-DSxx5_x-.mjs +190 -0
  16. package/dist/{_chunks/de-DsHQNzp2.mjs.map → admin/chunks/de-DSxx5_x-.mjs.map} +1 -1
  17. package/dist/admin/chunks/dk-BnjVZ7A_.mjs +180 -0
  18. package/dist/{_chunks/es-BE_zx2_w.mjs.map → admin/chunks/dk-BnjVZ7A_.mjs.map} +1 -1
  19. package/dist/admin/chunks/dk-CGm-qVH7.js +184 -0
  20. package/dist/{_chunks/dk-D3XnOjYz.js.map → admin/chunks/dk-CGm-qVH7.js.map} +1 -1
  21. package/dist/admin/chunks/en-BJUu34b0.js +217 -0
  22. package/dist/{_chunks/en-CXG5y_vo.js.map → admin/chunks/en-BJUu34b0.js.map} +1 -1
  23. package/dist/admin/chunks/en-Bhut8Yay.mjs +213 -0
  24. package/dist/{_chunks/en-jBwb53yg.mjs.map → admin/chunks/en-Bhut8Yay.mjs.map} +1 -1
  25. package/dist/admin/chunks/es-DG8g9igJ.mjs +180 -0
  26. package/dist/admin/chunks/es-DG8g9igJ.mjs.map +1 -0
  27. package/dist/admin/chunks/es-J8kvHlNy.js +184 -0
  28. package/dist/{_chunks/es-DL8lez9W.js.map → admin/chunks/es-J8kvHlNy.js.map} +1 -1
  29. package/dist/admin/chunks/fr-C6y35iY7.js +76 -0
  30. package/dist/admin/chunks/fr-C6y35iY7.js.map +1 -0
  31. package/dist/admin/chunks/fr-UpV34MHY.mjs +73 -0
  32. package/dist/admin/chunks/fr-UpV34MHY.mjs.map +1 -0
  33. package/dist/admin/chunks/id-BWM18ljw.mjs +163 -0
  34. package/dist/{_chunks/ru-DGSjru5m.mjs.map → admin/chunks/id-BWM18ljw.mjs.map} +1 -1
  35. package/dist/admin/chunks/id-BvxV6wLP.js +167 -0
  36. package/dist/{_chunks/ru-C8A_4j0w.js.map → admin/chunks/id-BvxV6wLP.js.map} +1 -1
  37. package/dist/admin/chunks/index-BQ2VO38W.js +7781 -0
  38. package/dist/admin/chunks/index-BQ2VO38W.js.map +1 -0
  39. package/dist/admin/chunks/index-BZeN5KRn.js +1421 -0
  40. package/dist/admin/chunks/index-BZeN5KRn.js.map +1 -0
  41. package/dist/admin/chunks/index-BhX2euW0.mjs +1384 -0
  42. package/dist/admin/chunks/index-BhX2euW0.mjs.map +1 -0
  43. package/dist/admin/chunks/index-Cr5tfW7U.mjs +7754 -0
  44. package/dist/admin/chunks/index-Cr5tfW7U.mjs.map +1 -0
  45. package/dist/admin/chunks/it-1_vd9gV4.mjs +164 -0
  46. package/dist/{_chunks/tr-DsUerr-c.mjs.map → admin/chunks/it-1_vd9gV4.mjs.map} +1 -1
  47. package/dist/admin/chunks/it-C_IgFU-G.js +168 -0
  48. package/dist/{_chunks/sk-raWRcmPT.js.map → admin/chunks/it-C_IgFU-G.js.map} +1 -1
  49. package/dist/admin/chunks/ja-CWo4Qqq6.js +51 -0
  50. package/dist/admin/chunks/ja-CWo4Qqq6.js.map +1 -0
  51. package/dist/admin/chunks/ja-Cx23a2Ui.mjs +48 -0
  52. package/dist/admin/chunks/ja-Cx23a2Ui.mjs.map +1 -0
  53. package/dist/admin/chunks/ko-BsByJNEl.js +184 -0
  54. package/dist/admin/chunks/ko-BsByJNEl.js.map +1 -0
  55. package/dist/admin/chunks/ko-DC7paEx5.mjs +180 -0
  56. package/dist/admin/chunks/ko-DC7paEx5.mjs.map +1 -0
  57. package/dist/admin/chunks/ms-C3s4kxq6.mjs +160 -0
  58. package/dist/{_chunks/id-W1sKBFEw.mjs.map → admin/chunks/ms-C3s4kxq6.mjs.map} +1 -1
  59. package/dist/admin/chunks/ms-DPTzS7SH.js +164 -0
  60. package/dist/{_chunks/th-C83Bb_kR.js.map → admin/chunks/ms-DPTzS7SH.js.map} +1 -1
  61. package/dist/admin/chunks/nl-TzvfktV_.mjs +153 -0
  62. package/dist/{_chunks/nl-BaTAuelQ.mjs.map → admin/chunks/nl-TzvfktV_.mjs.map} +1 -1
  63. package/dist/admin/chunks/nl-db29QMOx.js +157 -0
  64. package/dist/{_chunks/nl-DQjrDEw0.js.map → admin/chunks/nl-db29QMOx.js.map} +1 -1
  65. package/dist/admin/chunks/pl-BdvupIN_.mjs +190 -0
  66. package/dist/admin/chunks/pl-BdvupIN_.mjs.map +1 -0
  67. package/dist/admin/chunks/pl-pYy1djj3.js +194 -0
  68. package/dist/admin/chunks/pl-pYy1djj3.js.map +1 -0
  69. package/dist/admin/chunks/pt-BQmWcdeG.js +52 -0
  70. package/dist/admin/chunks/pt-BQmWcdeG.js.map +1 -0
  71. package/dist/admin/chunks/pt-BR-CTPuXGWF.js +194 -0
  72. package/dist/{_chunks/pt-BR-DPd5nRnl.js.map → admin/chunks/pt-BR-CTPuXGWF.js.map} +1 -1
  73. package/dist/admin/chunks/pt-BR-DPrVmKeZ.mjs +190 -0
  74. package/dist/{_chunks/pt-BR-CCQGwXs0.mjs.map → admin/chunks/pt-BR-DPrVmKeZ.mjs.map} +1 -1
  75. package/dist/admin/chunks/pt-BTLIwmCv.mjs +49 -0
  76. package/dist/admin/chunks/pt-BTLIwmCv.mjs.map +1 -0
  77. package/dist/admin/chunks/ru-D46no502.mjs +165 -0
  78. package/dist/{_chunks/dk-BC7NAQR2.mjs.map → admin/chunks/ru-D46no502.mjs.map} +1 -1
  79. package/dist/admin/chunks/ru-DQiDXgUV.js +169 -0
  80. package/dist/admin/chunks/ru-DQiDXgUV.js.map +1 -0
  81. package/dist/admin/chunks/sk-Byr_l4Jc.mjs +164 -0
  82. package/dist/{_chunks/ko-DoNsXHXA.mjs.map → admin/chunks/sk-Byr_l4Jc.mjs.map} +1 -1
  83. package/dist/admin/chunks/sk-DrnebmXb.js +168 -0
  84. package/dist/{_chunks/it-DS4sM3km.js.map → admin/chunks/sk-DrnebmXb.js.map} +1 -1
  85. package/dist/admin/chunks/sv-Bbam7IDm.mjs +199 -0
  86. package/dist/admin/chunks/sv-Bbam7IDm.mjs.map +1 -0
  87. package/dist/admin/chunks/sv-CrWlNosi.js +203 -0
  88. package/dist/admin/chunks/sv-CrWlNosi.js.map +1 -0
  89. package/dist/admin/chunks/th-BbrCkfgX.js +165 -0
  90. package/dist/{_chunks/id-DYuTgqcc.js.map → admin/chunks/th-BbrCkfgX.js.map} +1 -1
  91. package/dist/admin/chunks/th-hfS0Wmk_.mjs +161 -0
  92. package/dist/{_chunks/it-D04lb2Wc.mjs.map → admin/chunks/th-hfS0Wmk_.mjs.map} +1 -1
  93. package/dist/admin/chunks/tr-CHdMj8m6.js +180 -0
  94. package/dist/admin/chunks/tr-CHdMj8m6.js.map +1 -0
  95. package/dist/admin/chunks/tr-DS7DBOhC.mjs +176 -0
  96. package/dist/admin/chunks/tr-DS7DBOhC.mjs.map +1 -0
  97. package/dist/admin/chunks/uk-BQEQ3weH.js +165 -0
  98. package/dist/{_chunks/uk-VwB0oiuV.js.map → admin/chunks/uk-BQEQ3weH.js.map} +1 -1
  99. package/dist/admin/chunks/uk-Cj8-BKeu.mjs +161 -0
  100. package/dist/{_chunks/sk-DVK4HfSC.mjs.map → admin/chunks/uk-Cj8-BKeu.mjs.map} +1 -1
  101. package/dist/admin/chunks/zh-BUVXH75-.mjs +199 -0
  102. package/dist/admin/chunks/zh-BUVXH75-.mjs.map +1 -0
  103. package/dist/admin/chunks/zh-CWj4avQA.js +203 -0
  104. package/dist/admin/chunks/zh-CWj4avQA.js.map +1 -0
  105. package/dist/admin/chunks/zh-Hans-BElOnuRb.mjs +144 -0
  106. package/dist/{_chunks/zh-Hans-Cc0M5PXr.mjs.map → admin/chunks/zh-Hans-BElOnuRb.mjs.map} +1 -1
  107. package/dist/admin/chunks/zh-Hans-lXbNiMp9.js +148 -0
  108. package/dist/{_chunks/zh-Hans-CLTLm_nt.js.map → admin/chunks/zh-Hans-lXbNiMp9.js.map} +1 -1
  109. package/dist/admin/index.js +24 -4
  110. package/dist/admin/index.js.map +1 -1
  111. package/dist/admin/index.mjs +17 -7
  112. package/dist/admin/index.mjs.map +1 -1
  113. package/dist/admin/src/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.d.ts +5 -2
  114. package/dist/admin/src/components/DataManagerProvider/reducer.d.ts +97 -6
  115. package/dist/admin/src/components/FormModal/reducer.d.ts +110 -4
  116. package/dist/admin/src/components/ListRow.d.ts +2 -1
  117. package/dist/admin/src/contexts/DataManagerContext.d.ts +4 -4
  118. package/dist/admin/src/index.d.ts +2 -1
  119. package/dist/admin/src/pages/ListView/LinkToCMSettingsView.d.ts +1 -2
  120. package/dist/admin/src/pluginId.d.ts +1 -1
  121. package/dist/admin/src/reducers.d.ts +8 -1
  122. package/dist/admin/src/types.d.ts +2 -5
  123. package/dist/server/index.js +2504 -2151
  124. package/dist/server/index.js.map +1 -1
  125. package/dist/server/index.mjs +2500 -2147
  126. package/dist/server/index.mjs.map +1 -1
  127. package/package.json +13 -10
  128. package/dist/_chunks/ListView-B7k6NgwS.mjs +0 -959
  129. package/dist/_chunks/ListView-B7k6NgwS.mjs.map +0 -1
  130. package/dist/_chunks/ListView-CsRxS9zZ.js +0 -964
  131. package/dist/_chunks/ListView-CsRxS9zZ.js.map +0 -1
  132. package/dist/_chunks/ar-BYDB75EB.mjs +0 -51
  133. package/dist/_chunks/ar-BYDB75EB.mjs.map +0 -1
  134. package/dist/_chunks/ar-OCxhAFUy.js +0 -51
  135. package/dist/_chunks/ar-OCxhAFUy.js.map +0 -1
  136. package/dist/_chunks/cs-ChL4LaFY.mjs +0 -139
  137. package/dist/_chunks/cs-Ci3js5EC.js +0 -139
  138. package/dist/_chunks/de-DnlblIOh.js +0 -193
  139. package/dist/_chunks/de-DsHQNzp2.mjs +0 -193
  140. package/dist/_chunks/dk-BC7NAQR2.mjs +0 -183
  141. package/dist/_chunks/dk-D3XnOjYz.js +0 -183
  142. package/dist/_chunks/en-CXG5y_vo.js +0 -216
  143. package/dist/_chunks/en-jBwb53yg.mjs +0 -216
  144. package/dist/_chunks/es-BE_zx2_w.mjs +0 -183
  145. package/dist/_chunks/es-DL8lez9W.js +0 -183
  146. package/dist/_chunks/fr-DnTxugIo.js +0 -75
  147. package/dist/_chunks/fr-DnTxugIo.js.map +0 -1
  148. package/dist/_chunks/fr-lU_OMJma.mjs +0 -75
  149. package/dist/_chunks/fr-lU_OMJma.mjs.map +0 -1
  150. package/dist/_chunks/id-DYuTgqcc.js +0 -166
  151. package/dist/_chunks/id-W1sKBFEw.mjs +0 -166
  152. package/dist/_chunks/index-97hm9i_H.mjs +0 -1331
  153. package/dist/_chunks/index-97hm9i_H.mjs.map +0 -1
  154. package/dist/_chunks/index-B5tHY87r.mjs +0 -6694
  155. package/dist/_chunks/index-B5tHY87r.mjs.map +0 -1
  156. package/dist/_chunks/index-BgMd59JL.js +0 -6730
  157. package/dist/_chunks/index-BgMd59JL.js.map +0 -1
  158. package/dist/_chunks/index-Cr85ijm8.js +0 -1357
  159. package/dist/_chunks/index-Cr85ijm8.js.map +0 -1
  160. package/dist/_chunks/it-D04lb2Wc.mjs +0 -167
  161. package/dist/_chunks/it-DS4sM3km.js +0 -167
  162. package/dist/_chunks/ja-BHLK_2_g.mjs +0 -50
  163. package/dist/_chunks/ja-BHLK_2_g.mjs.map +0 -1
  164. package/dist/_chunks/ja-BjouJgZf.js +0 -50
  165. package/dist/_chunks/ja-BjouJgZf.js.map +0 -1
  166. package/dist/_chunks/ko-D_71Pdfn.js +0 -183
  167. package/dist/_chunks/ko-D_71Pdfn.js.map +0 -1
  168. package/dist/_chunks/ko-DoNsXHXA.mjs +0 -183
  169. package/dist/_chunks/ms-BtGFDB9t.mjs +0 -163
  170. package/dist/_chunks/ms-BtGFDB9t.mjs.map +0 -1
  171. package/dist/_chunks/ms-Re1pSHmx.js +0 -163
  172. package/dist/_chunks/ms-Re1pSHmx.js.map +0 -1
  173. package/dist/_chunks/nl-BaTAuelQ.mjs +0 -156
  174. package/dist/_chunks/nl-DQjrDEw0.js +0 -156
  175. package/dist/_chunks/pl-BGwXgwH7.js +0 -193
  176. package/dist/_chunks/pl-BGwXgwH7.js.map +0 -1
  177. package/dist/_chunks/pl-CP2Zgp01.mjs +0 -193
  178. package/dist/_chunks/pl-CP2Zgp01.mjs.map +0 -1
  179. package/dist/_chunks/pt-BR-CCQGwXs0.mjs +0 -193
  180. package/dist/_chunks/pt-BR-DPd5nRnl.js +0 -193
  181. package/dist/_chunks/pt-CJoUDTHQ.js +0 -51
  182. package/dist/_chunks/pt-CJoUDTHQ.js.map +0 -1
  183. package/dist/_chunks/pt-DMeTMW2x.mjs +0 -51
  184. package/dist/_chunks/pt-DMeTMW2x.mjs.map +0 -1
  185. package/dist/_chunks/ru-C8A_4j0w.js +0 -168
  186. package/dist/_chunks/ru-DGSjru5m.mjs +0 -168
  187. package/dist/_chunks/sk-DVK4HfSC.mjs +0 -167
  188. package/dist/_chunks/sk-raWRcmPT.js +0 -167
  189. package/dist/_chunks/sv-BGb12eW3.mjs +0 -202
  190. package/dist/_chunks/sv-BGb12eW3.mjs.map +0 -1
  191. package/dist/_chunks/sv-BNN71SFE.js +0 -202
  192. package/dist/_chunks/sv-BNN71SFE.js.map +0 -1
  193. package/dist/_chunks/th--u3VqsON.mjs +0 -164
  194. package/dist/_chunks/th--u3VqsON.mjs.map +0 -1
  195. package/dist/_chunks/th-C83Bb_kR.js +0 -164
  196. package/dist/_chunks/tr-BW20CfcO.js +0 -179
  197. package/dist/_chunks/tr-BW20CfcO.js.map +0 -1
  198. package/dist/_chunks/tr-DsUerr-c.mjs +0 -179
  199. package/dist/_chunks/uk-Bx5IlOKX.mjs +0 -164
  200. package/dist/_chunks/uk-Bx5IlOKX.mjs.map +0 -1
  201. package/dist/_chunks/uk-VwB0oiuV.js +0 -164
  202. package/dist/_chunks/zh-BiOCwPJu.js +0 -202
  203. package/dist/_chunks/zh-BiOCwPJu.js.map +0 -1
  204. package/dist/_chunks/zh-CsUDN13W.mjs +0 -202
  205. package/dist/_chunks/zh-CsUDN13W.mjs.map +0 -1
  206. package/dist/_chunks/zh-Hans-CLTLm_nt.js +0 -147
  207. package/dist/_chunks/zh-Hans-Cc0M5PXr.mjs +0 -147
  208. package/dist/admin/src/components/DataManagerProvider/constants.d.ts +0 -17
  209. package/dist/admin/src/components/FormModal/constants.d.ts +0 -12
  210. package/dist/admin/src/pages/RecursivePath/RecursivePath.d.ts +0 -1
@@ -1,1315 +1,1455 @@
1
- "use strict";
2
- const _ = require("lodash");
3
- const fp = require("lodash/fp");
4
- const utils = require("@strapi/utils");
5
- const path = require("path");
6
- const fse = require("fs-extra");
7
- const pluralize = require("pluralize");
8
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
- function _interopNamespace(e) {
10
- if (e && e.__esModule) return e;
11
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
1
+ 'use strict';
2
+
3
+ require('@strapi/types');
4
+ var _ = require('lodash');
5
+ var fp = require('lodash/fp');
6
+ var utils = require('@strapi/utils');
7
+ var path = require('path');
8
+ var fse = require('fs-extra');
9
+ var pluralize = require('pluralize');
10
+
11
+ function _interopNamespaceDefault(e) {
12
+ var n = Object.create(null);
12
13
  if (e) {
13
- for (const k in e) {
14
- if (k !== "default") {
15
- const d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
17
  Object.defineProperty(n, k, d.get ? d : {
17
18
  enumerable: true,
18
- get: () => e[k]
19
+ get: function () { return e[k]; }
19
20
  });
20
21
  }
21
- }
22
+ });
22
23
  }
23
24
  n.default = e;
24
25
  return Object.freeze(n);
25
26
  }
26
- const ___default = /* @__PURE__ */ _interopDefault(_);
27
- const utils__default = /* @__PURE__ */ _interopDefault(utils);
28
- const path__namespace = /* @__PURE__ */ _interopNamespace(path);
29
- const fse__namespace = /* @__PURE__ */ _interopNamespace(fse);
30
- const pluralize__default = /* @__PURE__ */ _interopDefault(pluralize);
31
- const config = {
32
- default: {},
33
- validator() {
34
- }
35
- };
36
- const bootstrap = async ({ strapi: strapi2 }) => {
37
- const actions = [
38
- {
39
- section: "plugins",
40
- displayName: "Read",
41
- uid: "read",
42
- pluginName: "content-type-builder"
43
- }
44
- ];
45
- await strapi2.service("admin::permission").actionProvider.registerMany(actions);
46
- };
47
- const { ApplicationError: ApplicationError$3 } = utils.errors;
48
- const isConfigurable = (attribute) => ___default.default.get(attribute, "configurable", true);
49
- const isRelation = (attribute) => attribute.type === "relation";
50
- const formatAttributes = (model) => {
51
- const { getVisibleAttributes } = utils__default.default.contentTypes;
52
- return getVisibleAttributes(model).reduce((acc, key) => {
53
- acc[key] = formatAttribute(model.attributes[key]);
54
- return acc;
55
- }, {});
56
- };
57
- const formatAttribute = (attribute) => {
58
- const { configurable, required, autoPopulate, pluginOptions } = attribute;
59
- if (attribute.type === "media") {
60
- return {
61
- type: "media",
62
- multiple: !!attribute.multiple,
63
- required: !!required,
64
- configurable: configurable === false ? false : void 0,
65
- private: !!attribute.private,
66
- allowedTypes: attribute.allowedTypes,
67
- pluginOptions
68
- };
69
- }
70
- if (attribute.type === "relation") {
71
- return {
72
- ...attribute,
73
- type: "relation",
74
- target: attribute.target,
75
- targetAttribute: attribute.inversedBy || attribute.mappedBy || null,
76
- configurable: configurable === false ? false : void 0,
77
- private: !!attribute.private,
78
- pluginOptions,
79
- // TODO: remove
80
- autoPopulate
81
- };
82
- }
83
- return attribute;
84
- };
85
- const replaceTemporaryUIDs = (uidMap) => (schema) => {
86
- return {
87
- ...schema,
88
- attributes: Object.keys(schema.attributes).reduce((acc, key) => {
89
- const attr = schema.attributes[key];
90
- if (attr.type === "component") {
91
- if (___default.default.has(uidMap, attr.component)) {
92
- acc[key] = {
93
- ...attr,
94
- component: uidMap[attr.component]
95
- };
96
- return acc;
27
+
28
+ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
29
+ var fse__namespace = /*#__PURE__*/_interopNamespaceDefault(fse);
30
+
31
+ var config = {
32
+ default: {},
33
+ validator () {}
34
+ };
35
+
36
+ var bootstrap = (async ({ strapi })=>{
37
+ const actions = [
38
+ {
39
+ section: 'plugins',
40
+ displayName: 'Read',
41
+ uid: 'read',
42
+ pluginName: 'content-type-builder'
97
43
  }
98
- if (!___default.default.has(strapi.components, attr.component)) {
99
- throw new ApplicationError$3("component.notFound");
100
- }
101
- }
102
- if (attr.type === "dynamiczone" && ___default.default.intersection(attr.components, Object.keys(uidMap)).length > 0) {
103
- acc[key] = {
104
- ...attr,
105
- components: attr.components.map((value) => {
106
- if (___default.default.has(uidMap, value)) return uidMap[value];
107
- if (!___default.default.has(strapi.components, value)) {
108
- throw new ApplicationError$3("component.notFound");
109
- }
110
- return value;
111
- })
112
- };
44
+ ];
45
+ await strapi.service('admin::permission').actionProvider.registerMany(actions);
46
+ });
47
+
48
+ const { ApplicationError: ApplicationError$3 } = utils.errors;
49
+ const isConfigurable = (attribute)=>_.get(attribute, 'configurable', true);
50
+ const isRelation = (attribute)=>attribute.type === 'relation';
51
+ /**
52
+ * Formats a component's attributes
53
+ */ const formatAttributes = (model)=>{
54
+ const { getVisibleAttributes } = utils.contentTypes;
55
+ // only get attributes that can be seen in the CTB
56
+ return getVisibleAttributes(model).reduce((acc, key)=>{
57
+ acc[key] = formatAttribute(model.attributes[key]);
113
58
  return acc;
114
- }
115
- acc[key] = attr;
116
- return acc;
117
- }, {})
118
- };
119
- };
120
- function createSchemaHandler(infos) {
121
- const { category, modelName, plugin, uid, dir, filename, schema } = infos;
122
- const initialState = {
123
- modelName,
124
- plugin,
125
- category,
126
- uid,
127
- dir,
128
- filename,
129
- schema: schema || {
130
- info: {},
131
- options: {},
132
- attributes: {}
59
+ }, {});
60
+ };
61
+ /**
62
+ * Formats a component attribute
63
+ */ const formatAttribute = (attribute)=>{
64
+ const { configurable, required, autoPopulate, pluginOptions } = attribute;
65
+ if (attribute.type === 'media') {
66
+ return {
67
+ type: 'media',
68
+ multiple: !!attribute.multiple,
69
+ required: !!required,
70
+ configurable: configurable === false ? false : undefined,
71
+ private: !!attribute.private,
72
+ allowedTypes: attribute.allowedTypes,
73
+ pluginOptions
74
+ };
133
75
  }
134
- };
135
- const state = ___default.default.cloneDeep(initialState);
136
- Object.freeze(initialState.schema);
137
- let modified = false;
138
- let deleted = false;
139
- return {
140
- get modelName() {
141
- return initialState.modelName;
142
- },
143
- get plugin() {
144
- return initialState.plugin;
145
- },
146
- get category() {
147
- return initialState.category;
148
- },
149
- get kind() {
150
- return ___default.default.get(state.schema, "kind", "collectionType");
151
- },
152
- get uid() {
153
- return state.uid;
154
- },
155
- get writable() {
156
- return ___default.default.get(state, "plugin") !== "admin";
157
- },
158
- setUID(val) {
159
- modified = true;
160
- state.uid = val;
161
- return this;
162
- },
163
- setDir(val) {
164
- modified = true;
165
- state.dir = val;
166
- return this;
167
- },
168
- get schema() {
169
- return ___default.default.cloneDeep(state.schema);
170
- },
171
- setSchema(val) {
172
- modified = true;
173
- state.schema = ___default.default.cloneDeep(val);
174
- return this;
175
- },
176
- // get a particular path inside the schema
177
- get(path2) {
178
- return ___default.default.get(state.schema, path2);
179
- },
180
- // set a particular path inside the schema
181
- set(path2, val) {
182
- if (!state.schema) return this;
183
- modified = true;
184
- const value = ___default.default.defaultTo(val, ___default.default.get(state.schema, path2));
185
- ___default.default.set(state.schema, path2, value);
186
- return this;
187
- },
188
- // delete a particular path inside the schema
189
- unset(path2) {
190
- modified = true;
191
- ___default.default.unset(state.schema, path2);
192
- return this;
193
- },
194
- delete() {
195
- deleted = true;
196
- return this;
197
- },
198
- getAttribute(key) {
199
- return this.get(["attributes", key]);
200
- },
201
- setAttribute(key, attribute) {
202
- return this.set(["attributes", key], attribute);
203
- },
204
- deleteAttribute(key) {
205
- return this.unset(["attributes", key]);
206
- },
207
- setAttributes(newAttributes) {
208
- if (!this.schema) return this;
209
- for (const key in this.schema.attributes) {
210
- if (isConfigurable(this.schema.attributes[key])) {
211
- this.deleteAttribute(key);
212
- }
213
- }
214
- for (const key of Object.keys(newAttributes)) {
215
- this.setAttribute(key, newAttributes[key]);
216
- }
217
- return this;
218
- },
219
- removeContentType(uid2) {
220
- if (!state.schema) return this;
221
- const attributes = state.schema.attributes;
222
- Object.keys(attributes).forEach((key) => {
223
- const attribute = attributes[key];
224
- if (attribute.target === uid2) {
225
- this.deleteAttribute(key);
226
- }
227
- });
228
- return this;
229
- },
230
- // utils
231
- removeComponent(uid2) {
232
- if (!state.schema) return this;
233
- const attributes = state.schema.attributes;
234
- Object.keys(attributes).forEach((key) => {
235
- const attr = attributes[key];
236
- if (attr.type === "component" && attr.component === uid2) {
237
- this.deleteAttribute(key);
238
- }
239
- if (attr.type === "dynamiczone" && Array.isArray(attr.components) && attr.components.includes(uid2)) {
240
- const updatedComponentList = attributes[key].components.filter(
241
- (val) => val !== uid2
242
- );
243
- this.set(["attributes", key, "components"], updatedComponentList);
244
- }
245
- });
246
- return this;
247
- },
248
- updateComponent(uid2, newUID) {
249
- if (!state.schema) return this;
250
- const attributes = state.schema.attributes;
251
- Object.keys(attributes).forEach((key) => {
252
- const attr = attributes[key];
253
- if (attr.type === "component" && attr.component === uid2) {
254
- this.set(["attributes", key, "component"], newUID);
255
- }
256
- if (attr.type === "dynamiczone" && Array.isArray(attr.components) && attr.components.includes(uid2)) {
257
- const updatedComponentList = attr.components.map(
258
- (val) => val === uid2 ? newUID : val
259
- );
260
- this.set(["attributes", key, "components"], updatedComponentList);
261
- }
262
- });
263
- return this;
264
- },
265
- // save the schema to disk
266
- async flush() {
267
- if (!this.writable) {
268
- return;
269
- }
270
- const initialPath = path__namespace.default.join(initialState.dir, initialState.filename);
271
- const filePath = path__namespace.default.join(state.dir, state.filename);
272
- if (deleted) {
273
- await fse__namespace.default.remove(initialPath);
274
- const list = await fse__namespace.default.readdir(initialState.dir);
275
- if (list.length === 0) {
276
- await fse__namespace.default.remove(initialState.dir);
277
- }
278
- return;
279
- }
280
- if (modified) {
281
- if (!state.schema) return Promise.resolve();
282
- await fse__namespace.default.ensureFile(filePath);
283
- await fse__namespace.default.writeJSON(
284
- filePath,
285
- {
286
- kind: state.schema.kind,
287
- collectionName: state.schema.collectionName,
288
- info: state.schema.info,
289
- options: state.schema.options,
290
- pluginOptions: state.schema.pluginOptions,
291
- attributes: state.schema.attributes,
292
- config: state.schema.config
293
- },
294
- { spaces: 2 }
295
- );
296
- if (initialPath !== filePath) {
297
- await fse__namespace.default.remove(initialPath);
298
- const list = await fse__namespace.default.readdir(initialState.dir);
299
- if (list.length === 0) {
300
- await fse__namespace.default.remove(initialState.dir);
301
- }
302
- }
303
- return;
304
- }
305
- return Promise.resolve();
306
- },
307
- // reset the schema to its initial value
308
- async rollback() {
309
- if (!this.writable) {
310
- return;
311
- }
312
- const initialPath = path__namespace.default.join(initialState.dir, initialState.filename);
313
- const filePath = path__namespace.default.join(state.dir, state.filename);
314
- if (!initialState.uid) {
315
- await fse__namespace.default.remove(filePath);
316
- const list = await fse__namespace.default.readdir(state.dir);
317
- if (list.length === 0) {
318
- await fse__namespace.default.remove(state.dir);
76
+ if (attribute.type === 'relation') {
77
+ return {
78
+ ...attribute,
79
+ type: 'relation',
80
+ target: attribute.target,
81
+ targetAttribute: attribute.inversedBy || attribute.mappedBy || null,
82
+ configurable: configurable === false ? false : undefined,
83
+ private: !!attribute.private,
84
+ pluginOptions,
85
+ // TODO: remove
86
+ autoPopulate
87
+ };
88
+ }
89
+ return attribute;
90
+ };
91
+ // TODO: move to schema builder
92
+ const replaceTemporaryUIDs = (uidMap)=>(schema)=>{
93
+ return {
94
+ ...schema,
95
+ attributes: Object.keys(schema.attributes).reduce((acc, key)=>{
96
+ const attr = schema.attributes[key];
97
+ if (attr.type === 'component') {
98
+ if (_.has(uidMap, attr.component)) {
99
+ acc[key] = {
100
+ ...attr,
101
+ component: uidMap[attr.component]
102
+ };
103
+ return acc;
104
+ }
105
+ if (!_.has(strapi.components, attr.component)) {
106
+ throw new ApplicationError$3('component.notFound');
107
+ }
108
+ }
109
+ if (attr.type === 'dynamiczone' && _.intersection(attr.components, Object.keys(uidMap)).length > 0) {
110
+ acc[key] = {
111
+ ...attr,
112
+ components: attr.components.map((value)=>{
113
+ if (_.has(uidMap, value)) return uidMap[value];
114
+ if (!_.has(strapi.components, value)) {
115
+ throw new ApplicationError$3('component.notFound');
116
+ }
117
+ return value;
118
+ })
119
+ };
120
+ return acc;
121
+ }
122
+ acc[key] = attr;
123
+ return acc;
124
+ }, {})
125
+ };
126
+ };
127
+
128
+ function createSchemaHandler(infos) {
129
+ const { category, modelName, plugin, uid, dir, filename, schema } = infos;
130
+ const initialState = {
131
+ modelName,
132
+ plugin,
133
+ category,
134
+ uid,
135
+ dir,
136
+ filename,
137
+ schema: schema || {
138
+ info: {},
139
+ options: {},
140
+ attributes: {}
319
141
  }
320
- return;
321
- }
322
- if (modified || deleted) {
323
- await fse__namespace.default.ensureFile(initialPath);
324
- await fse__namespace.default.writeJSON(initialPath, initialState.schema, { spaces: 2 });
325
- if (initialPath !== filePath) {
326
- await fse__namespace.default.remove(filePath);
327
- const list = await fse__namespace.default.readdir(state.dir);
328
- if (list.length === 0) {
329
- await fse__namespace.default.remove(state.dir);
330
- }
142
+ };
143
+ const state = _.cloneDeep(initialState);
144
+ // always keep it the same to rollback
145
+ Object.freeze(initialState.schema);
146
+ let modified = false;
147
+ let deleted = false;
148
+ return {
149
+ get modelName () {
150
+ return initialState.modelName;
151
+ },
152
+ get plugin () {
153
+ return initialState.plugin;
154
+ },
155
+ get category () {
156
+ return initialState.category;
157
+ },
158
+ get kind () {
159
+ return _.get(state.schema, 'kind', 'collectionType');
160
+ },
161
+ get uid () {
162
+ return state.uid;
163
+ },
164
+ get writable () {
165
+ return _.get(state, 'plugin') !== 'admin';
166
+ },
167
+ setUID (val) {
168
+ modified = true;
169
+ state.uid = val;
170
+ return this;
171
+ },
172
+ setDir (val) {
173
+ modified = true;
174
+ state.dir = val;
175
+ return this;
176
+ },
177
+ get schema () {
178
+ return _.cloneDeep(state.schema);
179
+ },
180
+ setSchema (val) {
181
+ modified = true;
182
+ state.schema = _.cloneDeep(val);
183
+ return this;
184
+ },
185
+ // get a particular path inside the schema
186
+ get (path) {
187
+ return _.get(state.schema, path);
188
+ },
189
+ // set a particular path inside the schema
190
+ set (path, val) {
191
+ if (!state.schema) return this;
192
+ modified = true;
193
+ const value = _.defaultTo(val, _.get(state.schema, path));
194
+ _.set(state.schema, path, value);
195
+ return this;
196
+ },
197
+ // delete a particular path inside the schema
198
+ unset (path) {
199
+ modified = true;
200
+ _.unset(state.schema, path);
201
+ return this;
202
+ },
203
+ delete () {
204
+ deleted = true;
205
+ return this;
206
+ },
207
+ getAttribute (key) {
208
+ return this.get([
209
+ 'attributes',
210
+ key
211
+ ]);
212
+ },
213
+ setAttribute (key, attribute) {
214
+ return this.set([
215
+ 'attributes',
216
+ key
217
+ ], attribute);
218
+ },
219
+ deleteAttribute (key) {
220
+ return this.unset([
221
+ 'attributes',
222
+ key
223
+ ]);
224
+ },
225
+ setAttributes (newAttributes) {
226
+ if (!this.schema) return this;
227
+ // delete old configurable attributes
228
+ for(const key in this.schema.attributes){
229
+ if (isConfigurable(this.schema.attributes[key])) {
230
+ this.deleteAttribute(key);
231
+ }
232
+ }
233
+ // set new Attributes
234
+ for (const key of Object.keys(newAttributes)){
235
+ this.setAttribute(key, newAttributes[key]);
236
+ }
237
+ return this;
238
+ },
239
+ removeContentType (uid) {
240
+ if (!state.schema) return this;
241
+ const attributes = state.schema.attributes;
242
+ Object.keys(attributes).forEach((key)=>{
243
+ const attribute = attributes[key];
244
+ if (attribute.target === uid) {
245
+ this.deleteAttribute(key);
246
+ }
247
+ });
248
+ return this;
249
+ },
250
+ // utils
251
+ removeComponent (uid) {
252
+ if (!state.schema) return this;
253
+ const attributes = state.schema.attributes;
254
+ Object.keys(attributes).forEach((key)=>{
255
+ const attr = attributes[key];
256
+ if (attr.type === 'component' && attr.component === uid) {
257
+ this.deleteAttribute(key);
258
+ }
259
+ if (attr.type === 'dynamiczone' && Array.isArray(attr.components) && attr.components.includes(uid)) {
260
+ const updatedComponentList = attributes[key].components.filter((val)=>val !== uid);
261
+ this.set([
262
+ 'attributes',
263
+ key,
264
+ 'components'
265
+ ], updatedComponentList);
266
+ }
267
+ });
268
+ return this;
269
+ },
270
+ updateComponent (uid, newUID) {
271
+ if (!state.schema) return this;
272
+ const attributes = state.schema.attributes;
273
+ Object.keys(attributes).forEach((key)=>{
274
+ const attr = attributes[key];
275
+ if (attr.type === 'component' && attr.component === uid) {
276
+ this.set([
277
+ 'attributes',
278
+ key,
279
+ 'component'
280
+ ], newUID);
281
+ }
282
+ if (attr.type === 'dynamiczone' && Array.isArray(attr.components) && attr.components.includes(uid)) {
283
+ const updatedComponentList = attr.components.map((val)=>val === uid ? newUID : val);
284
+ this.set([
285
+ 'attributes',
286
+ key,
287
+ 'components'
288
+ ], updatedComponentList);
289
+ }
290
+ });
291
+ return this;
292
+ },
293
+ // save the schema to disk
294
+ async flush () {
295
+ if (!this.writable) {
296
+ return;
297
+ }
298
+ const initialPath = path.join(initialState.dir, initialState.filename);
299
+ const filePath = path.join(state.dir, state.filename);
300
+ if (deleted) {
301
+ await fse.remove(initialPath);
302
+ const list = await fse.readdir(initialState.dir);
303
+ if (list.length === 0) {
304
+ await fse.remove(initialState.dir);
305
+ }
306
+ return;
307
+ }
308
+ if (modified) {
309
+ if (!state.schema) return Promise.resolve();
310
+ await fse.ensureFile(filePath);
311
+ await fse.writeJSON(filePath, {
312
+ kind: state.schema.kind,
313
+ collectionName: state.schema.collectionName,
314
+ info: state.schema.info,
315
+ options: state.schema.options,
316
+ pluginOptions: state.schema.pluginOptions,
317
+ attributes: state.schema.attributes,
318
+ config: state.schema.config
319
+ }, {
320
+ spaces: 2
321
+ });
322
+ // remove from oldPath
323
+ if (initialPath !== filePath) {
324
+ await fse.remove(initialPath);
325
+ const list = await fse.readdir(initialState.dir);
326
+ if (list.length === 0) {
327
+ await fse.remove(initialState.dir);
328
+ }
329
+ }
330
+ return;
331
+ }
332
+ return Promise.resolve();
333
+ },
334
+ // reset the schema to its initial value
335
+ async rollback () {
336
+ if (!this.writable) {
337
+ return;
338
+ }
339
+ const initialPath = path.join(initialState.dir, initialState.filename);
340
+ const filePath = path.join(state.dir, state.filename);
341
+ // it was a creation so it needs to be deleted
342
+ if (!initialState.uid) {
343
+ await fse.remove(filePath);
344
+ const list = await fse.readdir(state.dir);
345
+ if (list.length === 0) {
346
+ await fse.remove(state.dir);
347
+ }
348
+ return;
349
+ }
350
+ if (modified || deleted) {
351
+ await fse.ensureFile(initialPath);
352
+ await fse.writeJSON(initialPath, initialState.schema, {
353
+ spaces: 2
354
+ });
355
+ // remove
356
+ if (initialPath !== filePath) {
357
+ await fse.remove(filePath);
358
+ const list = await fse.readdir(state.dir);
359
+ if (list.length === 0) {
360
+ await fse.remove(state.dir);
361
+ }
362
+ }
363
+ }
364
+ return Promise.resolve();
331
365
  }
332
- }
333
- return Promise.resolve();
334
- }
335
- };
366
+ };
336
367
  }
368
+
337
369
  const { ApplicationError: ApplicationError$2 } = utils.errors;
338
370
  function createComponentBuilder$1() {
339
- return {
340
- createComponentUID({ category, displayName }) {
341
- return `${utils.strings.nameToSlug(category)}.${utils.strings.nameToSlug(displayName)}`;
342
- },
343
- createNewComponentUIDMap(components2) {
344
- return components2.reduce((uidMap, component) => {
345
- uidMap[component.tmpUID] = this.createComponentUID(component);
346
- return uidMap;
347
- }, {});
348
- },
349
- /**
371
+ return {
372
+ createComponentUID ({ category, displayName }) {
373
+ return `${utils.strings.nameToSlug(category)}.${utils.strings.nameToSlug(displayName)}`;
374
+ },
375
+ createNewComponentUIDMap (components) {
376
+ return components.reduce((uidMap, component)=>{
377
+ uidMap[component.tmpUID] = this.createComponentUID(component);
378
+ return uidMap;
379
+ }, {});
380
+ },
381
+ /**
350
382
  * create a component in the tmpComponent map
351
- */
352
- createComponent(infos) {
353
- const uid = this.createComponentUID(infos);
354
- if (this.components.has(uid)) {
355
- throw new ApplicationError$2("component.alreadyExists");
356
- }
357
- const handler = createSchemaHandler({
358
- dir: path__namespace.default.join(strapi.dirs.app.components, utils.strings.nameToSlug(infos.category)),
359
- filename: `${utils.strings.nameToSlug(infos.displayName)}.json`
360
- });
361
- const collectionName = `components_${utils.strings.nameToCollectionName(
362
- infos.category
363
- )}_${utils.strings.nameToCollectionName(pluralize__default.default(infos.displayName))}`;
364
- this.components.forEach((compo) => {
365
- if (compo.schema.collectionName === collectionName) {
366
- throw new ApplicationError$2("component.alreadyExists");
367
- }
368
- });
369
- handler.setUID(uid).set("collectionName", collectionName).set(["info", "displayName"], infos.displayName).set(["info", "icon"], infos.icon).set(["info", "description"], infos.description).set("pluginOptions", infos.pluginOptions).set("config", infos.config).setAttributes(this.convertAttributes(infos.attributes));
370
- if (this.components.size === 0) {
371
- strapi.telemetry.send("didCreateFirstComponent");
372
- } else {
373
- strapi.telemetry.send("didCreateComponent");
374
- }
375
- this.components.set(uid, handler);
376
- return handler;
377
- },
378
- /**
383
+ */ createComponent (infos) {
384
+ const uid = this.createComponentUID(infos);
385
+ if (this.components.has(uid)) {
386
+ throw new ApplicationError$2('component.alreadyExists');
387
+ }
388
+ const handler = createSchemaHandler({
389
+ dir: path.join(strapi.dirs.app.components, utils.strings.nameToSlug(infos.category)),
390
+ filename: `${utils.strings.nameToSlug(infos.displayName)}.json`
391
+ });
392
+ // TODO: create a utility for this
393
+ // Duplicate in admin/src/components/FormModal/forms/utils/createCollectionName.ts
394
+ const collectionName = `components_${utils.strings.nameToCollectionName(infos.category)}_${utils.strings.nameToCollectionName(pluralize(infos.displayName))}`;
395
+ this.components.forEach((compo)=>{
396
+ if (compo.schema.collectionName === collectionName) {
397
+ throw new ApplicationError$2('component.alreadyExists');
398
+ }
399
+ });
400
+ handler.setUID(uid).set('collectionName', collectionName).set([
401
+ 'info',
402
+ 'displayName'
403
+ ], infos.displayName).set([
404
+ 'info',
405
+ 'icon'
406
+ ], infos.icon).set([
407
+ 'info',
408
+ 'description'
409
+ ], infos.description).set('pluginOptions', infos.pluginOptions).set('config', infos.config).setAttributes(this.convertAttributes(infos.attributes));
410
+ if (this.components.size === 0) {
411
+ strapi.telemetry.send('didCreateFirstComponent');
412
+ } else {
413
+ strapi.telemetry.send('didCreateComponent');
414
+ }
415
+ this.components.set(uid, handler);
416
+ return handler;
417
+ },
418
+ /**
379
419
  * create a component in the tmpComponent map
380
- */
381
- editComponent(infos) {
382
- const { uid } = infos;
383
- if (!this.components.has(uid)) {
384
- throw new utils.errors.ApplicationError("component.notFound");
385
- }
386
- const component = this.components.get(uid);
387
- const [, nameUID] = uid.split(".");
388
- const newCategory = utils.strings.nameToSlug(infos.category);
389
- const newUID = `${newCategory}.${nameUID}`;
390
- if (newUID !== uid && this.components.has(newUID)) {
391
- throw new utils.errors.ApplicationError("component.edit.alreadyExists");
392
- }
393
- const newDir = path__namespace.default.join(strapi.dirs.app.components, newCategory);
394
- const oldAttributes = component.schema.attributes;
395
- const newAttributes = ___default.default.omitBy(infos.attributes, (attr, key) => {
396
- return ___default.default.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
397
- });
398
- component.setUID(newUID).setDir(newDir).set(["info", "displayName"], infos.displayName).set(["info", "icon"], infos.icon).set(["info", "description"], infos.description).set("pluginOptions", infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
399
- if (newUID !== uid) {
400
- this.components.forEach((compo) => {
401
- compo.updateComponent(uid, newUID);
402
- });
403
- this.contentTypes.forEach((ct) => {
404
- ct.updateComponent(uid, newUID);
405
- });
406
- }
407
- return component;
408
- },
409
- deleteComponent(uid) {
410
- if (!this.components.has(uid)) {
411
- throw new utils.errors.ApplicationError("component.notFound");
412
- }
413
- this.components.forEach((compo) => {
414
- compo.removeComponent(uid);
415
- });
416
- this.contentTypes.forEach((ct) => {
417
- ct.removeComponent(uid);
418
- });
419
- return this.components.get(uid).delete();
420
- }
421
- };
420
+ */ editComponent (infos) {
421
+ const { uid } = infos;
422
+ if (!this.components.has(uid)) {
423
+ throw new utils.errors.ApplicationError('component.notFound');
424
+ }
425
+ const component = this.components.get(uid);
426
+ const [, nameUID] = uid.split('.');
427
+ const newCategory = utils.strings.nameToSlug(infos.category);
428
+ const newUID = `${newCategory}.${nameUID}`;
429
+ if (newUID !== uid && this.components.has(newUID)) {
430
+ throw new utils.errors.ApplicationError('component.edit.alreadyExists');
431
+ }
432
+ const newDir = path.join(strapi.dirs.app.components, newCategory);
433
+ const oldAttributes = component.schema.attributes;
434
+ const newAttributes = _.omitBy(infos.attributes, (attr, key)=>{
435
+ return _.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
436
+ });
437
+ component.setUID(newUID).setDir(newDir).set([
438
+ 'info',
439
+ 'displayName'
440
+ ], infos.displayName).set([
441
+ 'info',
442
+ 'icon'
443
+ ], infos.icon).set([
444
+ 'info',
445
+ 'description'
446
+ ], infos.description).set('pluginOptions', infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
447
+ if (newUID !== uid) {
448
+ this.components.forEach((compo)=>{
449
+ compo.updateComponent(uid, newUID);
450
+ });
451
+ this.contentTypes.forEach((ct)=>{
452
+ ct.updateComponent(uid, newUID);
453
+ });
454
+ }
455
+ return component;
456
+ },
457
+ deleteComponent (uid) {
458
+ if (!this.components.has(uid)) {
459
+ throw new utils.errors.ApplicationError('component.notFound');
460
+ }
461
+ this.components.forEach((compo)=>{
462
+ compo.removeComponent(uid);
463
+ });
464
+ this.contentTypes.forEach((ct)=>{
465
+ ct.removeComponent(uid);
466
+ });
467
+ return this.components.get(uid).delete();
468
+ }
469
+ };
422
470
  }
471
+
423
472
  const modelTypes = {
424
- CONTENT_TYPE: "CONTENT_TYPE",
425
- COMPONENT: "COMPONENT"
473
+ CONTENT_TYPE: 'CONTENT_TYPE',
474
+ COMPONENT: 'COMPONENT'
426
475
  };
427
476
  const typeKinds = {
428
- SINGLE_TYPE: "singleType",
429
- COLLECTION_TYPE: "collectionType"
477
+ SINGLE_TYPE: 'singleType',
478
+ COLLECTION_TYPE: 'collectionType'
430
479
  };
431
480
  const DEFAULT_TYPES = [
432
- // advanced types
433
- "media",
434
- // scalar types
435
- "string",
436
- "text",
437
- "richtext",
438
- "blocks",
439
- "json",
440
- "enumeration",
441
- "password",
442
- "email",
443
- "integer",
444
- "biginteger",
445
- "float",
446
- "decimal",
447
- "date",
448
- "time",
449
- "datetime",
450
- "timestamp",
451
- "boolean",
452
- "relation"
481
+ // advanced types
482
+ 'media',
483
+ // scalar types
484
+ 'string',
485
+ 'text',
486
+ 'richtext',
487
+ 'blocks',
488
+ 'json',
489
+ 'enumeration',
490
+ 'password',
491
+ 'email',
492
+ 'integer',
493
+ 'biginteger',
494
+ 'float',
495
+ 'decimal',
496
+ 'date',
497
+ 'time',
498
+ 'datetime',
499
+ 'timestamp',
500
+ 'boolean',
501
+ 'relation'
502
+ ];
503
+ const VALID_UID_TARGETS = [
504
+ 'string',
505
+ 'text'
453
506
  ];
454
- const VALID_UID_TARGETS = ["string", "text"];
455
507
  const coreUids = {
456
- STRAPI_USER: "admin::user",
457
- PREFIX: "strapi::"
508
+ STRAPI_USER: 'admin::user',
509
+ PREFIX: 'strapi::'
458
510
  };
459
511
  const pluginsUids = {
460
- UPLOAD_FILE: "plugin::upload.file"
512
+ UPLOAD_FILE: 'plugin::upload.file'
461
513
  };
514
+
462
515
  const { ApplicationError: ApplicationError$1 } = utils.errors;
463
- const reuseUnsetPreviousProperties = (newAttribute, oldAttribute) => {
464
- ___default.default.defaults(
465
- newAttribute,
466
- ___default.default.omit(oldAttribute, [
467
- "configurable",
468
- "required",
469
- "private",
470
- "unique",
471
- "pluginOptions",
472
- "inversedBy",
473
- "mappedBy"
474
- ])
475
- );
516
+ const reuseUnsetPreviousProperties = (newAttribute, oldAttribute)=>{
517
+ _.defaults(newAttribute, _.omit(oldAttribute, [
518
+ 'configurable',
519
+ 'required',
520
+ 'private',
521
+ 'unique',
522
+ 'pluginOptions',
523
+ 'inversedBy',
524
+ 'mappedBy'
525
+ ]));
476
526
  };
477
527
  function createComponentBuilder() {
478
- return {
479
- setRelation({ key, uid, attribute }) {
480
- if (!___default.default.has(attribute, "target")) {
481
- return;
482
- }
483
- const targetCT = this.contentTypes.get(attribute.target);
484
- const targetAttribute = targetCT.getAttribute(attribute.targetAttribute);
485
- if (!attribute.targetAttribute) {
486
- return;
487
- }
488
- targetCT.setAttribute(
489
- attribute.targetAttribute,
490
- generateRelation({ key, attribute, uid, targetAttribute })
491
- );
492
- },
493
- unsetRelation(attribute) {
494
- if (!___default.default.has(attribute, "target")) {
495
- return;
496
- }
497
- const targetCT = this.contentTypes.get(attribute.target);
498
- const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
499
- const targetAttribute = targetCT.getAttribute(targetAttributeName);
500
- if (!targetAttribute) return;
501
- return targetCT.deleteAttribute(targetAttributeName);
502
- },
503
- /**
528
+ return {
529
+ setRelation ({ key, uid, attribute }) {
530
+ if (!_.has(attribute, 'target')) {
531
+ return;
532
+ }
533
+ const targetCT = this.contentTypes.get(attribute.target);
534
+ const targetAttribute = targetCT.getAttribute(attribute.targetAttribute);
535
+ if (!attribute.targetAttribute) {
536
+ return;
537
+ }
538
+ targetCT.setAttribute(attribute.targetAttribute, generateRelation({
539
+ key,
540
+ attribute,
541
+ uid,
542
+ targetAttribute
543
+ }));
544
+ },
545
+ unsetRelation (attribute) {
546
+ if (!_.has(attribute, 'target')) {
547
+ return;
548
+ }
549
+ const targetCT = this.contentTypes.get(attribute.target);
550
+ const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
551
+ const targetAttribute = targetCT.getAttribute(targetAttributeName);
552
+ if (!targetAttribute) return;
553
+ return targetCT.deleteAttribute(targetAttributeName);
554
+ },
555
+ /**
504
556
  * Creates a content type in memory to be written to files later on
505
- */
506
- createContentType(infos) {
507
- const uid = createContentTypeUID(infos);
508
- if (this.contentTypes.has(uid)) {
509
- throw new ApplicationError$1("contentType.alreadyExists");
510
- }
511
- const contentType = createSchemaHandler({
512
- modelName: infos.singularName,
513
- dir: path__namespace.default.join(
514
- strapi.dirs.app.api,
515
- infos.singularName,
516
- "content-types",
517
- infos.singularName
518
- ),
519
- filename: `schema.json`
520
- });
521
- this.contentTypes.set(uid, contentType);
522
- Object.keys(infos.attributes).forEach((key) => {
523
- const { target } = infos.attributes[key];
524
- if (target === "__self__") {
525
- infos.attributes[key].target = uid;
526
- }
527
- });
528
- contentType.setUID(uid).set("kind", infos.kind || typeKinds.COLLECTION_TYPE).set(
529
- "collectionName",
530
- infos.collectionName || utils.strings.nameToCollectionName(infos.pluralName)
531
- ).set("info", {
532
- singularName: infos.singularName,
533
- pluralName: infos.pluralName,
534
- displayName: infos.displayName,
535
- description: infos.description
536
- }).set("options", {
537
- ...infos.options ?? {},
538
- draftAndPublish: infos.draftAndPublish
539
- }).set("pluginOptions", infos.pluginOptions).set("config", infos.config).setAttributes(this.convertAttributes(infos.attributes));
540
- Object.keys(infos.attributes).forEach((key) => {
541
- const attribute = infos.attributes[key];
542
- if (isRelation(attribute)) {
543
- if (["manyToMany", "oneToOne"].includes(attribute.relation)) {
544
- if (attribute.target === uid && attribute.targetAttribute !== void 0) {
545
- const targetAttribute = infos.attributes[attribute.targetAttribute];
546
- if (targetAttribute.dominant === void 0) {
547
- attribute.dominant = true;
548
- } else {
549
- attribute.dominant = false;
550
- }
551
- } else {
552
- attribute.dominant = true;
553
- }
554
- }
555
- this.setRelation({
556
- key,
557
- uid,
558
- attribute
559
- });
560
- }
561
- });
562
- return contentType;
563
- },
564
- editContentType(infos) {
565
- const { uid } = infos;
566
- if (!this.contentTypes.has(uid)) {
567
- throw new ApplicationError$1("contentType.notFound");
568
- }
569
- const contentType = this.contentTypes.get(uid);
570
- const oldAttributes = contentType.schema.attributes;
571
- const newAttributes = ___default.default.omitBy(infos.attributes, (attr, key) => {
572
- return ___default.default.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
573
- });
574
- const newKeys = ___default.default.difference(Object.keys(newAttributes), Object.keys(oldAttributes));
575
- const deletedKeys = ___default.default.difference(Object.keys(oldAttributes), Object.keys(newAttributes));
576
- const remainingKeys = ___default.default.intersection(Object.keys(oldAttributes), Object.keys(newAttributes));
577
- deletedKeys.forEach((key) => {
578
- const attribute = oldAttributes[key];
579
- const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
580
- if (isConfigurable(attribute) && isRelation(attribute) && !___default.default.isNil(targetAttributeName)) {
581
- this.unsetRelation(attribute);
582
- }
583
- });
584
- remainingKeys.forEach((key) => {
585
- const oldAttribute = oldAttributes[key];
586
- const newAttribute = newAttributes[key];
587
- if (!isRelation(oldAttribute) && isRelation(newAttribute)) {
588
- return this.setRelation({
589
- key,
590
- uid,
591
- attribute: newAttributes[key]
592
- });
593
- }
594
- if (isRelation(oldAttribute) && !isRelation(newAttribute)) {
595
- return this.unsetRelation(oldAttribute);
596
- }
597
- if (isRelation(oldAttribute) && isRelation(newAttribute)) {
598
- const oldTargetAttributeName = oldAttribute.inversedBy || oldAttribute.mappedBy;
599
- const sameRelation = oldAttribute.relation === newAttribute.relation;
600
- const targetAttributeHasChanged = oldTargetAttributeName !== newAttribute.targetAttribute;
601
- if (!sameRelation || targetAttributeHasChanged) {
602
- this.unsetRelation(oldAttribute);
603
- }
604
- reuseUnsetPreviousProperties(newAttribute, oldAttribute);
605
- if (oldAttribute.inversedBy) {
606
- newAttribute.dominant = true;
607
- } else if (oldAttribute.mappedBy) {
608
- newAttribute.dominant = false;
609
- }
610
- return this.setRelation({
611
- key,
612
- uid,
613
- attribute: newAttribute
614
- });
615
- }
616
- });
617
- newKeys.forEach((key) => {
618
- const attribute = newAttributes[key];
619
- if (isRelation(attribute)) {
620
- if (["manyToMany", "oneToOne"].includes(attribute.relation)) {
621
- if (attribute.target === uid && attribute.targetAttribute !== void 0) {
622
- const targetAttribute = newAttributes[attribute.targetAttribute];
623
- if (targetAttribute.dominant === void 0) {
624
- attribute.dominant = true;
625
- } else {
626
- attribute.dominant = false;
627
- }
628
- } else {
629
- attribute.dominant = true;
630
- }
631
- }
632
- this.setRelation({
633
- key,
634
- uid,
635
- attribute
636
- });
557
+ */ createContentType (infos) {
558
+ const uid = createContentTypeUID(infos);
559
+ if (this.contentTypes.has(uid)) {
560
+ throw new ApplicationError$1('contentType.alreadyExists');
561
+ }
562
+ const contentType = createSchemaHandler({
563
+ modelName: infos.singularName,
564
+ dir: path.join(strapi.dirs.app.api, infos.singularName, 'content-types', infos.singularName),
565
+ filename: `schema.json`
566
+ });
567
+ this.contentTypes.set(uid, contentType);
568
+ // support self referencing content type relation
569
+ Object.keys(infos.attributes).forEach((key)=>{
570
+ const { target } = infos.attributes[key];
571
+ if (target === '__self__') {
572
+ infos.attributes[key].target = uid;
573
+ }
574
+ });
575
+ contentType.setUID(uid).set('kind', infos.kind || typeKinds.COLLECTION_TYPE).set('collectionName', infos.collectionName || utils.strings.nameToCollectionName(infos.pluralName)).set('info', {
576
+ singularName: infos.singularName,
577
+ pluralName: infos.pluralName,
578
+ displayName: infos.displayName,
579
+ description: infos.description
580
+ }).set('options', {
581
+ ...infos.options ?? {},
582
+ draftAndPublish: infos.draftAndPublish
583
+ }).set('pluginOptions', infos.pluginOptions).set('config', infos.config).setAttributes(this.convertAttributes(infos.attributes));
584
+ Object.keys(infos.attributes).forEach((key)=>{
585
+ const attribute = infos.attributes[key];
586
+ if (isRelation(attribute)) {
587
+ if ([
588
+ 'manyToMany',
589
+ 'oneToOne'
590
+ ].includes(attribute.relation)) {
591
+ if (attribute.target === uid && attribute.targetAttribute !== undefined) {
592
+ // self referencing relation
593
+ const targetAttribute = infos.attributes[attribute.targetAttribute];
594
+ if (targetAttribute.dominant === undefined) {
595
+ attribute.dominant = true;
596
+ } else {
597
+ attribute.dominant = false;
598
+ }
599
+ } else {
600
+ attribute.dominant = true;
601
+ }
602
+ }
603
+ this.setRelation({
604
+ key,
605
+ uid,
606
+ attribute
607
+ });
608
+ }
609
+ });
610
+ return contentType;
611
+ },
612
+ editContentType (infos) {
613
+ const { uid } = infos;
614
+ if (!this.contentTypes.has(uid)) {
615
+ throw new ApplicationError$1('contentType.notFound');
616
+ }
617
+ const contentType = this.contentTypes.get(uid);
618
+ const oldAttributes = contentType.schema.attributes;
619
+ const newAttributes = _.omitBy(infos.attributes, (attr, key)=>{
620
+ return _.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
621
+ });
622
+ const newKeys = _.difference(Object.keys(newAttributes), Object.keys(oldAttributes));
623
+ const deletedKeys = _.difference(Object.keys(oldAttributes), Object.keys(newAttributes));
624
+ const remainingKeys = _.intersection(Object.keys(oldAttributes), Object.keys(newAttributes));
625
+ // remove old relations
626
+ deletedKeys.forEach((key)=>{
627
+ const attribute = oldAttributes[key];
628
+ const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
629
+ // if the old relation has a target attribute. we need to remove it in the target type
630
+ if (isConfigurable(attribute) && isRelation(attribute) && !_.isNil(targetAttributeName)) {
631
+ this.unsetRelation(attribute);
632
+ }
633
+ });
634
+ remainingKeys.forEach((key)=>{
635
+ const oldAttribute = oldAttributes[key];
636
+ const newAttribute = newAttributes[key];
637
+ if (!isRelation(oldAttribute) && isRelation(newAttribute)) {
638
+ return this.setRelation({
639
+ key,
640
+ uid,
641
+ attribute: newAttributes[key]
642
+ });
643
+ }
644
+ if (isRelation(oldAttribute) && !isRelation(newAttribute)) {
645
+ return this.unsetRelation(oldAttribute);
646
+ }
647
+ if (isRelation(oldAttribute) && isRelation(newAttribute)) {
648
+ const oldTargetAttributeName = oldAttribute.inversedBy || oldAttribute.mappedBy;
649
+ const sameRelation = oldAttribute.relation === newAttribute.relation;
650
+ const targetAttributeHasChanged = oldTargetAttributeName !== newAttribute.targetAttribute;
651
+ if (!sameRelation || targetAttributeHasChanged) {
652
+ this.unsetRelation(oldAttribute);
653
+ }
654
+ // keep extra options that were set manually on oldAttribute
655
+ reuseUnsetPreviousProperties(newAttribute, oldAttribute);
656
+ if (oldAttribute.inversedBy) {
657
+ newAttribute.dominant = true;
658
+ } else if (oldAttribute.mappedBy) {
659
+ newAttribute.dominant = false;
660
+ }
661
+ return this.setRelation({
662
+ key,
663
+ uid,
664
+ attribute: newAttribute
665
+ });
666
+ }
667
+ });
668
+ // add new relations
669
+ newKeys.forEach((key)=>{
670
+ const attribute = newAttributes[key];
671
+ if (isRelation(attribute)) {
672
+ if ([
673
+ 'manyToMany',
674
+ 'oneToOne'
675
+ ].includes(attribute.relation)) {
676
+ if (attribute.target === uid && attribute.targetAttribute !== undefined) {
677
+ // self referencing relation
678
+ const targetAttribute = newAttributes[attribute.targetAttribute];
679
+ if (targetAttribute.dominant === undefined) {
680
+ attribute.dominant = true;
681
+ } else {
682
+ attribute.dominant = false;
683
+ }
684
+ } else {
685
+ attribute.dominant = true;
686
+ }
687
+ }
688
+ this.setRelation({
689
+ key,
690
+ uid,
691
+ attribute
692
+ });
693
+ }
694
+ });
695
+ contentType.set('kind', infos.kind || contentType.schema.kind).set([
696
+ 'info',
697
+ 'displayName'
698
+ ], infos.displayName).set([
699
+ 'info',
700
+ 'description'
701
+ ], infos.description).set('options', {
702
+ ...infos.options ?? {},
703
+ draftAndPublish: infos.draftAndPublish
704
+ }).set('pluginOptions', infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
705
+ return contentType;
706
+ },
707
+ deleteContentType (uid) {
708
+ if (!this.contentTypes.has(uid)) {
709
+ throw new ApplicationError$1('contentType.notFound');
710
+ }
711
+ this.components.forEach((compo)=>{
712
+ compo.removeContentType(uid);
713
+ });
714
+ this.contentTypes.forEach((ct)=>{
715
+ ct.removeContentType(uid);
716
+ });
717
+ return this.contentTypes.get(uid).delete();
637
718
  }
638
- });
639
- contentType.set("kind", infos.kind || contentType.schema.kind).set(["info", "displayName"], infos.displayName).set(["info", "description"], infos.description).set("options", {
640
- ...infos.options ?? {},
641
- draftAndPublish: infos.draftAndPublish
642
- }).set("pluginOptions", infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
643
- return contentType;
644
- },
645
- deleteContentType(uid) {
646
- if (!this.contentTypes.has(uid)) {
647
- throw new ApplicationError$1("contentType.notFound");
648
- }
649
- this.components.forEach((compo) => {
650
- compo.removeContentType(uid);
651
- });
652
- this.contentTypes.forEach((ct) => {
653
- ct.removeContentType(uid);
654
- });
655
- return this.contentTypes.get(uid).delete();
656
- }
657
- };
719
+ };
658
720
  }
659
- const createContentTypeUID = ({
660
- singularName
661
- }) => `api::${singularName}.${singularName}`;
662
- const generateRelation = ({ key, attribute, uid, targetAttribute = {} }) => {
663
- const opts = {
664
- type: "relation",
665
- target: uid,
666
- autoPopulate: targetAttribute.autoPopulate,
667
- private: targetAttribute.private || void 0,
668
- pluginOptions: targetAttribute.pluginOptions || void 0
669
- };
670
- switch (attribute.relation) {
671
- case "oneToOne": {
672
- opts.relation = "oneToOne";
673
- if (attribute.dominant) {
674
- opts.mappedBy = key;
675
- } else {
676
- opts.inversedBy = key;
677
- }
678
- break;
679
- }
680
- case "oneToMany": {
681
- opts.relation = "manyToOne";
682
- opts.inversedBy = key;
683
- break;
684
- }
685
- case "manyToOne": {
686
- opts.relation = "oneToMany";
687
- opts.mappedBy = key;
688
- break;
689
- }
690
- case "manyToMany": {
691
- opts.relation = "manyToMany";
692
- if (attribute.dominant) {
693
- opts.mappedBy = key;
694
- } else {
695
- opts.inversedBy = key;
696
- }
697
- break;
721
+ /**
722
+ * Returns a uid from a content type infos
723
+ *
724
+ * @param {object} options options
725
+ * @param {string} options.singularName content-type singularName
726
+ * @returns {string} uid
727
+ */ const createContentTypeUID = ({ singularName })=>`api::${singularName}.${singularName}`;
728
+ const generateRelation = ({ key, attribute, uid, targetAttribute = {} })=>{
729
+ const opts = {
730
+ type: 'relation',
731
+ target: uid,
732
+ autoPopulate: targetAttribute.autoPopulate,
733
+ private: targetAttribute.private || undefined,
734
+ pluginOptions: targetAttribute.pluginOptions || undefined
735
+ };
736
+ switch(attribute.relation){
737
+ case 'oneToOne':
738
+ {
739
+ opts.relation = 'oneToOne';
740
+ if (attribute.dominant) {
741
+ opts.mappedBy = key;
742
+ } else {
743
+ opts.inversedBy = key;
744
+ }
745
+ break;
746
+ }
747
+ case 'oneToMany':
748
+ {
749
+ opts.relation = 'manyToOne';
750
+ opts.inversedBy = key;
751
+ break;
752
+ }
753
+ case 'manyToOne':
754
+ {
755
+ opts.relation = 'oneToMany';
756
+ opts.mappedBy = key;
757
+ break;
758
+ }
759
+ case 'manyToMany':
760
+ {
761
+ opts.relation = 'manyToMany';
762
+ if (attribute.dominant) {
763
+ opts.mappedBy = key;
764
+ } else {
765
+ opts.inversedBy = key;
766
+ }
767
+ break;
768
+ }
698
769
  }
699
- }
700
- const { type, relation, target, ...restOptions } = opts;
701
- return {
702
- type,
703
- relation,
704
- target,
705
- ...restOptions
706
- };
707
- };
708
- function createBuilder() {
709
- const components2 = Object.values(strapi.components).map((componentInput) => ({
710
- category: componentInput.category,
711
- modelName: componentInput.modelName,
712
- plugin: componentInput.modelName,
713
- uid: componentInput.uid,
714
- filename: componentInput.__filename__,
715
- dir: path.join(strapi.dirs.app.components, componentInput.category),
716
- schema: componentInput.__schema__,
717
- config: componentInput.config
718
- }));
719
- const contentTypes2 = Object.values(strapi.contentTypes).map((contentTypeInput) => {
720
- const dir = contentTypeInput.plugin ? path.join(
721
- strapi.dirs.app.extensions,
722
- contentTypeInput.plugin,
723
- "content-types",
724
- contentTypeInput.info.singularName
725
- ) : path.join(
726
- strapi.dirs.app.api,
727
- contentTypeInput.apiName,
728
- "content-types",
729
- contentTypeInput.info.singularName
730
- );
770
+ // we do this just to make sure we have the same key order when writing to files
771
+ const { type, relation, target, ...restOptions } = opts;
731
772
  return {
732
- modelName: contentTypeInput.modelName,
733
- plugin: contentTypeInput.plugin,
734
- uid: contentTypeInput.uid,
735
- filename: "schema.json",
736
- dir,
737
- schema: contentTypeInput.__schema__,
738
- config: contentTypeInput.config
773
+ type,
774
+ relation,
775
+ target,
776
+ ...restOptions
739
777
  };
740
- });
741
- return createSchemaBuilder({
742
- components: components2,
743
- contentTypes: contentTypes2
744
- });
778
+ };
779
+
780
+ /**
781
+ * Creates a content type schema builder instance
782
+ */ function createBuilder() {
783
+ const components = Object.values(strapi.components).map((componentInput)=>({
784
+ category: componentInput.category,
785
+ modelName: componentInput.modelName,
786
+ plugin: componentInput.modelName,
787
+ uid: componentInput.uid,
788
+ filename: componentInput.__filename__,
789
+ dir: path.join(strapi.dirs.app.components, componentInput.category),
790
+ schema: componentInput.__schema__,
791
+ config: componentInput.config
792
+ }));
793
+ const contentTypes = Object.values(strapi.contentTypes).map((contentTypeInput)=>{
794
+ const dir = contentTypeInput.plugin ? path.join(strapi.dirs.app.extensions, contentTypeInput.plugin, 'content-types', contentTypeInput.info.singularName) : path.join(strapi.dirs.app.api, contentTypeInput.apiName, 'content-types', contentTypeInput.info.singularName);
795
+ return {
796
+ modelName: contentTypeInput.modelName,
797
+ plugin: contentTypeInput.plugin,
798
+ uid: contentTypeInput.uid,
799
+ filename: 'schema.json',
800
+ dir,
801
+ schema: contentTypeInput.__schema__,
802
+ config: contentTypeInput.config
803
+ };
804
+ });
805
+ return createSchemaBuilder({
806
+ components,
807
+ contentTypes
808
+ });
745
809
  }
746
- function createSchemaBuilder({ components: components2, contentTypes: contentTypes2 }) {
747
- const tmpComponents = /* @__PURE__ */ new Map();
748
- const tmpContentTypes = /* @__PURE__ */ new Map();
749
- Object.keys(contentTypes2).forEach((key) => {
750
- tmpContentTypes.set(contentTypes2[key].uid, createSchemaHandler(contentTypes2[key]));
751
- });
752
- Object.keys(components2).forEach((key) => {
753
- tmpComponents.set(components2[key].uid, createSchemaHandler(components2[key]));
754
- });
755
- return {
756
- get components() {
757
- return tmpComponents;
758
- },
759
- get contentTypes() {
760
- return tmpContentTypes;
761
- },
762
- /**
810
+ function createSchemaBuilder({ components, contentTypes }) {
811
+ const tmpComponents = new Map();
812
+ const tmpContentTypes = new Map();
813
+ // init temporary ContentTypes
814
+ Object.keys(contentTypes).forEach((key)=>{
815
+ tmpContentTypes.set(contentTypes[key].uid, createSchemaHandler(contentTypes[key]));
816
+ });
817
+ // init temporary components
818
+ Object.keys(components).forEach((key)=>{
819
+ tmpComponents.set(components[key].uid, createSchemaHandler(components[key]));
820
+ });
821
+ return {
822
+ get components () {
823
+ return tmpComponents;
824
+ },
825
+ get contentTypes () {
826
+ return tmpContentTypes;
827
+ },
828
+ /**
763
829
  * Convert Attributes received from the API to the right syntax
764
- */
765
- convertAttributes(attributes) {
766
- return Object.keys(attributes).reduce(
767
- (acc, key) => {
768
- const attribute = attributes[key];
769
- const { configurable, private: isPrivate } = attribute;
770
- const baseProperties = {
771
- private: isPrivate === true ? true : void 0,
772
- configurable: configurable === false ? false : void 0
773
- };
774
- if (attribute.type === "relation") {
775
- const { target, relation, targetAttribute, dominant, ...restOfProperties } = attribute;
776
- const attr = {
777
- type: "relation",
778
- relation,
779
- target,
780
- ...restOfProperties,
781
- ...baseProperties
782
- };
783
- acc[key] = attr;
784
- if (target && !this.contentTypes.has(target)) {
785
- throw new utils.errors.ApplicationError(`target: ${target} does not exist`);
786
- }
787
- if (___default.default.isNil(targetAttribute)) {
788
- return acc;
789
- }
790
- if (["oneToOne", "manyToMany"].includes(relation) && dominant === true) {
791
- attr.inversedBy = targetAttribute;
792
- } else if (["oneToOne", "manyToMany"].includes(relation) && dominant === false) {
793
- attr.mappedBy = targetAttribute;
794
- } else if (["oneToOne", "manyToOne", "manyToMany"].includes(relation)) {
795
- attr.inversedBy = targetAttribute;
796
- } else if (["oneToMany"].includes(relation)) {
797
- attr.mappedBy = targetAttribute;
798
- }
799
- return acc;
800
- }
801
- acc[key] = {
802
- ...attribute,
803
- ...baseProperties
804
- };
805
- return acc;
830
+ */ convertAttributes (attributes) {
831
+ return Object.keys(attributes).reduce((acc, key)=>{
832
+ const attribute = attributes[key];
833
+ const { configurable, private: isPrivate } = attribute;
834
+ const baseProperties = {
835
+ private: isPrivate === true ? true : undefined,
836
+ configurable: configurable === false ? false : undefined
837
+ };
838
+ if (attribute.type === 'relation') {
839
+ const { target, relation, targetAttribute, dominant, ...restOfProperties } = attribute;
840
+ const attr = {
841
+ type: 'relation',
842
+ relation,
843
+ target,
844
+ ...restOfProperties,
845
+ ...baseProperties
846
+ };
847
+ acc[key] = attr;
848
+ if (target && !this.contentTypes.has(target)) {
849
+ throw new utils.errors.ApplicationError(`target: ${target} does not exist`);
850
+ }
851
+ if (_.isNil(targetAttribute)) {
852
+ return acc;
853
+ }
854
+ if ([
855
+ 'oneToOne',
856
+ 'manyToMany'
857
+ ].includes(relation) && dominant === true) {
858
+ attr.inversedBy = targetAttribute;
859
+ } else if ([
860
+ 'oneToOne',
861
+ 'manyToMany'
862
+ ].includes(relation) && dominant === false) {
863
+ attr.mappedBy = targetAttribute;
864
+ } else if ([
865
+ 'oneToOne',
866
+ 'manyToOne',
867
+ 'manyToMany'
868
+ ].includes(relation)) {
869
+ attr.inversedBy = targetAttribute;
870
+ } else if ([
871
+ 'oneToMany'
872
+ ].includes(relation)) {
873
+ attr.mappedBy = targetAttribute;
874
+ }
875
+ return acc;
876
+ }
877
+ acc[key] = {
878
+ ...attribute,
879
+ ...baseProperties
880
+ };
881
+ return acc;
882
+ }, {});
806
883
  },
807
- {}
808
- );
809
- },
810
- ...createComponentBuilder$1(),
811
- ...createComponentBuilder(),
812
- /**
884
+ ...createComponentBuilder$1(),
885
+ ...createComponentBuilder(),
886
+ /**
813
887
  * Write all type to files
814
- */
815
- writeFiles() {
816
- const schemas = [
817
- ...Array.from(tmpComponents.values()),
818
- ...Array.from(tmpContentTypes.values())
819
- ];
820
- return Promise.all(schemas.map((schema) => schema.flush())).catch((error) => {
821
- strapi.log.error("Error writing schema files");
822
- strapi.log.error(error);
823
- return this.rollback();
824
- }).catch((error) => {
825
- strapi.log.error(
826
- "Error rolling back schema files. You might need to fix your files manually"
827
- );
828
- strapi.log.error(error);
829
- throw new utils.errors.ApplicationError("Invalid schema edition");
830
- });
831
- },
832
- /**
888
+ */ writeFiles () {
889
+ const schemas = [
890
+ ...Array.from(tmpComponents.values()),
891
+ ...Array.from(tmpContentTypes.values())
892
+ ];
893
+ return Promise.all(schemas.map((schema)=>schema.flush())).catch((error)=>{
894
+ strapi.log.error('Error writing schema files');
895
+ strapi.log.error(error);
896
+ return this.rollback();
897
+ }).catch((error)=>{
898
+ strapi.log.error('Error rolling back schema files. You might need to fix your files manually');
899
+ strapi.log.error(error);
900
+ throw new utils.errors.ApplicationError('Invalid schema edition');
901
+ });
902
+ },
903
+ /**
833
904
  * rollback all files
834
- */
835
- rollback() {
836
- return Promise.all(
837
- [...Array.from(tmpComponents.values()), ...Array.from(tmpContentTypes.values())].map(
838
- (schema) => schema.rollback()
839
- )
840
- );
841
- }
842
- };
905
+ */ rollback () {
906
+ return Promise.all([
907
+ ...Array.from(tmpComponents.values()),
908
+ ...Array.from(tmpContentTypes.values())
909
+ ].map((schema)=>schema.rollback()));
910
+ }
911
+ };
843
912
  }
913
+
844
914
  const { ApplicationError } = utils.errors;
845
- const isContentTypeVisible = (model) => fp.getOr(true, "pluginOptions.content-type-builder.visible", model) === true;
846
- const getRestrictRelationsTo = (contentType) => {
847
- const { uid } = contentType;
848
- if (uid === coreUids.STRAPI_USER) {
849
- return ["oneWay", "manyWay"];
850
- }
851
- if (uid.startsWith(coreUids.PREFIX) || uid === pluginsUids.UPLOAD_FILE || !isContentTypeVisible(contentType)) {
852
- return [];
853
- }
854
- return null;
855
- };
856
- const formatContentType = (contentType) => {
857
- const { uid, kind, modelName, plugin, collectionName, info } = contentType;
858
- return {
859
- uid,
860
- plugin,
861
- apiID: modelName,
862
- schema: {
863
- ...utils.contentTypes.getOptions(contentType),
864
- displayName: info.displayName,
865
- singularName: info.singularName,
866
- pluralName: info.pluralName,
867
- description: ___default.default.get(info, "description", ""),
868
- pluginOptions: contentType.pluginOptions,
869
- kind: kind || "collectionType",
870
- collectionName,
871
- attributes: formatAttributes(contentType),
872
- visible: isContentTypeVisible(contentType),
873
- restrictRelationsTo: getRestrictRelationsTo(contentType)
874
- }
875
- };
915
+ const isContentTypeVisible = (model)=>fp.getOr(true, 'pluginOptions.content-type-builder.visible', model) === true;
916
+ const getRestrictRelationsTo = (contentType)=>{
917
+ const { uid } = contentType;
918
+ if (uid === coreUids.STRAPI_USER) {
919
+ // TODO: replace with an obj { relation: 'x', bidirectional: true|false }
920
+ return [
921
+ 'oneWay',
922
+ 'manyWay'
923
+ ];
924
+ }
925
+ if (uid.startsWith(coreUids.PREFIX) || uid === pluginsUids.UPLOAD_FILE || !isContentTypeVisible(contentType)) {
926
+ return [];
927
+ }
928
+ return null;
929
+ };
930
+ /**
931
+ * Format a contentType info to be used by the front-end
932
+ */ const formatContentType = (contentType)=>{
933
+ const { uid, kind, modelName, plugin, collectionName, info } = contentType;
934
+ return {
935
+ uid,
936
+ plugin,
937
+ apiID: modelName,
938
+ schema: {
939
+ ...utils.contentTypes.getOptions(contentType),
940
+ displayName: info.displayName,
941
+ singularName: info.singularName,
942
+ pluralName: info.pluralName,
943
+ description: _.get(info, 'description', ''),
944
+ pluginOptions: contentType.pluginOptions,
945
+ kind: kind || 'collectionType',
946
+ collectionName,
947
+ attributes: formatAttributes(contentType),
948
+ visible: isContentTypeVisible(contentType),
949
+ restrictRelationsTo: getRestrictRelationsTo(contentType)
950
+ }
951
+ };
876
952
  };
877
- const createContentTypes = async (contentTypes2) => {
878
- const builder2 = createBuilder();
879
- const createdContentTypes = [];
880
- for (const contentType of contentTypes2) {
881
- createdContentTypes.push(await createContentType(contentType, { defaultBuilder: builder2 }));
882
- }
883
- await builder2.writeFiles();
884
- return createdContentTypes;
885
- };
886
- const createContentType = async ({ contentType, components: components2 }, options = {}) => {
887
- const builder2 = options.defaultBuilder || createBuilder();
888
- const uidMap = builder2.createNewComponentUIDMap(components2 || []);
889
- const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
890
- const newContentType = builder2.createContentType(replaceTmpUIDs(contentType));
891
- const targetContentType = (infos) => {
892
- Object.keys(infos.attributes).forEach((key) => {
893
- const { target } = infos.attributes[key];
894
- if (target === "__contentType__") {
895
- infos.attributes[key].target = newContentType.uid;
896
- }
953
+ const createContentTypes = async (contentTypes)=>{
954
+ const builder = createBuilder();
955
+ const createdContentTypes = [];
956
+ for (const contentType of contentTypes){
957
+ createdContentTypes.push(await createContentType(contentType, {
958
+ defaultBuilder: builder
959
+ }));
960
+ }
961
+ await builder.writeFiles();
962
+ return createdContentTypes;
963
+ };
964
+ /**
965
+ * Creates a content type and handle the nested components sent with it
966
+ */ const createContentType = async ({ contentType, components }, options = {})=>{
967
+ const builder = options.defaultBuilder || createBuilder();
968
+ const uidMap = builder.createNewComponentUIDMap(components || []);
969
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
970
+ const newContentType = builder.createContentType(replaceTmpUIDs(contentType));
971
+ // allow components to target the new contentType
972
+ const targetContentType = (infos)=>{
973
+ Object.keys(infos.attributes).forEach((key)=>{
974
+ const { target } = infos.attributes[key];
975
+ if (target === '__contentType__') {
976
+ infos.attributes[key].target = newContentType.uid;
977
+ }
978
+ });
979
+ return infos;
980
+ };
981
+ components?.forEach((component)=>{
982
+ const options = replaceTmpUIDs(targetContentType(component));
983
+ if (!_.has(component, 'uid')) {
984
+ return builder.createComponent(options);
985
+ }
986
+ return builder.editComponent(options);
897
987
  });
898
- return infos;
899
- };
900
- components2?.forEach((component) => {
901
- const options2 = replaceTmpUIDs(targetContentType(component));
902
- if (!___default.default.has(component, "uid")) {
903
- return builder2.createComponent(options2);
904
- }
905
- return builder2.editComponent(options2);
906
- });
907
- await generateAPI({
908
- displayName: contentType.displayName || contentType.info.displayName,
909
- singularName: contentType.singularName,
910
- pluralName: contentType.pluralName,
911
- kind: contentType.kind
912
- });
913
- if (!options.defaultBuilder) {
914
- await builder2.writeFiles();
915
- }
916
- strapi.eventHub.emit("content-type.create", { contentType: newContentType });
917
- return newContentType;
918
- };
919
- const generateAPI = ({
920
- singularName,
921
- kind = "collectionType",
922
- pluralName,
923
- displayName
924
- }) => {
925
- const strapiGenerators = require("@strapi/generators");
926
- return strapiGenerators.generate(
927
- "content-type",
928
- {
929
- kind,
930
- singularName,
931
- id: singularName,
932
- pluralName,
933
- displayName,
934
- destination: "new",
935
- bootstrapApi: true,
936
- attributes: []
937
- },
938
- { dir: strapi.dirs.app.root }
939
- );
940
- };
941
- const editContentType = async (uid, { contentType, components: components2 = [] }) => {
942
- const builder2 = createBuilder();
943
- const previousSchema = builder2.contentTypes.get(uid).schema;
944
- const previousKind = previousSchema.kind;
945
- const newKind = contentType.kind || previousKind;
946
- const previousAttributes = previousSchema.attributes;
947
- const prevNonVisibleAttributes = utils.contentTypes.getNonVisibleAttributes(previousSchema).reduce((acc, key) => {
948
- if (key in previousAttributes) {
949
- acc[key] = previousAttributes[key];
950
- }
951
- return acc;
952
- }, {});
953
- contentType.attributes = ___default.default.merge(prevNonVisibleAttributes, contentType.attributes);
954
- if (newKind !== previousKind && newKind === "singleType") {
955
- const entryCount = await strapi.db.query(uid).count();
956
- if (entryCount > 1) {
957
- throw new ApplicationError(
958
- "You cannot convert a collectionType to a singleType when having multiple entries in DB"
959
- );
988
+ // generate api skeleton
989
+ await generateAPI({
990
+ displayName: contentType.displayName || contentType.info.displayName,
991
+ singularName: contentType.singularName,
992
+ pluralName: contentType.pluralName,
993
+ kind: contentType.kind
994
+ });
995
+ if (!options.defaultBuilder) {
996
+ await builder.writeFiles();
960
997
  }
961
- }
962
- const uidMap = builder2.createNewComponentUIDMap(components2);
963
- const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
964
- const updatedContentType = builder2.editContentType({
965
- uid,
966
- ...replaceTmpUIDs(contentType)
967
- });
968
- components2.forEach((component) => {
969
- if (!___default.default.has(component, "uid")) {
970
- return builder2.createComponent(replaceTmpUIDs(component));
998
+ strapi.eventHub.emit('content-type.create', {
999
+ contentType: newContentType
1000
+ });
1001
+ return newContentType;
1002
+ };
1003
+ /**
1004
+ * Generate an API skeleton
1005
+ */ const generateAPI = ({ singularName, kind = 'collectionType', pluralName, displayName })=>{
1006
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1007
+ const strapiGenerators = require('@strapi/generators');
1008
+ return strapiGenerators.generate('content-type', {
1009
+ kind,
1010
+ singularName,
1011
+ id: singularName,
1012
+ pluralName,
1013
+ displayName,
1014
+ destination: 'new',
1015
+ bootstrapApi: true,
1016
+ attributes: []
1017
+ }, {
1018
+ dir: strapi.dirs.app.root
1019
+ });
1020
+ };
1021
+ /**
1022
+ * Edits a contentType and handle the nested contentTypes sent with it
1023
+ */ const editContentType = async (uid, { contentType, components = [] })=>{
1024
+ const builder = createBuilder();
1025
+ const previousSchema = builder.contentTypes.get(uid).schema;
1026
+ const previousKind = previousSchema.kind;
1027
+ const newKind = contentType.kind || previousKind;
1028
+ // Restore non-visible attributes from previous schema
1029
+ const previousAttributes = previousSchema.attributes;
1030
+ const prevNonVisibleAttributes = utils.contentTypes.getNonVisibleAttributes(previousSchema).reduce((acc, key)=>{
1031
+ if (key in previousAttributes) {
1032
+ acc[key] = previousAttributes[key];
1033
+ }
1034
+ return acc;
1035
+ }, {});
1036
+ contentType.attributes = _.merge(prevNonVisibleAttributes, contentType.attributes);
1037
+ if (newKind !== previousKind && newKind === 'singleType') {
1038
+ const entryCount = await strapi.db.query(uid).count();
1039
+ if (entryCount > 1) {
1040
+ throw new ApplicationError('You cannot convert a collectionType to a singleType when having multiple entries in DB');
1041
+ }
971
1042
  }
972
- return builder2.editComponent(replaceTmpUIDs(component));
973
- });
974
- if (newKind !== previousKind) {
975
- const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
976
- await apiHandler2.backup(uid);
977
- try {
978
- await apiHandler2.clear(uid);
979
- await generateAPI({
980
- displayName: updatedContentType.schema.info.displayName,
981
- singularName: updatedContentType.schema.info.singularName,
982
- pluralName: updatedContentType.schema.info.pluralName,
983
- kind: updatedContentType.schema.kind
984
- });
985
- await builder2.writeFiles();
986
- } catch (error) {
987
- strapi.log.error(error);
988
- await apiHandler2.rollback(uid);
1043
+ const uidMap = builder.createNewComponentUIDMap(components);
1044
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1045
+ const updatedContentType = builder.editContentType({
1046
+ uid,
1047
+ ...replaceTmpUIDs(contentType)
1048
+ });
1049
+ components.forEach((component)=>{
1050
+ if (!_.has(component, 'uid')) {
1051
+ return builder.createComponent(replaceTmpUIDs(component));
1052
+ }
1053
+ return builder.editComponent(replaceTmpUIDs(component));
1054
+ });
1055
+ if (newKind !== previousKind) {
1056
+ const apiHandler = strapi.plugin('content-type-builder').service('api-handler');
1057
+ await apiHandler.backup(uid);
1058
+ try {
1059
+ await apiHandler.clear(uid);
1060
+ // generate new api skeleton
1061
+ await generateAPI({
1062
+ displayName: updatedContentType.schema.info.displayName,
1063
+ singularName: updatedContentType.schema.info.singularName,
1064
+ pluralName: updatedContentType.schema.info.pluralName,
1065
+ kind: updatedContentType.schema.kind
1066
+ });
1067
+ await builder.writeFiles();
1068
+ } catch (error) {
1069
+ strapi.log.error(error);
1070
+ await apiHandler.rollback(uid);
1071
+ }
1072
+ return updatedContentType;
989
1073
  }
1074
+ await builder.writeFiles();
1075
+ strapi.eventHub.emit('content-type.update', {
1076
+ contentType: updatedContentType
1077
+ });
990
1078
  return updatedContentType;
991
- }
992
- await builder2.writeFiles();
993
- strapi.eventHub.emit("content-type.update", { contentType: updatedContentType });
994
- return updatedContentType;
995
- };
996
- const deleteContentTypes = async (uids) => {
997
- const builder2 = createBuilder();
998
- const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
999
- for (const uid of uids) {
1000
- await deleteContentType(uid, builder2);
1001
- }
1002
- await builder2.writeFiles();
1003
- for (const uid of uids) {
1004
- try {
1005
- await apiHandler2.clear(uid);
1006
- } catch (error) {
1007
- strapi.log.error(error);
1008
- await apiHandler2.rollback(uid);
1009
- }
1010
- }
1011
1079
  };
1012
- const deleteContentType = async (uid, defaultBuilder = void 0) => {
1013
- const builder2 = defaultBuilder || createBuilder();
1014
- const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
1015
- await apiHandler2.backup(uid);
1016
- const contentType = builder2.deleteContentType(uid);
1017
- if (!defaultBuilder) {
1018
- try {
1019
- await builder2.writeFiles();
1020
- await apiHandler2.clear(uid);
1021
- } catch (error) {
1022
- await apiHandler2.rollback(uid);
1080
+ const deleteContentTypes = async (uids)=>{
1081
+ const builder = createBuilder();
1082
+ const apiHandler = strapi.plugin('content-type-builder').service('api-handler');
1083
+ for (const uid of uids){
1084
+ await deleteContentType(uid, builder);
1085
+ }
1086
+ await builder.writeFiles();
1087
+ for (const uid of uids){
1088
+ try {
1089
+ await apiHandler.clear(uid);
1090
+ } catch (error) {
1091
+ strapi.log.error(error);
1092
+ await apiHandler.rollback(uid);
1093
+ }
1023
1094
  }
1024
- }
1025
- strapi.eventHub.emit("content-type.delete", { contentType });
1026
- return contentType;
1027
1095
  };
1028
- const contentTypes$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1029
- __proto__: null,
1030
- createContentType,
1031
- createContentTypes,
1032
- deleteContentType,
1033
- deleteContentTypes,
1034
- editContentType,
1035
- formatContentType,
1036
- generateAPI,
1037
- getRestrictRelationsTo,
1038
- isContentTypeVisible
1039
- }, Symbol.toStringTag, { value: "Module" }));
1040
- const formatComponent = (component) => {
1041
- const { uid, modelName, connection, collectionName, info, category } = component;
1042
- return {
1043
- uid,
1044
- category,
1045
- apiId: modelName,
1046
- schema: {
1047
- displayName: _.get(info, "displayName"),
1048
- description: _.get(info, "description", ""),
1049
- icon: _.get(info, "icon"),
1050
- connection,
1051
- collectionName,
1052
- pluginOptions: component.pluginOptions,
1053
- attributes: formatAttributes(component)
1054
- }
1055
- };
1056
- };
1057
- const createComponent = async ({ component, components: components2 = [] }) => {
1058
- const builder2 = createBuilder();
1059
- const uidMap = builder2.createNewComponentUIDMap(components2);
1060
- const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1061
- const newComponent = builder2.createComponent(replaceTmpUIDs(component));
1062
- components2.forEach((component2) => {
1063
- if (!_.has(component2, "uid")) {
1064
- return builder2.createComponent(replaceTmpUIDs(component2));
1065
- }
1066
- return builder2.editComponent(replaceTmpUIDs(component2));
1067
- });
1068
- await builder2.writeFiles();
1069
- strapi.eventHub.emit("component.create", { component: newComponent });
1070
- return newComponent;
1071
- };
1072
- const editComponent = async (uid, { component, components: components2 = [] }) => {
1073
- const builder2 = createBuilder();
1074
- const uidMap = builder2.createNewComponentUIDMap(components2);
1075
- const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1076
- const updatedComponent = builder2.editComponent({
1077
- uid,
1078
- ...replaceTmpUIDs(component)
1079
- });
1080
- components2.forEach((component2) => {
1081
- if (!_.has(component2, "uid")) {
1082
- return builder2.createComponent(replaceTmpUIDs(component2));
1096
+ /**
1097
+ * Deletes a content type and the api files related to it
1098
+ */ const deleteContentType = async (uid, defaultBuilder = undefined)=>{
1099
+ const builder = defaultBuilder || createBuilder();
1100
+ // make a backup
1101
+ const apiHandler = strapi.plugin('content-type-builder').service('api-handler');
1102
+ await apiHandler.backup(uid);
1103
+ const contentType = builder.deleteContentType(uid);
1104
+ if (!defaultBuilder) {
1105
+ try {
1106
+ await builder.writeFiles();
1107
+ await apiHandler.clear(uid);
1108
+ } catch (error) {
1109
+ await apiHandler.rollback(uid);
1110
+ }
1083
1111
  }
1084
- return builder2.editComponent(replaceTmpUIDs(component2));
1085
- });
1086
- await builder2.writeFiles();
1087
- strapi.eventHub.emit("component.update", { component: updatedComponent });
1088
- return updatedComponent;
1089
- };
1090
- const deleteComponent = async (uid) => {
1091
- const builder2 = createBuilder();
1092
- const deletedComponent = builder2.deleteComponent(uid);
1093
- await builder2.writeFiles();
1094
- strapi.eventHub.emit("component.delete", { component: deletedComponent });
1095
- return deletedComponent;
1096
- };
1097
- const components$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1112
+ strapi.eventHub.emit('content-type.delete', {
1113
+ contentType
1114
+ });
1115
+ return contentType;
1116
+ };
1117
+
1118
+ var contentTypes$1 = /*#__PURE__*/Object.freeze({
1098
1119
  __proto__: null,
1099
- createComponent,
1100
- deleteComponent,
1101
- editComponent,
1102
- formatComponent
1103
- }, Symbol.toStringTag, { value: "Module" }));
1104
- const editCategory = async (name, infos) => {
1105
- const newName = utils.strings.nameToSlug(infos.name);
1106
- if (name === newName) return;
1107
- if (!categoryExists(name)) {
1108
- throw new utils.errors.ApplicationError("category not found");
1109
- }
1110
- if (categoryExists(newName)) {
1111
- throw new utils.errors.ApplicationError("Name already taken");
1112
- }
1113
- const builder2 = createBuilder();
1114
- builder2.components.forEach((component) => {
1115
- const oldUID = component.uid;
1116
- const newUID = `${newName}.${component.modelName}`;
1117
- if (component.category !== name) return;
1118
- component.setUID(newUID).setDir(path.join(strapi.dirs.app.components, newName));
1119
- builder2.components.forEach((compo) => {
1120
- compo.updateComponent(oldUID, newUID);
1120
+ createContentType: createContentType,
1121
+ createContentTypes: createContentTypes,
1122
+ deleteContentType: deleteContentType,
1123
+ deleteContentTypes: deleteContentTypes,
1124
+ editContentType: editContentType,
1125
+ formatContentType: formatContentType,
1126
+ generateAPI: generateAPI,
1127
+ getRestrictRelationsTo: getRestrictRelationsTo,
1128
+ isContentTypeVisible: isContentTypeVisible
1129
+ });
1130
+
1131
+ /**
1132
+ * Formats a component attributes
1133
+ */ const formatComponent = (component)=>{
1134
+ const { uid, modelName, connection, collectionName, info, category } = component;
1135
+ return {
1136
+ uid,
1137
+ category,
1138
+ apiId: modelName,
1139
+ schema: {
1140
+ displayName: _.get(info, 'displayName'),
1141
+ description: _.get(info, 'description', ''),
1142
+ icon: _.get(info, 'icon'),
1143
+ connection,
1144
+ collectionName,
1145
+ pluginOptions: component.pluginOptions,
1146
+ attributes: formatAttributes(component)
1147
+ }
1148
+ };
1149
+ };
1150
+ /**
1151
+ * Creates a component and handle the nested components sent with it
1152
+ */ const createComponent = async ({ component, components = [] })=>{
1153
+ const builder = createBuilder();
1154
+ const uidMap = builder.createNewComponentUIDMap(components);
1155
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1156
+ const newComponent = builder.createComponent(replaceTmpUIDs(component));
1157
+ components.forEach((component)=>{
1158
+ if (!_.has(component, 'uid')) {
1159
+ return builder.createComponent(replaceTmpUIDs(component));
1160
+ }
1161
+ return builder.editComponent(replaceTmpUIDs(component));
1162
+ });
1163
+ await builder.writeFiles();
1164
+ strapi.eventHub.emit('component.create', {
1165
+ component: newComponent
1166
+ });
1167
+ return newComponent;
1168
+ };
1169
+ const editComponent = async (uid, { component, components = [] })=>{
1170
+ const builder = createBuilder();
1171
+ const uidMap = builder.createNewComponentUIDMap(components);
1172
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1173
+ const updatedComponent = builder.editComponent({
1174
+ uid,
1175
+ ...replaceTmpUIDs(component)
1176
+ });
1177
+ components.forEach((component)=>{
1178
+ if (!_.has(component, 'uid')) {
1179
+ return builder.createComponent(replaceTmpUIDs(component));
1180
+ }
1181
+ return builder.editComponent(replaceTmpUIDs(component));
1121
1182
  });
1122
- builder2.contentTypes.forEach((ct) => {
1123
- ct.updateComponent(oldUID, newUID);
1183
+ await builder.writeFiles();
1184
+ strapi.eventHub.emit('component.update', {
1185
+ component: updatedComponent
1124
1186
  });
1125
- });
1126
- await builder2.writeFiles();
1127
- return newName;
1187
+ return updatedComponent;
1188
+ };
1189
+ const deleteComponent = async (uid)=>{
1190
+ const builder = createBuilder();
1191
+ const deletedComponent = builder.deleteComponent(uid);
1192
+ await builder.writeFiles();
1193
+ strapi.eventHub.emit('component.delete', {
1194
+ component: deletedComponent
1195
+ });
1196
+ return deletedComponent;
1128
1197
  };
1129
- const deleteCategory = async (name) => {
1130
- if (!categoryExists(name)) {
1131
- throw new utils.errors.ApplicationError("category not found");
1132
- }
1133
- const builder2 = createBuilder();
1134
- builder2.components.forEach((component) => {
1135
- if (component.category === name) {
1136
- builder2.deleteComponent(component.uid);
1137
- }
1138
- });
1139
- await builder2.writeFiles();
1198
+
1199
+ var components$1 = /*#__PURE__*/Object.freeze({
1200
+ __proto__: null,
1201
+ createComponent: createComponent,
1202
+ deleteComponent: deleteComponent,
1203
+ editComponent: editComponent,
1204
+ formatComponent: formatComponent
1205
+ });
1206
+
1207
+ /**
1208
+ * Edit a category name and move components to the write folder
1209
+ */ const editCategory = async (name, infos)=>{
1210
+ const newName = utils.strings.nameToSlug(infos.name);
1211
+ // don't do anything the name doesn't change
1212
+ if (name === newName) return;
1213
+ if (!categoryExists(name)) {
1214
+ throw new utils.errors.ApplicationError('category not found');
1215
+ }
1216
+ if (categoryExists(newName)) {
1217
+ throw new utils.errors.ApplicationError('Name already taken');
1218
+ }
1219
+ const builder = createBuilder();
1220
+ builder.components.forEach((component)=>{
1221
+ const oldUID = component.uid;
1222
+ const newUID = `${newName}.${component.modelName}`;
1223
+ // only edit the components in this specific category
1224
+ if (component.category !== name) return;
1225
+ component.setUID(newUID).setDir(path.join(strapi.dirs.app.components, newName));
1226
+ builder.components.forEach((compo)=>{
1227
+ compo.updateComponent(oldUID, newUID);
1228
+ });
1229
+ builder.contentTypes.forEach((ct)=>{
1230
+ ct.updateComponent(oldUID, newUID);
1231
+ });
1232
+ });
1233
+ await builder.writeFiles();
1234
+ return newName;
1235
+ };
1236
+ /**
1237
+ * Deletes a category and its components
1238
+ */ const deleteCategory = async (name)=>{
1239
+ if (!categoryExists(name)) {
1240
+ throw new utils.errors.ApplicationError('category not found');
1241
+ }
1242
+ const builder = createBuilder();
1243
+ builder.components.forEach((component)=>{
1244
+ if (component.category === name) {
1245
+ builder.deleteComponent(component.uid);
1246
+ }
1247
+ });
1248
+ await builder.writeFiles();
1140
1249
  };
1141
- const categoryExists = (name) => {
1142
- const matchingIndex = Object.values(strapi.components).findIndex(
1143
- (component) => component.category === name
1144
- );
1145
- return matchingIndex > -1;
1250
+ /**
1251
+ * Checks if a category exists
1252
+ */ const categoryExists = (name)=>{
1253
+ const matchingIndex = Object.values(strapi.components).findIndex((component)=>component.category === name);
1254
+ return matchingIndex > -1;
1146
1255
  };
1147
- const componentCategories$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1256
+
1257
+ var componentCategories$1 = /*#__PURE__*/Object.freeze({
1148
1258
  __proto__: null,
1149
- deleteCategory,
1150
- editCategory
1151
- }, Symbol.toStringTag, { value: "Module" }));
1259
+ deleteCategory: deleteCategory,
1260
+ editCategory: editCategory
1261
+ });
1262
+
1263
+ // use snake_case
1152
1264
  const reservedAttributes = [
1153
- // TODO: these need to come from a centralized place so we don't break things accidentally in the future and can share them outside the CTB, for example on Strapi bootstrap prior to schema db sync
1154
- // ID fields
1155
- "id",
1156
- "document_id",
1157
- // Creator fields
1158
- "created_at",
1159
- "updated_at",
1160
- "published_at",
1161
- "created_by_id",
1162
- "updated_by_id",
1163
- // does not actually conflict because the fields are called *_by_id but we'll leave it to avoid confusion
1164
- "created_by",
1165
- "updated_by",
1166
- // Used for Strapi functionality
1167
- "entry_id",
1168
- "status",
1169
- "localizations",
1170
- "meta",
1171
- "locale",
1172
- "__component",
1173
- "__contentType",
1174
- // We support ending with * to denote prefixes
1175
- "strapi*",
1176
- "_strapi*",
1177
- "__strapi*"
1265
+ // TODO: these need to come from a centralized place so we don't break things accidentally in the future and can share them outside the CTB, for example on Strapi bootstrap prior to schema db sync
1266
+ // ID fields
1267
+ 'id',
1268
+ 'document_id',
1269
+ // Creator fields
1270
+ 'created_at',
1271
+ 'updated_at',
1272
+ 'published_at',
1273
+ 'created_by_id',
1274
+ 'updated_by_id',
1275
+ // does not actually conflict because the fields are called *_by_id but we'll leave it to avoid confusion
1276
+ 'created_by',
1277
+ 'updated_by',
1278
+ // Used for Strapi functionality
1279
+ 'entry_id',
1280
+ 'status',
1281
+ 'localizations',
1282
+ 'meta',
1283
+ 'locale',
1284
+ '__component',
1285
+ '__contentType',
1286
+ // We support ending with * to denote prefixes
1287
+ 'strapi*',
1288
+ '_strapi*',
1289
+ '__strapi*'
1178
1290
  ];
1291
+ // use snake_case
1179
1292
  const reservedModels = [
1180
- "boolean",
1181
- "date",
1182
- "date_time",
1183
- "time",
1184
- "upload",
1185
- "document",
1186
- "then",
1187
- // no longer an issue but still restricting for being a javascript keyword
1188
- // We support ending with * to denote prefixes
1189
- "strapi*",
1190
- "_strapi*",
1191
- "__strapi*"
1293
+ 'boolean',
1294
+ 'date',
1295
+ 'date_time',
1296
+ 'time',
1297
+ 'upload',
1298
+ 'document',
1299
+ 'then',
1300
+ // We support ending with * to denote prefixes
1301
+ 'strapi*',
1302
+ '_strapi*',
1303
+ '__strapi*'
1192
1304
  ];
1193
- const getReservedNames = () => {
1194
- return {
1195
- models: reservedModels,
1196
- attributes: reservedAttributes
1197
- };
1198
- };
1199
- const isReservedModelName = (name) => {
1200
- const snakeCaseName = fp.snakeCase(name);
1201
- if (reservedModels.includes(snakeCaseName)) {
1202
- return true;
1203
- }
1204
- if (reservedModels.filter((key) => key.endsWith("*")).map((key) => key.slice(0, -1)).some((prefix) => snakeCaseName.startsWith(prefix))) {
1205
- return true;
1206
- }
1207
- return false;
1305
+ const getReservedNames = ()=>{
1306
+ return {
1307
+ models: reservedModels,
1308
+ attributes: reservedAttributes
1309
+ };
1208
1310
  };
1209
- const isReservedAttributeName = (name) => {
1210
- const snakeCaseName = fp.snakeCase(name);
1211
- if (reservedAttributes.includes(snakeCaseName)) {
1212
- return true;
1213
- }
1214
- if (reservedAttributes.filter((key) => key.endsWith("*")).map((key) => key.slice(0, -1)).some((prefix) => snakeCaseName.startsWith(prefix))) {
1215
- return true;
1216
- }
1217
- return false;
1311
+ // compare snake case to check the actual column names that will be used in the database
1312
+ const isReservedModelName = (name)=>{
1313
+ const snakeCaseName = fp.snakeCase(name);
1314
+ if (reservedModels.includes(snakeCaseName)) {
1315
+ return true;
1316
+ }
1317
+ if (reservedModels.filter((key)=>key.endsWith('*')).map((key)=>key.slice(0, -1)).some((prefix)=>snakeCaseName.startsWith(prefix))) {
1318
+ return true;
1319
+ }
1320
+ return false;
1218
1321
  };
1219
- const builder$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1322
+ // compare snake case to check the actual column names that will be used in the database
1323
+ const isReservedAttributeName = (name)=>{
1324
+ const snakeCaseName = fp.snakeCase(name);
1325
+ if (reservedAttributes.includes(snakeCaseName)) {
1326
+ return true;
1327
+ }
1328
+ if (reservedAttributes.filter((key)=>key.endsWith('*')).map((key)=>key.slice(0, -1)).some((prefix)=>snakeCaseName.startsWith(prefix))) {
1329
+ return true;
1330
+ }
1331
+ return false;
1332
+ };
1333
+
1334
+ var builder$1 = /*#__PURE__*/Object.freeze({
1220
1335
  __proto__: null,
1221
- getReservedNames,
1222
- isReservedAttributeName,
1223
- isReservedModelName,
1224
- reservedAttributes,
1225
- reservedModels
1226
- }, Symbol.toStringTag, { value: "Module" }));
1227
- async function clear(uid) {
1228
- const { apiName, modelName } = strapi.contentTypes[uid];
1229
- const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1230
- await recursiveRemoveFiles(apiFolder, createDeleteApiFunction(modelName));
1231
- await deleteBackup(uid);
1336
+ getReservedNames: getReservedNames,
1337
+ isReservedAttributeName: isReservedAttributeName,
1338
+ isReservedModelName: isReservedModelName,
1339
+ reservedAttributes: reservedAttributes,
1340
+ reservedModels: reservedModels
1341
+ });
1342
+
1343
+ /**
1344
+ * Deletes the API folder of a contentType
1345
+ */ async function clear(uid) {
1346
+ // TODO double check if this is the correct way to get the apiName
1347
+ const { apiName, modelName } = strapi.contentTypes[uid];
1348
+ const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1349
+ await recursiveRemoveFiles(apiFolder, createDeleteApiFunction(modelName));
1350
+ await deleteBackup(uid);
1232
1351
  }
1233
- async function backup(uid) {
1234
- const { apiName } = strapi.contentTypes[uid];
1235
- const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1236
- const backupFolder = path__namespace.join(strapi.dirs.app.api, ".backup", apiName);
1237
- await fse__namespace.copy(apiFolder, backupFolder);
1352
+ /**
1353
+ * Backups the API folder of a contentType
1354
+ * @param {string} uid content type uid
1355
+ */ async function backup(uid) {
1356
+ const { apiName } = strapi.contentTypes[uid];
1357
+ const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1358
+ const backupFolder = path__namespace.join(strapi.dirs.app.api, '.backup', apiName);
1359
+ // backup the api folder
1360
+ await fse__namespace.copy(apiFolder, backupFolder);
1238
1361
  }
1239
- async function deleteBackup(uid) {
1240
- const { apiName } = strapi.contentTypes[uid];
1241
- const backupFolder = path__namespace.join(strapi.dirs.app.api, ".backup");
1242
- const apiBackupFolder = path__namespace.join(strapi.dirs.app.api, ".backup", apiName);
1243
- await fse__namespace.remove(apiBackupFolder);
1244
- const list = await fse__namespace.readdir(backupFolder);
1245
- if (list.length === 0) {
1246
- await fse__namespace.remove(backupFolder);
1247
- }
1362
+ /**
1363
+ * Deletes an API backup folder
1364
+ */ async function deleteBackup(uid) {
1365
+ const { apiName } = strapi.contentTypes[uid];
1366
+ const backupFolder = path__namespace.join(strapi.dirs.app.api, '.backup');
1367
+ const apiBackupFolder = path__namespace.join(strapi.dirs.app.api, '.backup', apiName);
1368
+ await fse__namespace.remove(apiBackupFolder);
1369
+ const list = await fse__namespace.readdir(backupFolder);
1370
+ if (list.length === 0) {
1371
+ await fse__namespace.remove(backupFolder);
1372
+ }
1248
1373
  }
1249
- async function rollback(uid) {
1250
- const { apiName } = strapi.contentTypes[uid];
1251
- const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1252
- const backupFolder = path__namespace.join(strapi.dirs.app.api, ".backup", apiName);
1253
- try {
1254
- await fse__namespace.access(backupFolder);
1255
- } catch {
1256
- throw new Error("Cannot rollback api that was not backed up");
1257
- }
1258
- await fse__namespace.remove(apiFolder);
1259
- await fse__namespace.copy(backupFolder, apiFolder);
1260
- await deleteBackup(uid);
1374
+ /**
1375
+ * Rollbacks the API folder of a contentType
1376
+ */ async function rollback(uid) {
1377
+ const { apiName } = strapi.contentTypes[uid];
1378
+ const apiFolder = path__namespace.join(strapi.dirs.app.api, apiName);
1379
+ const backupFolder = path__namespace.join(strapi.dirs.app.api, '.backup', apiName);
1380
+ try {
1381
+ await fse__namespace.access(backupFolder);
1382
+ } catch {
1383
+ throw new Error('Cannot rollback api that was not backed up');
1384
+ }
1385
+ await fse__namespace.remove(apiFolder);
1386
+ await fse__namespace.copy(backupFolder, apiFolder);
1387
+ await deleteBackup(uid);
1261
1388
  }
1262
- const createDeleteApiFunction = (baseName) => {
1263
- return async (filePath) => {
1264
- const fileName = path__namespace.basename(filePath, path__namespace.extname(filePath));
1265
- const isSchemaFile = filePath.endsWith(`${baseName}/schema.json`);
1266
- if (fileName === baseName || isSchemaFile) {
1267
- return fse__namespace.remove(filePath);
1389
+ /**
1390
+ * Creates a delete function to clear an api folder
1391
+ */ const createDeleteApiFunction = (baseName)=>{
1392
+ /**
1393
+ * Delets a file in an api.
1394
+ * Will only update routes.json instead of deleting it if other routes are present
1395
+ */ return async (filePath)=>{
1396
+ const fileName = path__namespace.basename(filePath, path__namespace.extname(filePath));
1397
+ const isSchemaFile = filePath.endsWith(`${baseName}/schema.json`);
1398
+ if (fileName === baseName || isSchemaFile) {
1399
+ return fse__namespace.remove(filePath);
1400
+ }
1401
+ };
1402
+ };
1403
+ /**
1404
+ * Deletes a folder recursively using a delete function
1405
+ * @param {string} folder folder to delete
1406
+ */ const recursiveRemoveFiles = async (folder, deleteFn)=>{
1407
+ const filesName = await fse__namespace.readdir(folder);
1408
+ for (const fileName of filesName){
1409
+ const filePath = path__namespace.join(folder, fileName);
1410
+ const stat = await fse__namespace.stat(filePath);
1411
+ if (stat.isDirectory()) {
1412
+ await recursiveRemoveFiles(filePath, deleteFn);
1413
+ } else {
1414
+ await deleteFn(filePath);
1415
+ }
1268
1416
  }
1269
- };
1270
- };
1271
- const recursiveRemoveFiles = async (folder, deleteFn) => {
1272
- const filesName = await fse__namespace.readdir(folder);
1273
- for (const fileName of filesName) {
1274
- const filePath = path__namespace.join(folder, fileName);
1275
- const stat = await fse__namespace.stat(filePath);
1276
- if (stat.isDirectory()) {
1277
- await recursiveRemoveFiles(filePath, deleteFn);
1278
- } else {
1279
- await deleteFn(filePath);
1417
+ const files = await fse__namespace.readdir(folder);
1418
+ if (files.length === 0) {
1419
+ await fse__namespace.remove(folder);
1280
1420
  }
1281
- }
1282
- const files = await fse__namespace.readdir(folder);
1283
- if (files.length === 0) {
1284
- await fse__namespace.remove(folder);
1285
- }
1286
1421
  };
1287
- const apiHandler = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1422
+
1423
+ var apiHandler = /*#__PURE__*/Object.freeze({
1288
1424
  __proto__: null,
1289
- backup,
1290
- clear,
1291
- rollback
1292
- }, Symbol.toStringTag, { value: "Module" }));
1293
- const services = {
1294
- "content-types": contentTypes$1,
1295
- components: components$1,
1296
- "component-categories": componentCategories$1,
1297
- builder: builder$1,
1298
- "api-handler": apiHandler
1299
- };
1425
+ backup: backup,
1426
+ clear: clear,
1427
+ rollback: rollback
1428
+ });
1429
+
1430
+ var services = {
1431
+ 'content-types': contentTypes$1,
1432
+ components: components$1,
1433
+ 'component-categories': componentCategories$1,
1434
+ builder: builder$1,
1435
+ 'api-handler': apiHandler
1436
+ };
1437
+
1300
1438
  function getService(name) {
1301
- return strapi.plugin("content-type-builder").service(name);
1439
+ return strapi.plugin('content-type-builder').service(name);
1302
1440
  }
1303
- const builder = {
1304
- getReservedNames(ctx) {
1305
- ctx.body = getService("builder").getReservedNames();
1306
- }
1441
+
1442
+ var builder = {
1443
+ getReservedNames (ctx) {
1444
+ ctx.body = getService('builder').getReservedNames();
1445
+ }
1307
1446
  };
1447
+
1308
1448
  const validators = {
1309
- required: utils.yup.boolean(),
1310
- unique: utils.yup.boolean(),
1311
- minLength: utils.yup.number().integer().positive(),
1312
- maxLength: utils.yup.number().integer().positive()
1449
+ required: utils.yup.boolean(),
1450
+ unique: utils.yup.boolean(),
1451
+ minLength: utils.yup.number().integer().positive(),
1452
+ maxLength: utils.yup.number().integer().positive()
1313
1453
  };
1314
1454
  const NAME_REGEX = /^[A-Za-z][_0-9A-Za-z]*$/;
1315
1455
  const COLLECTION_NAME_REGEX = /^[A-Za-z][-_0-9A-Za-z]*$/;
@@ -1317,997 +1457,1210 @@ const CATEGORY_NAME_REGEX = /^[A-Za-z][-_0-9A-Za-z]*$/;
1317
1457
  const ICON_REGEX = /^[A-Za-z0-9][-A-Za-z0-9]*$/;
1318
1458
  const UID_REGEX = /^[A-Za-z0-9-_.~]*$/;
1319
1459
  const isValidName = {
1320
- name: "isValidName",
1321
- message: `\${path} must match the following regex: ${NAME_REGEX}`,
1322
- test: (val) => val === "" || NAME_REGEX.test(val)
1460
+ name: 'isValidName',
1461
+ message: `\${path} must match the following regex: ${NAME_REGEX}`,
1462
+ test: (val)=>val === '' || NAME_REGEX.test(val)
1323
1463
  };
1324
1464
  const isValidIcon = {
1325
- name: "isValidIcon",
1326
- message: `\${path} is not a valid icon name. Make sure your icon name starts with an alphanumeric character and only includes alphanumeric characters or dashes.`,
1327
- test: (val) => val === "" || ICON_REGEX.test(val)
1465
+ name: 'isValidIcon',
1466
+ message: `\${path} is not a valid icon name. Make sure your icon name starts with an alphanumeric character and only includes alphanumeric characters or dashes.`,
1467
+ test: (val)=>val === '' || ICON_REGEX.test(val)
1328
1468
  };
1329
1469
  const isValidUID = {
1330
- name: "isValidUID",
1331
- message: `\${path} must match the following regex: ${UID_REGEX}`,
1332
- test: (val) => val === "" || UID_REGEX.test(val)
1470
+ name: 'isValidUID',
1471
+ message: `\${path} must match the following regex: ${UID_REGEX}`,
1472
+ test: (val)=>val === '' || UID_REGEX.test(val)
1333
1473
  };
1334
1474
  const isValidCategoryName = {
1335
- name: "isValidCategoryName",
1336
- message: `\${path} must match the following regex: ${CATEGORY_NAME_REGEX}`,
1337
- test: (val) => val === "" || CATEGORY_NAME_REGEX.test(val)
1475
+ name: 'isValidCategoryName',
1476
+ message: `\${path} must match the following regex: ${CATEGORY_NAME_REGEX}`,
1477
+ test: (val)=>val === '' || CATEGORY_NAME_REGEX.test(val)
1338
1478
  };
1339
1479
  const isValidCollectionName = {
1340
- name: "isValidCollectionName",
1341
- message: `\${path} must match the following regex: ${COLLECTION_NAME_REGEX}`,
1342
- test: (val) => val === "" || COLLECTION_NAME_REGEX.test(val)
1343
- };
1344
- const isValidKey = (key) => ({
1345
- name: "isValidKey",
1346
- message: `Attribute name '${key}' must match the following regex: ${NAME_REGEX}`,
1347
- test: () => NAME_REGEX.test(key)
1348
- });
1480
+ name: 'isValidCollectionName',
1481
+ message: `\${path} must match the following regex: ${COLLECTION_NAME_REGEX}`,
1482
+ test: (val)=>val === '' || COLLECTION_NAME_REGEX.test(val)
1483
+ };
1484
+ const isValidKey = (key)=>({
1485
+ name: 'isValidKey',
1486
+ message: `Attribute name '${key}' must match the following regex: ${NAME_REGEX}`,
1487
+ test: ()=>NAME_REGEX.test(key)
1488
+ });
1349
1489
  const isValidEnum = {
1350
- name: "isValidEnum",
1351
- message: "${path} should not start with number",
1352
- test: (val) => val === "" || !utils.strings.startsWithANumber(val)
1490
+ name: 'isValidEnum',
1491
+ message: '${path} should not start with number',
1492
+ test: (val)=>val === '' || !utils.strings.startsWithANumber(val)
1353
1493
  };
1354
1494
  const areEnumValuesUnique = {
1355
- name: "areEnumValuesUnique",
1356
- message: "${path} cannot contain duplicate values",
1357
- test(values) {
1358
- const filtered = [...new Set(values)];
1359
- return filtered.length === values.length;
1360
- }
1495
+ name: 'areEnumValuesUnique',
1496
+ message: '${path} cannot contain duplicate values',
1497
+ test (values) {
1498
+ const filtered = [
1499
+ ...new Set(values)
1500
+ ];
1501
+ return filtered.length === values.length;
1502
+ }
1361
1503
  };
1362
1504
  const isValidRegExpPattern = {
1363
- name: "isValidRegExpPattern",
1364
- message: "${path} must be a valid RexExp pattern string",
1365
- test: (val) => val === "" || !!new RegExp(val)
1366
- };
1367
- const isValidDefaultJSON = {
1368
- name: "isValidDefaultJSON",
1369
- message: "${path} is not a valid JSON",
1370
- test(val) {
1371
- if (val === void 0) {
1372
- return true;
1373
- }
1374
- if (___default.default.isNumber(val) || ___default.default.isNull(val) || ___default.default.isObject(val) || ___default.default.isArray(val)) {
1375
- return true;
1376
- }
1377
- try {
1378
- JSON.parse(val);
1379
- return true;
1380
- } catch (err) {
1381
- return false;
1505
+ name: 'isValidRegExpPattern',
1506
+ message: '${path} must be a valid RexExp pattern string',
1507
+ test: (val)=>val === '' || !!new RegExp(val)
1508
+ };
1509
+ const isValidDefaultJSON = {
1510
+ name: 'isValidDefaultJSON',
1511
+ message: '${path} is not a valid JSON',
1512
+ test (val) {
1513
+ if (val === undefined) {
1514
+ return true;
1515
+ }
1516
+ if (_.isNumber(val) || _.isNull(val) || _.isObject(val) || _.isArray(val)) {
1517
+ return true;
1518
+ }
1519
+ try {
1520
+ JSON.parse(val);
1521
+ return true;
1522
+ } catch (err) {
1523
+ return false;
1524
+ }
1382
1525
  }
1383
- }
1384
1526
  };
1527
+
1385
1528
  const componentCategorySchema = utils.yup.object({
1386
- name: utils.yup.string().min(3).test(isValidCategoryName).required("name.required")
1529
+ name: utils.yup.string().min(3).test(isValidCategoryName).required('name.required')
1387
1530
  }).noUnknown();
1388
- const validateComponentCategory = utils.validateYupSchema(componentCategorySchema);
1389
- const componentCategories = {
1390
- async editCategory(ctx) {
1391
- const body = ctx.request.body;
1392
- try {
1393
- await validateComponentCategory(body);
1394
- } catch (error) {
1395
- return ctx.send({ error }, 400);
1531
+ var validateComponentCategory = utils.validateYupSchema(componentCategorySchema);
1532
+
1533
+ var componentCategories = {
1534
+ async editCategory (ctx) {
1535
+ const body = ctx.request.body;
1536
+ try {
1537
+ await validateComponentCategory(body);
1538
+ } catch (error) {
1539
+ return ctx.send({
1540
+ error
1541
+ }, 400);
1542
+ }
1543
+ const { name } = ctx.params;
1544
+ strapi.reload.isWatching = false;
1545
+ const componentCategoryService = getService('component-categories');
1546
+ const newName = await componentCategoryService.editCategory(name, body);
1547
+ setImmediate(()=>strapi.reload());
1548
+ ctx.send({
1549
+ name: newName
1550
+ });
1551
+ },
1552
+ async deleteCategory (ctx) {
1553
+ const { name } = ctx.params;
1554
+ strapi.reload.isWatching = false;
1555
+ const componentCategoryService = getService('component-categories');
1556
+ await componentCategoryService.deleteCategory(name);
1557
+ setImmediate(()=>strapi.reload());
1558
+ ctx.send({
1559
+ name
1560
+ });
1396
1561
  }
1397
- const { name } = ctx.params;
1398
- strapi.reload.isWatching = false;
1399
- const componentCategoryService = getService("component-categories");
1400
- const newName = await componentCategoryService.editCategory(name, body);
1401
- setImmediate(() => strapi.reload());
1402
- ctx.send({ name: newName });
1403
- },
1404
- async deleteCategory(ctx) {
1405
- const { name } = ctx.params;
1406
- strapi.reload.isWatching = false;
1407
- const componentCategoryService = getService("component-categories");
1408
- await componentCategoryService.deleteCategory(name);
1409
- setImmediate(() => strapi.reload());
1410
- ctx.send({ name });
1411
- }
1412
1562
  };
1563
+
1413
1564
  const maxLengthIsGreaterThanOrEqualToMinLength = {
1414
- name: "isGreaterThanMin",
1415
- message: "maxLength must be greater or equal to minLength",
1416
- test(value) {
1417
- const { minLength } = this.parent;
1418
- return !(!___default.default.isUndefined(minLength) && !___default.default.isUndefined(value) && value < minLength);
1419
- }
1420
- };
1421
- const getTypeValidator = (attribute, { types, modelType, attributes }) => {
1422
- return utils.yup.object({
1423
- type: utils.yup.string().oneOf([...types]).required(),
1424
- configurable: utils.yup.boolean().nullable(),
1425
- private: utils.yup.boolean().nullable(),
1426
- pluginOptions: utils.yup.object(),
1427
- ...getTypeShape(attribute, { modelType, attributes })
1428
- });
1429
- };
1430
- const getTypeShape = (attribute, { attributes } = {}) => {
1431
- switch (attribute.type) {
1432
- case "media": {
1433
- return {
1434
- multiple: utils.yup.boolean(),
1435
- required: validators.required,
1436
- allowedTypes: utils.yup.array().of(utils.yup.string().oneOf(["images", "videos", "files", "audios"])).min(1)
1437
- };
1438
- }
1439
- case "uid": {
1440
- return {
1441
- required: validators.required,
1442
- targetField: utils.yup.string().oneOf(
1443
- Object.keys(attributes).filter(
1444
- (key) => VALID_UID_TARGETS.includes(___default.default.get(attributes[key], "type"))
1445
- )
1446
- ).nullable(),
1447
- default: utils.yup.string().test(
1448
- "isValidDefaultUID",
1449
- "cannot define a default UID if the targetField is set",
1450
- function(value) {
1451
- const { targetField } = this.parent;
1452
- return !!(___default.default.isNil(targetField) || ___default.default.isNil(value));
1453
- }
1454
- ).test(isValidUID),
1455
- minLength: validators.minLength,
1456
- maxLength: validators.maxLength.max(256).test(maxLengthIsGreaterThanOrEqualToMinLength),
1457
- options: utils.yup.object().shape({
1458
- separator: utils.yup.string(),
1459
- lowercase: utils.yup.boolean(),
1460
- decamelize: utils.yup.boolean(),
1461
- customReplacements: utils.yup.array().of(utils.yup.array().of(utils.yup.string()).min(2).max(2)),
1462
- preserveLeadingUnderscore: utils.yup.boolean()
1565
+ name: 'isGreaterThanMin',
1566
+ message: 'maxLength must be greater or equal to minLength',
1567
+ test (value) {
1568
+ const { minLength } = this.parent;
1569
+ return !(!_.isUndefined(minLength) && !_.isUndefined(value) && value < minLength);
1570
+ }
1571
+ };
1572
+ const getTypeValidator = (attribute, { types, modelType, attributes })=>{
1573
+ return utils.yup.object({
1574
+ type: utils.yup.string().oneOf([
1575
+ ...types
1576
+ ]).required(),
1577
+ configurable: utils.yup.boolean().nullable(),
1578
+ private: utils.yup.boolean().nullable(),
1579
+ pluginOptions: utils.yup.object(),
1580
+ ...getTypeShape(attribute, {
1581
+ modelType,
1582
+ attributes
1463
1583
  })
1464
- };
1465
- }
1466
- case "string":
1467
- case "text": {
1468
- return {
1469
- default: utils.yup.string(),
1470
- required: validators.required,
1471
- unique: validators.unique,
1472
- minLength: validators.minLength,
1473
- maxLength: validators.maxLength,
1474
- regex: utils.yup.string().test(isValidRegExpPattern)
1475
- };
1476
- }
1477
- case "richtext": {
1478
- return {
1479
- default: utils.yup.string(),
1480
- required: validators.required,
1481
- minLength: validators.minLength,
1482
- maxLength: validators.maxLength
1483
- };
1484
- }
1485
- case "blocks": {
1486
- return {
1487
- required: validators.required
1488
- };
1489
- }
1490
- case "json": {
1491
- return {
1492
- default: utils.yup.mixed().test(isValidDefaultJSON),
1493
- required: validators.required
1494
- };
1495
- }
1496
- case "enumeration": {
1497
- return {
1498
- enum: utils.yup.array().of(utils.yup.string().test(isValidEnum).required()).min(1).test(areEnumValuesUnique).required(),
1499
- default: utils.yup.string().when("enum", (enumVal) => utils.yup.string().oneOf(enumVal)),
1500
- enumName: utils.yup.string().test(isValidName),
1501
- required: validators.required
1502
- };
1503
- }
1504
- case "password": {
1505
- return {
1506
- required: validators.required,
1507
- minLength: validators.minLength,
1508
- maxLength: validators.maxLength
1509
- };
1510
- }
1511
- case "email": {
1512
- return {
1513
- default: utils.yup.string().email(),
1514
- required: validators.required,
1515
- unique: validators.unique,
1516
- minLength: validators.minLength,
1517
- maxLength: validators.maxLength
1518
- };
1519
- }
1520
- case "integer": {
1521
- return {
1522
- default: utils.yup.number().integer(),
1523
- required: validators.required,
1524
- unique: validators.unique,
1525
- min: utils.yup.number().integer(),
1526
- max: utils.yup.number().integer()
1527
- };
1528
- }
1529
- case "biginteger": {
1530
- return {
1531
- default: utils.yup.string().nullable().matches(/^\d*$/),
1532
- required: validators.required,
1533
- unique: validators.unique,
1534
- min: utils.yup.string().nullable().matches(/^\d*$/),
1535
- max: utils.yup.string().nullable().matches(/^\d*$/)
1536
- };
1537
- }
1538
- case "float": {
1539
- return {
1540
- default: utils.yup.number(),
1541
- required: validators.required,
1542
- unique: validators.unique,
1543
- min: utils.yup.number(),
1544
- max: utils.yup.number()
1545
- };
1546
- }
1547
- case "decimal": {
1548
- return {
1549
- default: utils.yup.number(),
1550
- required: validators.required,
1551
- unique: validators.unique,
1552
- min: utils.yup.number(),
1553
- max: utils.yup.number()
1554
- };
1555
- }
1556
- case "time":
1557
- case "datetime":
1558
- case "date": {
1559
- return {
1560
- default: utils.yup.string(),
1561
- required: validators.required,
1562
- unique: validators.unique
1563
- };
1564
- }
1565
- case "boolean": {
1566
- return {
1567
- default: utils.yup.boolean(),
1568
- required: validators.required
1569
- };
1570
- }
1571
- case "component": {
1572
- return {
1573
- required: validators.required,
1574
- repeatable: utils.yup.boolean(),
1575
- // TODO: Add correct server validation for nested components
1576
- component: utils.yup.string().required(),
1577
- min: utils.yup.number(),
1578
- max: utils.yup.number()
1579
- };
1580
- }
1581
- case "dynamiczone": {
1582
- return {
1583
- required: validators.required,
1584
- components: utils.yup.array().of(utils.yup.string().required()).test("isArray", "${path} must be an array", (value) => Array.isArray(value)).min(1),
1585
- min: utils.yup.number(),
1586
- max: utils.yup.number()
1587
- };
1588
- }
1589
- default: {
1590
- return {};
1591
- }
1592
- }
1584
+ });
1593
1585
  };
1594
- const STRAPI_USER_RELATIONS = ["oneToOne", "oneToMany"];
1595
- const isValidRelation = (validNatures) => function(value) {
1596
- if (value === void 0) {
1597
- return true;
1598
- }
1599
- if (this.parent.target === coreUids.STRAPI_USER) {
1600
- if (!validNatures.includes(value) || !fp.isUndefined(this.parent.targetAttribute)) {
1601
- return this.createError({
1602
- path: this.path,
1603
- message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(", ")}`
1604
- });
1605
- }
1606
- }
1607
- return validNatures.includes(value) ? true : this.createError({
1608
- path: this.path,
1609
- message: `must be one of the following values: ${validNatures.join(", ")}`
1610
- });
1611
- };
1612
- const getRelationValidator = (attribute, allowedRelations) => {
1613
- const contentTypesUIDs = Object.keys(strapi.contentTypes).filter((key) => strapi.contentTypes[key].kind === typeKinds.COLLECTION_TYPE).filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER).concat(["__self__", "__contentType__"]);
1614
- const base = {
1615
- type: utils.yup.string().oneOf(["relation"]).required(),
1616
- relation: utils.yup.string().test("isValidRelation", isValidRelation(allowedRelations)).required(),
1617
- configurable: utils.yup.boolean().nullable(),
1618
- private: utils.yup.boolean().nullable(),
1619
- pluginOptions: utils.yup.object()
1620
- };
1621
- switch (attribute.relation) {
1622
- case "oneToOne":
1623
- case "oneToMany":
1624
- case "manyToOne":
1625
- case "manyToMany":
1626
- case "morphOne":
1627
- case "morphMany": {
1628
- return utils.yup.object({
1629
- ...base,
1630
- target: utils.yup.string().oneOf(contentTypesUIDs).required(),
1631
- targetAttribute: utils.yup.string().test(isValidName).nullable()
1632
- });
1633
- }
1634
- case "morphToOne":
1635
- case "morphToMany":
1636
- default: {
1637
- return utils.yup.object({ ...base });
1586
+ const getTypeShape = (attribute, { attributes } = {})=>{
1587
+ switch(attribute.type){
1588
+ /**
1589
+ * complex types
1590
+ */ case 'media':
1591
+ {
1592
+ return {
1593
+ multiple: utils.yup.boolean(),
1594
+ required: validators.required,
1595
+ allowedTypes: utils.yup.array().of(utils.yup.string().oneOf([
1596
+ 'images',
1597
+ 'videos',
1598
+ 'files',
1599
+ 'audios'
1600
+ ])).min(1)
1601
+ };
1602
+ }
1603
+ case 'uid':
1604
+ {
1605
+ return {
1606
+ required: validators.required,
1607
+ targetField: utils.yup.string().oneOf(Object.keys(attributes).filter((key)=>VALID_UID_TARGETS.includes(_.get(attributes[key], 'type')))).nullable(),
1608
+ default: utils.yup.string().test('isValidDefaultUID', 'cannot define a default UID if the targetField is set', function(value) {
1609
+ const { targetField } = this.parent;
1610
+ return !!(_.isNil(targetField) || _.isNil(value));
1611
+ }).test(isValidUID),
1612
+ minLength: validators.minLength,
1613
+ maxLength: validators.maxLength.max(256).test(maxLengthIsGreaterThanOrEqualToMinLength),
1614
+ options: utils.yup.object().shape({
1615
+ separator: utils.yup.string(),
1616
+ lowercase: utils.yup.boolean(),
1617
+ decamelize: utils.yup.boolean(),
1618
+ customReplacements: utils.yup.array().of(utils.yup.array().of(utils.yup.string()).min(2).max(2)),
1619
+ preserveLeadingUnderscore: utils.yup.boolean()
1620
+ })
1621
+ };
1622
+ }
1623
+ /**
1624
+ * scalar types
1625
+ */ case 'string':
1626
+ case 'text':
1627
+ {
1628
+ return {
1629
+ default: utils.yup.string(),
1630
+ required: validators.required,
1631
+ unique: validators.unique,
1632
+ minLength: validators.minLength,
1633
+ maxLength: validators.maxLength,
1634
+ regex: utils.yup.string().test(isValidRegExpPattern)
1635
+ };
1636
+ }
1637
+ case 'richtext':
1638
+ {
1639
+ return {
1640
+ default: utils.yup.string(),
1641
+ required: validators.required,
1642
+ minLength: validators.minLength,
1643
+ maxLength: validators.maxLength
1644
+ };
1645
+ }
1646
+ case 'blocks':
1647
+ {
1648
+ return {
1649
+ required: validators.required
1650
+ };
1651
+ }
1652
+ case 'json':
1653
+ {
1654
+ return {
1655
+ default: utils.yup.mixed().test(isValidDefaultJSON),
1656
+ required: validators.required
1657
+ };
1658
+ }
1659
+ case 'enumeration':
1660
+ {
1661
+ return {
1662
+ enum: utils.yup.array().of(utils.yup.string().test(isValidEnum).required()).min(1).test(areEnumValuesUnique).required(),
1663
+ default: utils.yup.string().when('enum', (enumVal)=>utils.yup.string().oneOf(enumVal)),
1664
+ enumName: utils.yup.string().test(isValidName),
1665
+ required: validators.required
1666
+ };
1667
+ }
1668
+ case 'password':
1669
+ {
1670
+ return {
1671
+ required: validators.required,
1672
+ minLength: validators.minLength,
1673
+ maxLength: validators.maxLength
1674
+ };
1675
+ }
1676
+ case 'email':
1677
+ {
1678
+ return {
1679
+ default: utils.yup.string().email(),
1680
+ required: validators.required,
1681
+ unique: validators.unique,
1682
+ minLength: validators.minLength,
1683
+ maxLength: validators.maxLength
1684
+ };
1685
+ }
1686
+ case 'integer':
1687
+ {
1688
+ return {
1689
+ default: utils.yup.number().integer(),
1690
+ required: validators.required,
1691
+ unique: validators.unique,
1692
+ min: utils.yup.number().integer(),
1693
+ max: utils.yup.number().integer()
1694
+ };
1695
+ }
1696
+ case 'biginteger':
1697
+ {
1698
+ return {
1699
+ default: utils.yup.string().nullable().matches(/^\d*$/),
1700
+ required: validators.required,
1701
+ unique: validators.unique,
1702
+ min: utils.yup.string().nullable().matches(/^\d*$/),
1703
+ max: utils.yup.string().nullable().matches(/^\d*$/)
1704
+ };
1705
+ }
1706
+ case 'float':
1707
+ {
1708
+ return {
1709
+ default: utils.yup.number(),
1710
+ required: validators.required,
1711
+ unique: validators.unique,
1712
+ min: utils.yup.number(),
1713
+ max: utils.yup.number()
1714
+ };
1715
+ }
1716
+ case 'decimal':
1717
+ {
1718
+ return {
1719
+ default: utils.yup.number(),
1720
+ required: validators.required,
1721
+ unique: validators.unique,
1722
+ min: utils.yup.number(),
1723
+ max: utils.yup.number()
1724
+ };
1725
+ }
1726
+ case 'time':
1727
+ case 'datetime':
1728
+ case 'date':
1729
+ {
1730
+ return {
1731
+ default: utils.yup.string(),
1732
+ required: validators.required,
1733
+ unique: validators.unique
1734
+ };
1735
+ }
1736
+ case 'boolean':
1737
+ {
1738
+ return {
1739
+ default: utils.yup.boolean(),
1740
+ required: validators.required
1741
+ };
1742
+ }
1743
+ case 'component':
1744
+ {
1745
+ return {
1746
+ required: validators.required,
1747
+ repeatable: utils.yup.boolean(),
1748
+ // TODO: Add correct server validation for nested components
1749
+ component: utils.yup.string().required(),
1750
+ min: utils.yup.number(),
1751
+ max: utils.yup.number()
1752
+ };
1753
+ }
1754
+ case 'dynamiczone':
1755
+ {
1756
+ return {
1757
+ required: validators.required,
1758
+ components: utils.yup.array().of(utils.yup.string().required()).test('isArray', '${path} must be an array', (value)=>Array.isArray(value)).min(1),
1759
+ min: utils.yup.number(),
1760
+ max: utils.yup.number()
1761
+ };
1762
+ }
1763
+ default:
1764
+ {
1765
+ return {};
1766
+ }
1638
1767
  }
1639
- }
1640
1768
  };
1641
- const createSchema = (types, relations, { modelType } = {}) => {
1642
- const shape = {
1643
- description: utils.yup.string(),
1644
- options: utils.yup.object(),
1645
- pluginOptions: utils.yup.object(),
1646
- collectionName: utils.yup.string().nullable().test(isValidCollectionName),
1647
- attributes: createAttributesValidator({ types, relations, modelType }),
1648
- draftAndPublish: utils.yup.boolean()
1649
- };
1650
- if (modelType === modelTypes.CONTENT_TYPE) {
1651
- shape.kind = utils.yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();
1652
- }
1653
- return utils.yup.object(shape).noUnknown();
1654
- };
1655
- const createAttributesValidator = ({ types, modelType, relations }) => {
1656
- return utils.yup.lazy((attributes) => {
1657
- return utils.yup.object().shape(
1658
- ___default.default.mapValues(attributes, (attribute, key) => {
1659
- if (isForbiddenKey(key)) {
1660
- return forbiddenValidator();
1661
- }
1662
- if (isConflictingKey(key, attributes)) {
1663
- return conflictingKeysValidator(key);
1664
- }
1665
- if (attribute.type === "relation") {
1666
- return getRelationValidator(attribute, relations).test(isValidKey(key));
1769
+
1770
+ const STRAPI_USER_RELATIONS = [
1771
+ 'oneToOne',
1772
+ 'oneToMany'
1773
+ ];
1774
+ const isValidRelation = (validNatures)=>function(value) {
1775
+ // NOTE: In case of an undefined value, delegate the check to .required()
1776
+ if (value === undefined) {
1777
+ return true;
1667
1778
  }
1668
- if (___default.default.has(attribute, "type")) {
1669
- return getTypeValidator(attribute, { types, modelType, attributes }).test(
1670
- isValidKey(key)
1671
- );
1779
+ if (this.parent.target === coreUids.STRAPI_USER) {
1780
+ if (!validNatures.includes(value) || !fp.isUndefined(this.parent.targetAttribute)) {
1781
+ return this.createError({
1782
+ path: this.path,
1783
+ message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`
1784
+ });
1785
+ }
1672
1786
  }
1673
- return typeOrRelationValidator;
1674
- })
1675
- ).required("attributes.required");
1676
- });
1677
- };
1678
- const isConflictingKey = (key, attributes) => {
1679
- const snakeCaseKey = fp.snakeCase(key);
1680
- return Object.keys(attributes).some((existingKey) => {
1681
- if (existingKey === key) return false;
1682
- return fp.snakeCase(existingKey) === snakeCaseKey;
1683
- });
1684
- };
1685
- const isForbiddenKey = (key) => {
1686
- return getService("builder").isReservedAttributeName(key);
1687
- };
1688
- const forbiddenValidator = () => {
1689
- const reservedNames = [...getService("builder").getReservedNames().attributes];
1690
- return utils.yup.mixed().test({
1691
- name: "forbiddenKeys",
1692
- message: `Attribute keys cannot be one of ${reservedNames.join(", ")}`,
1693
- test: () => false
1694
- });
1695
- };
1696
- const conflictingKeysValidator = (key) => {
1697
- return utils.yup.mixed().test({
1698
- name: "conflictingKeys",
1699
- message: `Attribute ${key} conflicts with an existing key`,
1700
- test: () => false
1701
- });
1787
+ return validNatures.includes(value) ? true : this.createError({
1788
+ path: this.path,
1789
+ message: `must be one of the following values: ${validNatures.join(', ')}`
1790
+ });
1791
+ };
1792
+ const getRelationValidator = (attribute, allowedRelations)=>{
1793
+ const contentTypesUIDs = Object.keys(strapi.contentTypes).filter((key)=>strapi.contentTypes[key].kind === typeKinds.COLLECTION_TYPE).filter((key)=>!key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER).concat([
1794
+ '__self__',
1795
+ '__contentType__'
1796
+ ]);
1797
+ const base = {
1798
+ type: utils.yup.string().oneOf([
1799
+ 'relation'
1800
+ ]).required(),
1801
+ relation: utils.yup.string().test('isValidRelation', isValidRelation(allowedRelations)).required(),
1802
+ configurable: utils.yup.boolean().nullable(),
1803
+ private: utils.yup.boolean().nullable(),
1804
+ pluginOptions: utils.yup.object()
1805
+ };
1806
+ switch(attribute.relation){
1807
+ case 'oneToOne':
1808
+ case 'oneToMany':
1809
+ case 'manyToOne':
1810
+ case 'manyToMany':
1811
+ case 'morphOne':
1812
+ case 'morphMany':
1813
+ {
1814
+ return utils.yup.object({
1815
+ ...base,
1816
+ target: utils.yup.string().oneOf(contentTypesUIDs).required(),
1817
+ targetAttribute: utils.yup.string().test(isValidName).nullable()
1818
+ });
1819
+ }
1820
+ case 'morphToOne':
1821
+ case 'morphToMany':
1822
+ default:
1823
+ {
1824
+ return utils.yup.object({
1825
+ ...base
1826
+ });
1827
+ }
1828
+ }
1829
+ };
1830
+
1831
+ const createSchema = (types, relations, { modelType } = {})=>{
1832
+ const shape = {
1833
+ description: utils.yup.string(),
1834
+ options: utils.yup.object(),
1835
+ pluginOptions: utils.yup.object(),
1836
+ collectionName: utils.yup.string().nullable().test(isValidCollectionName),
1837
+ attributes: createAttributesValidator({
1838
+ types,
1839
+ relations,
1840
+ modelType
1841
+ }),
1842
+ draftAndPublish: utils.yup.boolean()
1843
+ };
1844
+ if (modelType === modelTypes.CONTENT_TYPE) {
1845
+ shape.kind = utils.yup.string().oneOf([
1846
+ typeKinds.SINGLE_TYPE,
1847
+ typeKinds.COLLECTION_TYPE
1848
+ ]).nullable();
1849
+ }
1850
+ return utils.yup.object(shape).noUnknown();
1851
+ };
1852
+ const createAttributesValidator = ({ types, modelType, relations })=>{
1853
+ return utils.yup.lazy((attributes)=>{
1854
+ return utils.yup.object().shape(_.mapValues(attributes, (attribute, key)=>{
1855
+ if (isForbiddenKey(key)) {
1856
+ return forbiddenValidator();
1857
+ }
1858
+ if (isConflictingKey(key, attributes)) {
1859
+ return conflictingKeysValidator(key);
1860
+ }
1861
+ if (attribute.type === 'relation') {
1862
+ return getRelationValidator(attribute, relations).test(isValidKey(key));
1863
+ }
1864
+ if (_.has(attribute, 'type')) {
1865
+ return getTypeValidator(attribute, {
1866
+ types,
1867
+ modelType,
1868
+ attributes
1869
+ }).test(isValidKey(key));
1870
+ }
1871
+ return typeOrRelationValidator;
1872
+ })).required('attributes.required');
1873
+ });
1874
+ };
1875
+ const isConflictingKey = (key, attributes)=>{
1876
+ const snakeCaseKey = fp.snakeCase(key);
1877
+ return Object.keys(attributes).some((existingKey)=>{
1878
+ if (existingKey === key) return false; // don't compare against itself
1879
+ return fp.snakeCase(existingKey) === snakeCaseKey;
1880
+ });
1881
+ };
1882
+ const isForbiddenKey = (key)=>{
1883
+ return getService('builder').isReservedAttributeName(key);
1884
+ };
1885
+ const forbiddenValidator = ()=>{
1886
+ const reservedNames = [
1887
+ ...getService('builder').getReservedNames().attributes
1888
+ ];
1889
+ return utils.yup.mixed().test({
1890
+ name: 'forbiddenKeys',
1891
+ message: `Attribute keys cannot be one of ${reservedNames.join(', ')}`,
1892
+ test: ()=>false
1893
+ });
1894
+ };
1895
+ const conflictingKeysValidator = (key)=>{
1896
+ return utils.yup.mixed().test({
1897
+ name: 'conflictingKeys',
1898
+ message: `Attribute ${key} conflicts with an existing key`,
1899
+ test: ()=>false
1900
+ });
1702
1901
  };
1703
1902
  const typeOrRelationValidator = utils.yup.object().test({
1704
- name: "mustHaveTypeOrTarget",
1705
- message: "Attribute must have either a type or a target",
1706
- test: () => false
1903
+ name: 'mustHaveTypeOrTarget',
1904
+ message: 'Attribute must have either a type or a target',
1905
+ test: ()=>false
1707
1906
  });
1708
- const hasDefaultAttribute = (attribute) => {
1709
- return "default" in attribute;
1710
- };
1711
- const removeEmptyDefaults = (data) => {
1712
- const { attributes } = data || {};
1713
- Object.keys(attributes).forEach((attributeName) => {
1714
- const attribute = attributes[attributeName];
1715
- if (hasDefaultAttribute(attribute) && attribute.default === "") {
1716
- attribute.default = void 0;
1717
- }
1718
- });
1719
- };
1720
- const removeDeletedUIDTargetFields = (data) => {
1721
- if (___default.default.has(data, "attributes")) {
1722
- Object.values(data.attributes).forEach((attribute) => {
1723
- if (attribute.type === "uid" && !___default.default.isUndefined(attribute.targetField) && !___default.default.has(data.attributes, attribute.targetField)) {
1724
- attribute.targetField = void 0;
1725
- }
1907
+
1908
+ const hasDefaultAttribute = (attribute)=>{
1909
+ return 'default' in attribute;
1910
+ };
1911
+
1912
+ const removeEmptyDefaults = (data)=>{
1913
+ const { attributes } = data || {};
1914
+ Object.keys(attributes).forEach((attributeName)=>{
1915
+ const attribute = attributes[attributeName];
1916
+ if (hasDefaultAttribute(attribute) && attribute.default === '') {
1917
+ attribute.default = undefined;
1918
+ }
1726
1919
  });
1727
- }
1728
1920
  };
1729
- const VALID_RELATIONS$1 = ["oneToOne", "oneToMany"];
1730
- const VALID_TYPES$1 = [...DEFAULT_TYPES, "component", "customField"];
1921
+ const removeDeletedUIDTargetFields = (data)=>{
1922
+ if (_.has(data, 'attributes')) {
1923
+ Object.values(data.attributes).forEach((attribute)=>{
1924
+ if (attribute.type === 'uid' && !_.isUndefined(attribute.targetField) && !_.has(data.attributes, attribute.targetField)) {
1925
+ attribute.targetField = undefined;
1926
+ }
1927
+ });
1928
+ }
1929
+ };
1930
+
1931
+ const VALID_RELATIONS$1 = [
1932
+ 'oneToOne',
1933
+ 'oneToMany'
1934
+ ];
1935
+ const VALID_TYPES$1 = [
1936
+ ...DEFAULT_TYPES,
1937
+ 'component',
1938
+ 'customField'
1939
+ ];
1731
1940
  const componentSchema = createSchema(VALID_TYPES$1, VALID_RELATIONS$1, {
1732
- modelType: modelTypes.COMPONENT
1941
+ modelType: modelTypes.COMPONENT
1733
1942
  }).shape({
1734
- displayName: utils.yup.string().min(1).required("displayName.required"),
1735
- icon: utils.yup.string().nullable().test(isValidIcon),
1736
- category: utils.yup.string().nullable().test(isValidCategoryName).required("category.required")
1943
+ displayName: utils.yup.string().min(1).required('displayName.required'),
1944
+ icon: utils.yup.string().nullable().test(isValidIcon),
1945
+ category: utils.yup.string().nullable().test(isValidCategoryName).required('category.required')
1737
1946
  }).required().noUnknown();
1738
- const nestedComponentSchema = utils.yup.array().of(
1739
- componentSchema.shape({
1947
+ const nestedComponentSchema = utils.yup.array().of(componentSchema.shape({
1740
1948
  uid: utils.yup.string(),
1741
1949
  tmpUID: utils.yup.string()
1742
- }).test({
1743
- name: "mustHaveUIDOrTmpUID",
1744
- message: "Component must have a uid or a tmpUID",
1745
- test(attr) {
1746
- if (___default.default.has(attr, "uid") && ___default.default.has(attr, "tmpUID")) return false;
1747
- if (!___default.default.has(attr, "uid") && !___default.default.has(attr, "tmpUID")) return false;
1748
- return true;
1950
+ }).test({
1951
+ name: 'mustHaveUIDOrTmpUID',
1952
+ message: 'Component must have a uid or a tmpUID',
1953
+ test (attr) {
1954
+ if (_.has(attr, 'uid') && _.has(attr, 'tmpUID')) return false;
1955
+ if (!_.has(attr, 'uid') && !_.has(attr, 'tmpUID')) return false;
1956
+ return true;
1749
1957
  }
1750
- }).required().noUnknown()
1751
- );
1958
+ }).required().noUnknown());
1752
1959
  const componentInputSchema = utils.yup.object({
1753
- component: componentSchema,
1754
- components: nestedComponentSchema
1960
+ component: componentSchema,
1961
+ components: nestedComponentSchema
1755
1962
  }).noUnknown();
1756
1963
  const validateComponentInput = utils.validateYupSchema(componentInputSchema);
1757
1964
  const updateComponentInputSchema = utils.yup.object({
1758
- component: componentSchema,
1759
- components: nestedComponentSchema
1965
+ component: componentSchema,
1966
+ components: nestedComponentSchema
1760
1967
  }).noUnknown();
1761
- const validateUpdateComponentInput = (data) => {
1762
- if (___default.default.has(data, "component") && data.component) {
1763
- removeEmptyDefaults(data.component);
1764
- }
1765
- if (___default.default.has(data, "components") && Array.isArray(data.components)) {
1766
- data.components.forEach((data2) => {
1767
- if (___default.default.has(data2, "uid")) {
1768
- removeEmptyDefaults(data2);
1769
- }
1770
- });
1771
- }
1772
- return utils.validateYupSchema(updateComponentInputSchema)(data);
1968
+ const validateUpdateComponentInput = (data)=>{
1969
+ if (_.has(data, 'component') && data.component) {
1970
+ removeEmptyDefaults(data.component);
1971
+ }
1972
+ if (_.has(data, 'components') && Array.isArray(data.components)) {
1973
+ data.components.forEach((data)=>{
1974
+ if (_.has(data, 'uid')) {
1975
+ removeEmptyDefaults(data);
1976
+ }
1977
+ });
1978
+ }
1979
+ return utils.validateYupSchema(updateComponentInputSchema)(data);
1773
1980
  };
1774
- const components = {
1775
- /**
1981
+
1982
+ /**
1983
+ * Components controller
1984
+ */ var components = {
1985
+ /**
1776
1986
  * GET /components handler
1777
1987
  * Returns a list of available components
1778
1988
  * @param {Object} ctx - koa context
1779
- */
1780
- async getComponents(ctx) {
1781
- const componentService = getService("components");
1782
- const componentUIDs = Object.keys(strapi.components);
1783
- const data = componentUIDs.map((uid) => {
1784
- return componentService.formatComponent(strapi.components[uid]);
1785
- });
1786
- ctx.send({ data });
1787
- },
1788
- /**
1989
+ */ async getComponents (ctx) {
1990
+ const componentService = getService('components');
1991
+ const componentUIDs = Object.keys(strapi.components);
1992
+ const data = componentUIDs.map((uid)=>{
1993
+ return componentService.formatComponent(strapi.components[uid]);
1994
+ });
1995
+ ctx.send({
1996
+ data
1997
+ });
1998
+ },
1999
+ /**
1789
2000
  * GET /components/:uid
1790
2001
  * Returns a specific component
1791
2002
  * @param {Object} ctx - koa context
1792
- */
1793
- async getComponent(ctx) {
1794
- const { uid } = ctx.params;
1795
- const component = strapi.components[uid];
1796
- if (!component) {
1797
- return ctx.send({ error: "component.notFound" }, 404);
1798
- }
1799
- const componentService = getService("components");
1800
- ctx.send({ data: componentService.formatComponent(component) });
1801
- },
1802
- /**
2003
+ */ async getComponent (ctx) {
2004
+ const { uid } = ctx.params;
2005
+ const component = strapi.components[uid];
2006
+ if (!component) {
2007
+ return ctx.send({
2008
+ error: 'component.notFound'
2009
+ }, 404);
2010
+ }
2011
+ const componentService = getService('components');
2012
+ ctx.send({
2013
+ data: componentService.formatComponent(component)
2014
+ });
2015
+ },
2016
+ /**
1803
2017
  * POST /components
1804
2018
  * Creates a component and returns its infos
1805
2019
  * @param {Object} ctx - koa context
1806
- */
1807
- async createComponent(ctx) {
1808
- const body = ctx.request.body;
1809
- try {
1810
- await validateComponentInput(body);
1811
- } catch (error) {
1812
- return ctx.send({ error }, 400);
1813
- }
1814
- try {
1815
- strapi.reload.isWatching = false;
1816
- const componentService = getService("components");
1817
- const component = await componentService.createComponent({
1818
- component: body.component,
1819
- components: body.components
1820
- });
1821
- setImmediate(() => strapi.reload());
1822
- ctx.send({ data: { uid: component.uid } }, 201);
1823
- } catch (error) {
1824
- strapi.log.error(error);
1825
- ctx.send({ error: error?.message || "Unknown error" }, 400);
1826
- }
1827
- },
1828
- /**
2020
+ */ async createComponent (ctx) {
2021
+ const body = ctx.request.body;
2022
+ try {
2023
+ await validateComponentInput(body);
2024
+ } catch (error) {
2025
+ return ctx.send({
2026
+ error
2027
+ }, 400);
2028
+ }
2029
+ try {
2030
+ strapi.reload.isWatching = false;
2031
+ const componentService = getService('components');
2032
+ const component = await componentService.createComponent({
2033
+ component: body.component,
2034
+ components: body.components
2035
+ });
2036
+ setImmediate(()=>strapi.reload());
2037
+ ctx.send({
2038
+ data: {
2039
+ uid: component.uid
2040
+ }
2041
+ }, 201);
2042
+ } catch (error) {
2043
+ strapi.log.error(error);
2044
+ ctx.send({
2045
+ error: error?.message || 'Unknown error'
2046
+ }, 400);
2047
+ }
2048
+ },
2049
+ /**
1829
2050
  * PUT /components/:uid
1830
2051
  * Updates a component and return its infos
1831
2052
  * @param {Object} ctx - koa context - enhanced koa context
1832
- */
1833
- async updateComponent(ctx) {
1834
- const { uid } = ctx.params;
1835
- const body = ctx.request.body;
1836
- if (!___default.default.has(strapi.components, uid)) {
1837
- return ctx.send({ error: "component.notFound" }, 404);
1838
- }
1839
- try {
1840
- await validateUpdateComponentInput(body);
1841
- } catch (error) {
1842
- return ctx.send({ error }, 400);
1843
- }
1844
- try {
1845
- strapi.reload.isWatching = false;
1846
- const componentService = getService("components");
1847
- const component = await componentService.editComponent(uid, {
1848
- component: body.component,
1849
- components: body.components
1850
- });
1851
- setImmediate(() => strapi.reload());
1852
- ctx.send({ data: { uid: component.uid } });
1853
- } catch (error) {
1854
- strapi.log.error(error);
1855
- ctx.send({ error: error?.message || "Unknown error" }, 400);
1856
- }
1857
- },
1858
- /**
2053
+ */ async updateComponent (ctx) {
2054
+ const { uid } = ctx.params;
2055
+ const body = ctx.request.body;
2056
+ if (!_.has(strapi.components, uid)) {
2057
+ return ctx.send({
2058
+ error: 'component.notFound'
2059
+ }, 404);
2060
+ }
2061
+ try {
2062
+ await validateUpdateComponentInput(body);
2063
+ } catch (error) {
2064
+ return ctx.send({
2065
+ error
2066
+ }, 400);
2067
+ }
2068
+ try {
2069
+ strapi.reload.isWatching = false;
2070
+ const componentService = getService('components');
2071
+ const component = await componentService.editComponent(uid, {
2072
+ component: body.component,
2073
+ components: body.components
2074
+ });
2075
+ setImmediate(()=>strapi.reload());
2076
+ ctx.send({
2077
+ data: {
2078
+ uid: component.uid
2079
+ }
2080
+ });
2081
+ } catch (error) {
2082
+ strapi.log.error(error);
2083
+ ctx.send({
2084
+ error: error?.message || 'Unknown error'
2085
+ }, 400);
2086
+ }
2087
+ },
2088
+ /**
1859
2089
  * DELETE /components/:uid
1860
2090
  * Deletes a components and returns its old infos
1861
2091
  * @param {Object} ctx - koa context
1862
- */
1863
- async deleteComponent(ctx) {
1864
- const { uid } = ctx.params;
1865
- if (!___default.default.has(strapi.components, uid)) {
1866
- return ctx.send({ error: "component.notFound" }, 404);
2092
+ */ async deleteComponent (ctx) {
2093
+ const { uid } = ctx.params;
2094
+ if (!_.has(strapi.components, uid)) {
2095
+ return ctx.send({
2096
+ error: 'component.notFound'
2097
+ }, 404);
2098
+ }
2099
+ try {
2100
+ strapi.reload.isWatching = false;
2101
+ const componentService = getService('components');
2102
+ const component = await componentService.deleteComponent(uid);
2103
+ setImmediate(()=>strapi.reload());
2104
+ ctx.send({
2105
+ data: {
2106
+ uid: component.uid
2107
+ }
2108
+ });
2109
+ } catch (error) {
2110
+ strapi.log.error(error);
2111
+ ctx.send({
2112
+ error: error?.message || 'Unknown error'
2113
+ }, 400);
2114
+ }
1867
2115
  }
1868
- try {
1869
- strapi.reload.isWatching = false;
1870
- const componentService = getService("components");
1871
- const component = await componentService.deleteComponent(uid);
1872
- setImmediate(() => strapi.reload());
1873
- ctx.send({ data: { uid: component.uid } });
1874
- } catch (error) {
1875
- strapi.log.error(error);
1876
- ctx.send({ error: error?.message || "Unknown error" }, 400);
2116
+ };
2117
+
2118
+ /* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format
2119
+ /**
2120
+ * Allowed relation per type kind
2121
+ */ const VALID_RELATIONS = {
2122
+ [typeKinds.SINGLE_TYPE]: [
2123
+ 'oneToOne',
2124
+ 'oneToMany',
2125
+ 'morphOne',
2126
+ 'morphMany',
2127
+ 'morphToOne',
2128
+ 'morphToMany'
2129
+ ],
2130
+ [typeKinds.COLLECTION_TYPE]: [
2131
+ 'oneToOne',
2132
+ 'oneToMany',
2133
+ 'manyToOne',
2134
+ 'manyToMany',
2135
+ 'morphOne',
2136
+ 'morphMany',
2137
+ 'morphToOne',
2138
+ 'morphToMany'
2139
+ ]
2140
+ };
2141
+ /**
2142
+ * Allowed types
2143
+ */ const VALID_TYPES = [
2144
+ ...DEFAULT_TYPES,
2145
+ 'uid',
2146
+ 'component',
2147
+ 'dynamiczone',
2148
+ 'customField'
2149
+ ];
2150
+ /**
2151
+ * Returns a yup schema to validate a content type payload
2152
+ */ const createContentTypeSchema = (data, { isEdition = false } = {})=>{
2153
+ const kind = fp.getOr(typeKinds.COLLECTION_TYPE, 'contentType.kind', data);
2154
+ const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
2155
+ modelType: modelTypes.CONTENT_TYPE
2156
+ }).shape({
2157
+ displayName: utils.yup.string().min(1).required(),
2158
+ singularName: utils.yup.string().min(1).test(nameIsAvailable(isEdition)).test(forbiddenContentTypeNameValidator()).isKebabCase().required(),
2159
+ pluralName: utils.yup.string().min(1).test(nameIsAvailable(isEdition)).test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name
2160
+ .test(forbiddenContentTypeNameValidator()).isKebabCase().required()
2161
+ }).test('singularName-not-equal-pluralName', '${path}: singularName and pluralName should be different', (value)=>value.singularName !== value.pluralName);
2162
+ return utils.yup.object({
2163
+ // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error
2164
+ // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features
2165
+ // without having any reference to them in CE.
2166
+ // Why not handle an "options" object in the content-type ? The admin panel needs lots of rework
2167
+ // to be able to send this options object instead of top-level attributes.
2168
+ // @nathan-pichon 20/02/2023
2169
+ contentType: contentTypeSchema.required().noUnknown(false),
2170
+ components: nestedComponentSchema
2171
+ }).noUnknown();
2172
+ };
2173
+ /**
2174
+ * Validator for content type creation
2175
+ */ const validateContentTypeInput = (data)=>{
2176
+ return utils.validateYupSchema(createContentTypeSchema(data))(data);
2177
+ };
2178
+ /**
2179
+ * Validator for content type edition
2180
+ */ const validateUpdateContentTypeInput = (data)=>{
2181
+ if (fp.has('contentType', data)) {
2182
+ removeEmptyDefaults(data.contentType);
2183
+ removeDeletedUIDTargetFields(data.contentType);
2184
+ }
2185
+ if (fp.has('components', data) && Array.isArray(data.components)) {
2186
+ data.components.forEach((comp)=>{
2187
+ if (fp.has('uid', comp)) {
2188
+ removeEmptyDefaults(comp);
2189
+ }
2190
+ });
1877
2191
  }
1878
- }
2192
+ return utils.validateYupSchema(createContentTypeSchema(data, {
2193
+ isEdition: true
2194
+ }))(data);
1879
2195
  };
1880
- const VALID_RELATIONS = {
1881
- [typeKinds.SINGLE_TYPE]: [
1882
- "oneToOne",
1883
- "oneToMany",
1884
- "morphOne",
1885
- "morphMany",
1886
- "morphToOne",
1887
- "morphToMany"
1888
- ],
1889
- [typeKinds.COLLECTION_TYPE]: [
1890
- "oneToOne",
1891
- "oneToMany",
1892
- "manyToOne",
1893
- "manyToMany",
1894
- "morphOne",
1895
- "morphMany",
1896
- "morphToOne",
1897
- "morphToMany"
1898
- ]
1899
- };
1900
- const VALID_TYPES = [...DEFAULT_TYPES, "uid", "component", "dynamiczone", "customField"];
1901
- const createContentTypeSchema = (data, { isEdition = false } = {}) => {
1902
- const kind = fp.getOr(
1903
- typeKinds.COLLECTION_TYPE,
1904
- "contentType.kind",
1905
- data
1906
- );
1907
- const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
1908
- modelType: modelTypes.CONTENT_TYPE
1909
- }).shape({
1910
- displayName: utils.yup.string().min(1).required(),
1911
- singularName: utils.yup.string().min(1).test(nameIsAvailable(isEdition)).test(forbiddenContentTypeNameValidator()).isKebabCase().required(),
1912
- pluralName: utils.yup.string().min(1).test(nameIsAvailable(isEdition)).test(nameIsNotExistingCollectionName(isEdition)).test(forbiddenContentTypeNameValidator()).isKebabCase().required()
1913
- }).test(
1914
- "singularName-not-equal-pluralName",
1915
- "${path}: singularName and pluralName should be different",
1916
- (value) => value.singularName !== value.pluralName
1917
- );
1918
- return utils.yup.object({
1919
- // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error
1920
- // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features
1921
- // without having any reference to them in CE.
1922
- // Why not handle an "options" object in the content-type ? The admin panel needs lots of rework
1923
- // to be able to send this options object instead of top-level attributes.
1924
- // @nathan-pichon 20/02/2023
1925
- contentType: contentTypeSchema.required().noUnknown(false),
1926
- components: nestedComponentSchema
1927
- }).noUnknown();
2196
+ const forbiddenContentTypeNameValidator = ()=>{
2197
+ const reservedNames = getService('builder').getReservedNames().models;
2198
+ return {
2199
+ name: 'forbiddenContentTypeName',
2200
+ message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,
2201
+ test (value) {
2202
+ if (typeof value !== 'string') {
2203
+ return true;
2204
+ }
2205
+ return !getService('builder').isReservedModelName(value);
2206
+ }
2207
+ };
1928
2208
  };
1929
- const validateContentTypeInput = (data) => {
1930
- return utils.validateYupSchema(createContentTypeSchema(data))(data);
2209
+ const nameIsAvailable = (isEdition)=>{
2210
+ // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as
2211
+ const usedNames = fp.flatMap((ct)=>{
2212
+ return [
2213
+ ct.info?.singularName,
2214
+ ct.info?.pluralName
2215
+ ];
2216
+ })(strapi.contentTypes);
2217
+ return {
2218
+ name: 'nameAlreadyUsed',
2219
+ message: 'contentType: name `${value}` is already being used by another content type.',
2220
+ test (value) {
2221
+ // don't check on edition
2222
+ if (isEdition) return true;
2223
+ // ignore if not a string (will be caught in another validator)
2224
+ if (typeof value !== 'string') {
2225
+ return true;
2226
+ }
2227
+ // compare snake case to check the actual column names that will be used in the database
2228
+ return usedNames.every((usedName)=>fp.snakeCase(usedName) !== fp.snakeCase(value));
2229
+ }
2230
+ };
1931
2231
  };
1932
- const validateUpdateContentTypeInput = (data) => {
1933
- if (fp.has("contentType", data)) {
1934
- removeEmptyDefaults(data.contentType);
1935
- removeDeletedUIDTargetFields(data.contentType);
1936
- }
1937
- if (fp.has("components", data) && Array.isArray(data.components)) {
1938
- data.components.forEach((comp) => {
1939
- if (fp.has("uid", comp)) {
1940
- removeEmptyDefaults(comp);
1941
- }
1942
- });
1943
- }
1944
- return utils.validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);
1945
- };
1946
- const forbiddenContentTypeNameValidator = () => {
1947
- const reservedNames = getService("builder").getReservedNames().models;
1948
- return {
1949
- name: "forbiddenContentTypeName",
1950
- message: `Content Type name cannot be one of ${reservedNames.join(", ")}`,
1951
- test(value) {
1952
- if (typeof value !== "string") {
1953
- return true;
1954
- }
1955
- return !getService("builder").isReservedModelName(value);
1956
- }
1957
- };
1958
- };
1959
- const nameIsAvailable = (isEdition) => {
1960
- const usedNames = fp.flatMap((ct) => {
1961
- return [ct.info?.singularName, ct.info?.pluralName];
1962
- })(strapi.contentTypes);
1963
- return {
1964
- name: "nameAlreadyUsed",
1965
- message: "contentType: name `${value}` is already being used by another content type.",
1966
- test(value) {
1967
- if (isEdition) return true;
1968
- if (typeof value !== "string") {
1969
- return true;
1970
- }
1971
- return usedNames.every((usedName) => fp.snakeCase(usedName) !== fp.snakeCase(value));
1972
- }
1973
- };
1974
- };
1975
- const nameIsNotExistingCollectionName = (isEdition) => {
1976
- const usedNames = Object.keys(strapi.contentTypes).map(
1977
- (key) => strapi.contentTypes[key].collectionName
1978
- );
1979
- return {
1980
- name: "nameAlreadyUsed",
1981
- message: "contentType: name `${value}` is already being used by another content type.",
1982
- test(value) {
1983
- if (isEdition) return true;
1984
- if (typeof value !== "string") {
1985
- return true;
1986
- }
1987
- return usedNames.every((usedName) => fp.snakeCase(usedName) !== fp.snakeCase(value));
1988
- }
1989
- };
2232
+ const nameIsNotExistingCollectionName = (isEdition)=>{
2233
+ const usedNames = Object.keys(strapi.contentTypes).map((key)=>strapi.contentTypes[key].collectionName);
2234
+ return {
2235
+ name: 'nameAlreadyUsed',
2236
+ message: 'contentType: name `${value}` is already being used by another content type.',
2237
+ test (value) {
2238
+ // don't check on edition
2239
+ if (isEdition) return true;
2240
+ // ignore if not a string (will be caught in another validator)
2241
+ if (typeof value !== 'string') {
2242
+ return true;
2243
+ }
2244
+ // compare snake case to check the actual column names that will be used in the database
2245
+ return usedNames.every((usedName)=>fp.snakeCase(usedName) !== fp.snakeCase(value));
2246
+ }
2247
+ };
1990
2248
  };
1991
- const kindSchema = utils.yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);
2249
+ /**
2250
+ * Validates type kind
2251
+ */ const kindSchema = utils.yup.string().oneOf([
2252
+ typeKinds.SINGLE_TYPE,
2253
+ typeKinds.COLLECTION_TYPE
2254
+ ]);
1992
2255
  const validateKind = utils.validateYupSchema(kindSchema);
1993
- const contentTypes = {
1994
- async getContentTypes(ctx) {
1995
- const { kind } = ctx.query;
1996
- try {
1997
- await validateKind(kind);
1998
- } catch (error) {
1999
- return ctx.send({ error }, 400);
2000
- }
2001
- const contentTypeService = getService("content-types");
2002
- const contentTypes2 = Object.keys(strapi.contentTypes).filter(
2003
- (uid) => !kind || ___default.default.get(strapi.contentTypes[uid], "kind", "collectionType") === kind
2004
- ).map(
2005
- (uid) => contentTypeService.formatContentType(strapi.contentTypes[uid])
2006
- );
2007
- ctx.send({
2008
- data: contentTypes2
2009
- });
2010
- },
2011
- getContentType(ctx) {
2012
- const { uid } = ctx.params;
2013
- const contentType = strapi.contentTypes[uid];
2014
- if (!contentType) {
2015
- return ctx.send({ error: "contentType.notFound" }, 404);
2016
- }
2017
- const contentTypeService = getService("content-types");
2018
- ctx.send({ data: contentTypeService.formatContentType(contentType) });
2019
- },
2020
- async createContentType(ctx) {
2021
- const body = ctx.request.body;
2022
- try {
2023
- await validateContentTypeInput(body);
2024
- } catch (error) {
2025
- return ctx.send({ error }, 400);
2026
- }
2027
- try {
2028
- strapi.reload.isWatching = false;
2029
- const contentTypeService = getService("content-types");
2030
- const contentType = await contentTypeService.createContentType({
2031
- contentType: body.contentType,
2032
- components: body.components
2033
- });
2034
- const metricsPayload = {
2035
- eventProperties: {
2036
- kind: contentType.kind
2256
+
2257
+ var contentTypes = {
2258
+ async getContentTypes (ctx) {
2259
+ const { kind } = ctx.query;
2260
+ try {
2261
+ await validateKind(kind);
2262
+ } catch (error) {
2263
+ return ctx.send({
2264
+ error
2265
+ }, 400);
2037
2266
  }
2038
- };
2039
- if (___default.default.isEmpty(strapi.apis)) {
2040
- await strapi.telemetry.send("didCreateFirstContentType", metricsPayload);
2041
- } else {
2042
- await strapi.telemetry.send("didCreateContentType", metricsPayload);
2043
- }
2044
- setImmediate(() => strapi.reload());
2045
- ctx.send({ data: { uid: contentType.uid } }, 201);
2046
- } catch (err) {
2047
- strapi.log.error(err);
2048
- await strapi.telemetry.send("didNotCreateContentType", {
2049
- eventProperties: { error: err.message || err }
2050
- });
2051
- ctx.send({ error: err.message || "Unknown error" }, 400);
2052
- }
2053
- },
2054
- async updateContentType(ctx) {
2055
- const { uid } = ctx.params;
2056
- const body = ctx.request.body;
2057
- if (!___default.default.has(strapi.contentTypes, uid)) {
2058
- return ctx.send({ error: "contentType.notFound" }, 404);
2059
- }
2060
- try {
2061
- await validateUpdateContentTypeInput(body);
2062
- } catch (error) {
2063
- return ctx.send({ error }, 400);
2064
- }
2065
- try {
2066
- strapi.reload.isWatching = false;
2067
- const contentTypeService = getService("content-types");
2068
- const component = await contentTypeService.editContentType(uid, {
2069
- contentType: body.contentType,
2070
- components: body.components
2071
- });
2072
- setImmediate(() => strapi.reload());
2073
- ctx.send({ data: { uid: component.uid } }, 201);
2074
- } catch (error) {
2075
- strapi.log.error(error);
2076
- ctx.send({ error: error?.message || "Unknown error" }, 400);
2077
- }
2078
- },
2079
- async deleteContentType(ctx) {
2080
- const { uid } = ctx.params;
2081
- if (!___default.default.has(strapi.contentTypes, uid)) {
2082
- return ctx.send({ error: "contentType.notFound" }, 404);
2083
- }
2084
- try {
2085
- strapi.reload.isWatching = false;
2086
- const contentTypeService = getService("content-types");
2087
- const component = await contentTypeService.deleteContentType(uid);
2088
- setImmediate(() => strapi.reload());
2089
- ctx.send({ data: { uid: component.uid } });
2090
- } catch (error) {
2091
- strapi.log.error(error);
2092
- ctx.send({ error: error?.message || "Unknown error" }, 400);
2093
- }
2094
- }
2095
- };
2096
- const exportObject = {
2097
- builder,
2098
- "component-categories": componentCategories,
2099
- components,
2100
- "content-types": contentTypes
2101
- };
2102
- const admin = {
2103
- type: "admin",
2104
- routes: [
2105
- {
2106
- method: "GET",
2107
- path: "/reserved-names",
2108
- handler: "builder.getReservedNames",
2109
- config: {
2110
- policies: [
2111
- {
2112
- name: "admin::hasPermissions",
2113
- config: { actions: ["plugin::content-type-builder.read"] }
2114
- }
2115
- ]
2116
- }
2117
- },
2118
- {
2119
- method: "GET",
2120
- path: "/content-types",
2121
- handler: "content-types.getContentTypes",
2122
- config: {
2123
- policies: [
2124
- {
2125
- name: "admin::hasPermissions",
2126
- config: { actions: ["plugin::content-type-builder.read"] }
2127
- }
2128
- ]
2129
- }
2130
- },
2131
- {
2132
- method: "GET",
2133
- path: "/content-types/:uid",
2134
- handler: "content-types.getContentType",
2135
- config: {
2136
- policies: [
2137
- {
2138
- name: "admin::hasPermissions",
2139
- config: { actions: ["plugin::content-type-builder.read"] }
2140
- }
2141
- ]
2142
- }
2143
- },
2144
- {
2145
- method: "POST",
2146
- path: "/content-types",
2147
- handler: "content-types.createContentType",
2148
- config: {
2149
- policies: [
2150
- {
2151
- name: "admin::hasPermissions",
2152
- config: { actions: ["plugin::content-type-builder.read"] }
2153
- }
2154
- ]
2155
- }
2156
- },
2157
- {
2158
- method: "PUT",
2159
- path: "/content-types/:uid",
2160
- handler: "content-types.updateContentType",
2161
- config: {
2162
- policies: [
2163
- {
2164
- name: "admin::hasPermissions",
2165
- config: { actions: ["plugin::content-type-builder.read"] }
2166
- }
2167
- ]
2168
- }
2169
- },
2170
- {
2171
- method: "DELETE",
2172
- path: "/content-types/:uid",
2173
- handler: "content-types.deleteContentType",
2174
- config: {
2175
- policies: [
2176
- {
2177
- name: "admin::hasPermissions",
2178
- config: { actions: ["plugin::content-type-builder.read"] }
2179
- }
2180
- ]
2181
- }
2182
- },
2183
- {
2184
- method: "GET",
2185
- path: "/components",
2186
- handler: "components.getComponents",
2187
- config: {
2188
- policies: [
2189
- {
2190
- name: "admin::hasPermissions",
2191
- config: { actions: ["plugin::content-type-builder.read"] }
2192
- }
2193
- ]
2194
- }
2195
- },
2196
- {
2197
- method: "GET",
2198
- path: "/components/:uid",
2199
- handler: "components.getComponent",
2200
- config: {
2201
- policies: [
2202
- {
2203
- name: "admin::hasPermissions",
2204
- config: { actions: ["plugin::content-type-builder.read"] }
2205
- }
2206
- ]
2207
- }
2208
- },
2209
- {
2210
- method: "POST",
2211
- path: "/components",
2212
- handler: "components.createComponent",
2213
- config: {
2214
- policies: [
2215
- {
2216
- name: "admin::hasPermissions",
2217
- config: { actions: ["plugin::content-type-builder.read"] }
2218
- }
2219
- ]
2220
- }
2221
- },
2222
- {
2223
- method: "PUT",
2224
- path: "/components/:uid",
2225
- handler: "components.updateComponent",
2226
- config: {
2227
- policies: [
2228
- {
2229
- name: "admin::hasPermissions",
2230
- config: { actions: ["plugin::content-type-builder.read"] }
2231
- }
2232
- ]
2233
- }
2234
- },
2235
- {
2236
- method: "DELETE",
2237
- path: "/components/:uid",
2238
- handler: "components.deleteComponent",
2239
- config: {
2240
- policies: [
2241
- {
2242
- name: "admin::hasPermissions",
2243
- config: { actions: ["plugin::content-type-builder.read"] }
2244
- }
2245
- ]
2246
- }
2247
- },
2248
- {
2249
- method: "PUT",
2250
- path: "/component-categories/:name",
2251
- handler: "component-categories.editCategory",
2252
- config: {
2253
- policies: [
2254
- {
2255
- name: "admin::hasPermissions",
2256
- config: { actions: ["plugin::content-type-builder.read"] }
2257
- }
2258
- ]
2259
- }
2267
+ const contentTypeService = getService('content-types');
2268
+ const contentTypes = Object.keys(strapi.contentTypes).filter((uid)=>!kind || _.get(strapi.contentTypes[uid], 'kind', 'collectionType') === kind).map((uid)=>contentTypeService.formatContentType(strapi.contentTypes[uid]));
2269
+ ctx.send({
2270
+ data: contentTypes
2271
+ });
2260
2272
  },
2261
- {
2262
- method: "DELETE",
2263
- path: "/component-categories/:name",
2264
- handler: "component-categories.deleteCategory",
2265
- config: {
2266
- policies: [
2267
- {
2268
- name: "admin::hasPermissions",
2269
- config: { actions: ["plugin::content-type-builder.read"] }
2270
- }
2271
- ]
2272
- }
2273
- }
2274
- ]
2275
- };
2276
- const contentApi = {
2277
- type: "content-api",
2278
- routes: [
2279
- {
2280
- method: "GET",
2281
- path: "/content-types",
2282
- handler: "content-types.getContentTypes"
2273
+ getContentType (ctx) {
2274
+ const { uid } = ctx.params;
2275
+ const contentType = strapi.contentTypes[uid];
2276
+ if (!contentType) {
2277
+ return ctx.send({
2278
+ error: 'contentType.notFound'
2279
+ }, 404);
2280
+ }
2281
+ const contentTypeService = getService('content-types');
2282
+ ctx.send({
2283
+ data: contentTypeService.formatContentType(contentType)
2284
+ });
2283
2285
  },
2284
- {
2285
- method: "GET",
2286
- path: "/content-types/:uid",
2287
- handler: "content-types.getContentType"
2286
+ async createContentType (ctx) {
2287
+ const body = ctx.request.body;
2288
+ try {
2289
+ await validateContentTypeInput(body);
2290
+ } catch (error) {
2291
+ return ctx.send({
2292
+ error
2293
+ }, 400);
2294
+ }
2295
+ try {
2296
+ strapi.reload.isWatching = false;
2297
+ const contentTypeService = getService('content-types');
2298
+ const contentType = await contentTypeService.createContentType({
2299
+ contentType: body.contentType,
2300
+ components: body.components
2301
+ });
2302
+ const metricsPayload = {
2303
+ eventProperties: {
2304
+ kind: contentType.kind
2305
+ }
2306
+ };
2307
+ if (_.isEmpty(strapi.apis)) {
2308
+ await strapi.telemetry.send('didCreateFirstContentType', metricsPayload);
2309
+ } else {
2310
+ await strapi.telemetry.send('didCreateContentType', metricsPayload);
2311
+ }
2312
+ setImmediate(()=>strapi.reload());
2313
+ ctx.send({
2314
+ data: {
2315
+ uid: contentType.uid
2316
+ }
2317
+ }, 201);
2318
+ } catch (err) {
2319
+ strapi.log.error(err);
2320
+ await strapi.telemetry.send('didNotCreateContentType', {
2321
+ eventProperties: {
2322
+ error: err.message || err
2323
+ }
2324
+ });
2325
+ ctx.send({
2326
+ error: err.message || 'Unknown error'
2327
+ }, 400);
2328
+ }
2288
2329
  },
2289
- {
2290
- method: "GET",
2291
- path: "/components",
2292
- handler: "components.getComponents"
2330
+ async updateContentType (ctx) {
2331
+ const { uid } = ctx.params;
2332
+ const body = ctx.request.body;
2333
+ if (!_.has(strapi.contentTypes, uid)) {
2334
+ return ctx.send({
2335
+ error: 'contentType.notFound'
2336
+ }, 404);
2337
+ }
2338
+ try {
2339
+ await validateUpdateContentTypeInput(body);
2340
+ } catch (error) {
2341
+ return ctx.send({
2342
+ error
2343
+ }, 400);
2344
+ }
2345
+ try {
2346
+ strapi.reload.isWatching = false;
2347
+ const contentTypeService = getService('content-types');
2348
+ const component = await contentTypeService.editContentType(uid, {
2349
+ contentType: body.contentType,
2350
+ components: body.components
2351
+ });
2352
+ setImmediate(()=>strapi.reload());
2353
+ ctx.send({
2354
+ data: {
2355
+ uid: component.uid
2356
+ }
2357
+ }, 201);
2358
+ } catch (error) {
2359
+ strapi.log.error(error);
2360
+ ctx.send({
2361
+ error: error?.message || 'Unknown error'
2362
+ }, 400);
2363
+ }
2293
2364
  },
2294
- {
2295
- method: "GET",
2296
- path: "/components/:uid",
2297
- handler: "components.getComponent"
2365
+ async deleteContentType (ctx) {
2366
+ const { uid } = ctx.params;
2367
+ if (!_.has(strapi.contentTypes, uid)) {
2368
+ return ctx.send({
2369
+ error: 'contentType.notFound'
2370
+ }, 404);
2371
+ }
2372
+ try {
2373
+ strapi.reload.isWatching = false;
2374
+ const contentTypeService = getService('content-types');
2375
+ const component = await contentTypeService.deleteContentType(uid);
2376
+ setImmediate(()=>strapi.reload());
2377
+ ctx.send({
2378
+ data: {
2379
+ uid: component.uid
2380
+ }
2381
+ });
2382
+ } catch (error) {
2383
+ strapi.log.error(error);
2384
+ ctx.send({
2385
+ error: error?.message || 'Unknown error'
2386
+ }, 400);
2387
+ }
2298
2388
  }
2299
- ]
2300
- };
2301
- const routes = {
2302
- admin,
2303
- "content-api": contentApi
2304
- };
2305
- const index = () => ({
2306
- config,
2307
- bootstrap,
2308
- services,
2309
- controllers: exportObject,
2310
- routes
2311
- });
2389
+ };
2390
+
2391
+ const exportObject = {
2392
+ builder,
2393
+ 'component-categories': componentCategories,
2394
+ components,
2395
+ 'content-types': contentTypes
2396
+ };
2397
+
2398
+ var admin = {
2399
+ type: 'admin',
2400
+ routes: [
2401
+ {
2402
+ method: 'GET',
2403
+ path: '/reserved-names',
2404
+ handler: 'builder.getReservedNames',
2405
+ config: {
2406
+ policies: [
2407
+ {
2408
+ name: 'admin::hasPermissions',
2409
+ config: {
2410
+ actions: [
2411
+ 'plugin::content-type-builder.read'
2412
+ ]
2413
+ }
2414
+ }
2415
+ ]
2416
+ }
2417
+ },
2418
+ {
2419
+ method: 'GET',
2420
+ path: '/content-types',
2421
+ handler: 'content-types.getContentTypes',
2422
+ config: {
2423
+ policies: [
2424
+ {
2425
+ name: 'admin::hasPermissions',
2426
+ config: {
2427
+ actions: [
2428
+ 'plugin::content-type-builder.read'
2429
+ ]
2430
+ }
2431
+ }
2432
+ ]
2433
+ }
2434
+ },
2435
+ {
2436
+ method: 'GET',
2437
+ path: '/content-types/:uid',
2438
+ handler: 'content-types.getContentType',
2439
+ config: {
2440
+ policies: [
2441
+ {
2442
+ name: 'admin::hasPermissions',
2443
+ config: {
2444
+ actions: [
2445
+ 'plugin::content-type-builder.read'
2446
+ ]
2447
+ }
2448
+ }
2449
+ ]
2450
+ }
2451
+ },
2452
+ {
2453
+ method: 'POST',
2454
+ path: '/content-types',
2455
+ handler: 'content-types.createContentType',
2456
+ config: {
2457
+ policies: [
2458
+ {
2459
+ name: 'admin::hasPermissions',
2460
+ config: {
2461
+ actions: [
2462
+ 'plugin::content-type-builder.read'
2463
+ ]
2464
+ }
2465
+ }
2466
+ ]
2467
+ }
2468
+ },
2469
+ {
2470
+ method: 'PUT',
2471
+ path: '/content-types/:uid',
2472
+ handler: 'content-types.updateContentType',
2473
+ config: {
2474
+ policies: [
2475
+ {
2476
+ name: 'admin::hasPermissions',
2477
+ config: {
2478
+ actions: [
2479
+ 'plugin::content-type-builder.read'
2480
+ ]
2481
+ }
2482
+ }
2483
+ ]
2484
+ }
2485
+ },
2486
+ {
2487
+ method: 'DELETE',
2488
+ path: '/content-types/:uid',
2489
+ handler: 'content-types.deleteContentType',
2490
+ config: {
2491
+ policies: [
2492
+ {
2493
+ name: 'admin::hasPermissions',
2494
+ config: {
2495
+ actions: [
2496
+ 'plugin::content-type-builder.read'
2497
+ ]
2498
+ }
2499
+ }
2500
+ ]
2501
+ }
2502
+ },
2503
+ {
2504
+ method: 'GET',
2505
+ path: '/components',
2506
+ handler: 'components.getComponents',
2507
+ config: {
2508
+ policies: [
2509
+ {
2510
+ name: 'admin::hasPermissions',
2511
+ config: {
2512
+ actions: [
2513
+ 'plugin::content-type-builder.read'
2514
+ ]
2515
+ }
2516
+ }
2517
+ ]
2518
+ }
2519
+ },
2520
+ {
2521
+ method: 'GET',
2522
+ path: '/components/:uid',
2523
+ handler: 'components.getComponent',
2524
+ config: {
2525
+ policies: [
2526
+ {
2527
+ name: 'admin::hasPermissions',
2528
+ config: {
2529
+ actions: [
2530
+ 'plugin::content-type-builder.read'
2531
+ ]
2532
+ }
2533
+ }
2534
+ ]
2535
+ }
2536
+ },
2537
+ {
2538
+ method: 'POST',
2539
+ path: '/components',
2540
+ handler: 'components.createComponent',
2541
+ config: {
2542
+ policies: [
2543
+ {
2544
+ name: 'admin::hasPermissions',
2545
+ config: {
2546
+ actions: [
2547
+ 'plugin::content-type-builder.read'
2548
+ ]
2549
+ }
2550
+ }
2551
+ ]
2552
+ }
2553
+ },
2554
+ {
2555
+ method: 'PUT',
2556
+ path: '/components/:uid',
2557
+ handler: 'components.updateComponent',
2558
+ config: {
2559
+ policies: [
2560
+ {
2561
+ name: 'admin::hasPermissions',
2562
+ config: {
2563
+ actions: [
2564
+ 'plugin::content-type-builder.read'
2565
+ ]
2566
+ }
2567
+ }
2568
+ ]
2569
+ }
2570
+ },
2571
+ {
2572
+ method: 'DELETE',
2573
+ path: '/components/:uid',
2574
+ handler: 'components.deleteComponent',
2575
+ config: {
2576
+ policies: [
2577
+ {
2578
+ name: 'admin::hasPermissions',
2579
+ config: {
2580
+ actions: [
2581
+ 'plugin::content-type-builder.read'
2582
+ ]
2583
+ }
2584
+ }
2585
+ ]
2586
+ }
2587
+ },
2588
+ {
2589
+ method: 'PUT',
2590
+ path: '/component-categories/:name',
2591
+ handler: 'component-categories.editCategory',
2592
+ config: {
2593
+ policies: [
2594
+ {
2595
+ name: 'admin::hasPermissions',
2596
+ config: {
2597
+ actions: [
2598
+ 'plugin::content-type-builder.read'
2599
+ ]
2600
+ }
2601
+ }
2602
+ ]
2603
+ }
2604
+ },
2605
+ {
2606
+ method: 'DELETE',
2607
+ path: '/component-categories/:name',
2608
+ handler: 'component-categories.deleteCategory',
2609
+ config: {
2610
+ policies: [
2611
+ {
2612
+ name: 'admin::hasPermissions',
2613
+ config: {
2614
+ actions: [
2615
+ 'plugin::content-type-builder.read'
2616
+ ]
2617
+ }
2618
+ }
2619
+ ]
2620
+ }
2621
+ }
2622
+ ]
2623
+ };
2624
+
2625
+ var contentApi = {
2626
+ type: 'content-api',
2627
+ routes: [
2628
+ {
2629
+ method: 'GET',
2630
+ path: '/content-types',
2631
+ handler: 'content-types.getContentTypes'
2632
+ },
2633
+ {
2634
+ method: 'GET',
2635
+ path: '/content-types/:uid',
2636
+ handler: 'content-types.getContentType'
2637
+ },
2638
+ {
2639
+ method: 'GET',
2640
+ path: '/components',
2641
+ handler: 'components.getComponents'
2642
+ },
2643
+ {
2644
+ method: 'GET',
2645
+ path: '/components/:uid',
2646
+ handler: 'components.getComponent'
2647
+ }
2648
+ ]
2649
+ };
2650
+
2651
+ var routes = {
2652
+ admin,
2653
+ 'content-api': contentApi
2654
+ };
2655
+
2656
+ // eslint-disable-next-line import/no-extraneous-dependencies
2657
+ var index = (()=>({
2658
+ config,
2659
+ bootstrap,
2660
+ services,
2661
+ controllers: exportObject,
2662
+ routes
2663
+ }));
2664
+
2312
2665
  module.exports = index;
2313
2666
  //# sourceMappingURL=index.js.map