@strapi/strapi 4.3.4 → 4.3.5

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 (112) hide show
  1. package/README.md +1 -1
  2. package/bin/strapi.js +29 -26
  3. package/lib/Strapi.js +9 -9
  4. package/lib/commands/admin-create.js +2 -5
  5. package/lib/commands/admin-reset.js +1 -1
  6. package/lib/commands/build.js +1 -1
  7. package/lib/commands/builders/admin.js +1 -1
  8. package/lib/commands/builders/typescript.js +2 -2
  9. package/lib/commands/configurationDump.js +3 -3
  10. package/lib/commands/configurationRestore.js +5 -4
  11. package/lib/commands/console.js +1 -1
  12. package/lib/commands/content-types/list.js +2 -2
  13. package/lib/commands/controllers/list.js +2 -2
  14. package/lib/commands/develop.js +16 -10
  15. package/lib/commands/hooks/list.js +2 -2
  16. package/lib/commands/install.js +4 -4
  17. package/lib/commands/middlewares/list.js +2 -2
  18. package/lib/commands/new.js +1 -1
  19. package/lib/commands/opt-in-telemetry.js +12 -13
  20. package/lib/commands/opt-out-telemetry.js +3 -3
  21. package/lib/commands/policies/list.js +2 -2
  22. package/lib/commands/routes/list.js +3 -3
  23. package/lib/commands/services/list.js +2 -2
  24. package/lib/commands/start.js +1 -0
  25. package/lib/commands/ts/generate-types.js +1 -1
  26. package/lib/commands/uninstall.js +4 -4
  27. package/lib/commands/watchAdmin.js +1 -1
  28. package/lib/compile.js +1 -1
  29. package/lib/container.js +1 -1
  30. package/lib/core/app-configuration/config-loader.js +2 -2
  31. package/lib/core/app-configuration/load-config-file.js +3 -3
  32. package/lib/core/bootstrap.js +1 -1
  33. package/lib/core/domain/component/validator.js +3 -9
  34. package/lib/core/domain/content-type/index.js +2 -2
  35. package/lib/core/domain/content-type/validator.js +9 -23
  36. package/lib/core/domain/module/index.js +1 -1
  37. package/lib/core/domain/module/validation.js +3 -4
  38. package/lib/core/loaders/admin.js +1 -1
  39. package/lib/core/loaders/apis.js +22 -30
  40. package/lib/core/loaders/components.js +2 -2
  41. package/lib/core/loaders/middlewares.js +1 -1
  42. package/lib/core/loaders/plugins/get-enabled-plugins.js +7 -7
  43. package/lib/core/loaders/plugins/index.js +7 -8
  44. package/lib/core/loaders/sanitizers.js +1 -1
  45. package/lib/core/loaders/src-index.js +2 -2
  46. package/lib/core/registries/apis.js +1 -1
  47. package/lib/core/registries/config.js +1 -1
  48. package/lib/core/registries/content-types.js +1 -1
  49. package/lib/core/registries/modules.js +1 -1
  50. package/lib/core/registries/plugins.js +1 -1
  51. package/lib/core/registries/services.js +1 -1
  52. package/lib/core/utils.js +3 -6
  53. package/lib/core-api/controller/transform.js +4 -4
  54. package/lib/core-api/service/collection-type.js +1 -1
  55. package/lib/core-api/service/index.d.ts +6 -3
  56. package/lib/core-api/service/pagination.js +5 -5
  57. package/lib/factories.js +5 -5
  58. package/lib/load/filepath-to-prop-path.js +2 -2
  59. package/lib/load/load-files.js +1 -1
  60. package/lib/load/package-path.js +1 -1
  61. package/lib/middlewares/body.js +7 -3
  62. package/lib/middlewares/compression.js +1 -1
  63. package/lib/middlewares/cors.js +3 -10
  64. package/lib/middlewares/ip.js +1 -1
  65. package/lib/middlewares/logger.js +4 -1
  66. package/lib/middlewares/powered-by.js +1 -1
  67. package/lib/middlewares/query.js +8 -2
  68. package/lib/middlewares/response-time.js +1 -1
  69. package/lib/middlewares/responses.js +1 -1
  70. package/lib/middlewares/security.js +21 -16
  71. package/lib/middlewares/session.js +2 -2
  72. package/lib/migrations/draft-publish.js +1 -5
  73. package/lib/services/auth/index.js +1 -3
  74. package/lib/services/core-store.js +4 -4
  75. package/lib/services/entity-service/components.js +12 -18
  76. package/lib/services/entity-service/index.js +65 -51
  77. package/lib/services/entity-validator/index.js +134 -127
  78. package/lib/services/entity-validator/validators.js +18 -15
  79. package/lib/services/errors.js +6 -10
  80. package/lib/services/event-hub.js +1 -0
  81. package/lib/services/fs.js +1 -1
  82. package/lib/services/metrics/index.js +6 -9
  83. package/lib/services/metrics/is-truthy.js +1 -1
  84. package/lib/services/metrics/sender.js +4 -4
  85. package/lib/services/metrics/stringify-deep.js +1 -1
  86. package/lib/services/server/admin-api.js +1 -1
  87. package/lib/services/server/compose-endpoint.js +7 -7
  88. package/lib/services/server/content-api.js +1 -1
  89. package/lib/services/server/http-server.js +9 -9
  90. package/lib/services/server/index.js +4 -4
  91. package/lib/services/server/koa.js +9 -12
  92. package/lib/services/server/middleware.js +1 -1
  93. package/lib/services/server/policy.js +1 -1
  94. package/lib/services/server/register-middlewares.js +6 -8
  95. package/lib/services/server/register-routes.js +11 -11
  96. package/lib/services/server/routing.js +11 -26
  97. package/lib/services/utils/upload-files.js +3 -3
  98. package/lib/services/webhook-runner.js +8 -7
  99. package/lib/services/webhook-store.js +3 -2
  100. package/lib/services/worker-queue.js +1 -0
  101. package/lib/types/core/strapi/index.d.ts +4 -3
  102. package/lib/types/factories.d.ts +3 -3
  103. package/lib/utils/addSlash.js +3 -3
  104. package/lib/utils/ee.js +1 -1
  105. package/lib/utils/import-default.js +1 -1
  106. package/lib/utils/open-browser.js +1 -1
  107. package/lib/utils/run-checks.js +4 -4
  108. package/lib/utils/signals.js +2 -2
  109. package/lib/utils/startup-logger.js +2 -2
  110. package/lib/utils/success.js +1 -1
  111. package/lib/utils/update-notifier/index.js +4 -4
  112. package/package.json +14 -14
@@ -21,7 +21,7 @@ const getLimitConfigDefaults = () => ({
21
21
  const shouldApplyMaxLimit = (limit, maxLimit = null, { isPagedPagination = false } = {}) =>
22
22
  (!isPagedPagination && limit === -1) || (maxLimit && limit > maxLimit);
23
23
 
24
- const shouldCount = params => {
24
+ const shouldCount = (params) => {
25
25
  if (has('pagination.withCount', params)) {
26
26
  const { withCount } = params.pagination;
27
27
 
@@ -45,10 +45,10 @@ const shouldCount = params => {
45
45
  return Boolean(strapi.config.get('api.rest.withCount', true));
46
46
  };
47
47
 
48
- const isOffsetPagination = pagination => has('start', pagination) || has('limit', pagination);
49
- const isPagedPagination = pagination => has('page', pagination) || has('pageSize', pagination);
48
+ const isOffsetPagination = (pagination) => has('start', pagination) || has('limit', pagination);
49
+ const isPagedPagination = (pagination) => has('page', pagination) || has('pageSize', pagination);
50
50
 
51
- const getPaginationInfo = params => {
51
+ const getPaginationInfo = (params) => {
52
52
  const { defaultLimit, maxLimit } = getLimitConfigDefaults();
53
53
 
54
54
  const { pagination } = params;
@@ -90,7 +90,7 @@ const getPaginationInfo = params => {
90
90
  };
91
91
  };
92
92
 
93
- const convertPagedToStartLimit = pagination => {
93
+ const convertPagedToStartLimit = (pagination) => {
94
94
  if (isPagedPagination(pagination)) {
95
95
  const { page, pageSize } = pagination;
96
96
  return {
package/lib/factories.js CHANGED
@@ -12,7 +12,7 @@ const createCoreController = (uid, cfg = {}) => {
12
12
  contentType: strapi.contentType(uid),
13
13
  });
14
14
 
15
- let userCtrl = typeof cfg === 'function' ? cfg({ strapi }) : cfg;
15
+ const userCtrl = typeof cfg === 'function' ? cfg({ strapi }) : cfg;
16
16
 
17
17
  for (const methodName of Object.keys(baseController)) {
18
18
  if (userCtrl[methodName] === undefined) {
@@ -31,7 +31,7 @@ const createCoreService = (uid, cfg = {}) => {
31
31
  contentType: strapi.contentType(uid),
32
32
  });
33
33
 
34
- let userService = typeof cfg === 'function' ? cfg({ strapi }) : cfg;
34
+ const userService = typeof cfg === 'function' ? cfg({ strapi }) : cfg;
35
35
 
36
36
  for (const methodName of Object.keys(baseService)) {
37
37
  if (userService[methodName] === undefined) {
@@ -58,15 +58,15 @@ const createCoreRouter = (uid, cfg = {}) => {
58
58
 
59
59
  const defaultRoutes = createRoutes({ contentType });
60
60
 
61
- Object.keys(defaultRoutes).forEach(routeName => {
61
+ Object.keys(defaultRoutes).forEach((routeName) => {
62
62
  const defaultRoute = defaultRoutes[routeName];
63
63
 
64
64
  Object.assign(defaultRoute.config, config[routeName] || {});
65
65
  });
66
66
 
67
67
  const selectedRoutes = pipe(
68
- routes => (except ? omit(except, routes) : routes),
69
- routes => (only ? pick(only, routes) : routes)
68
+ (routes) => (except ? omit(except, routes) : routes),
69
+ (routes) => (only ? pick(only, routes) : routes)
70
70
  )(defaultRoutes);
71
71
 
72
72
  routes = Object.values(selectedRoutes);
@@ -8,13 +8,13 @@ const _ = require('lodash');
8
8
  * @param {boolean} useFileNameAsKey - wethear to skip the last path key
9
9
  */
10
10
  module.exports = (filePath, useFileNameAsKey = true) => {
11
- let cleanPath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
11
+ const cleanPath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
12
12
 
13
13
  const prop = cleanPath
14
14
  .replace(/(\.settings|\.json|\.js)/g, '')
15
15
  .toLowerCase()
16
16
  .split('/')
17
- .map(p => _.trimStart(p, '.'))
17
+ .map((p) => _.trimStart(p, '.'))
18
18
  .join('.')
19
19
  .split('.');
20
20
 
@@ -26,7 +26,7 @@ const loadFiles = async (
26
26
  const root = {};
27
27
  const files = await glob(pattern, { cwd: dir, ...globArgs });
28
28
 
29
- for (let file of files) {
29
+ for (const file of files) {
30
30
  const absolutePath = path.resolve(dir, file);
31
31
 
32
32
  // load module
@@ -6,4 +6,4 @@ const path = require('path');
6
6
  * Returns the path to a node modules root directory (not the main file path)
7
7
  * @param {string} moduleName - name of a node module
8
8
  */
9
- module.exports = moduleName => path.dirname(require.resolve(`${moduleName}/package.json`));
9
+ module.exports = (moduleName) => path.dirname(require.resolve(`${moduleName}/package.json`));
@@ -23,12 +23,16 @@ function getFiles(ctx) {
23
23
  /**
24
24
  * @type {import('./').MiddlewareFactory}
25
25
  */
26
- module.exports = config => {
26
+
27
+ module.exports = (config, { strapi }) => {
27
28
  const bodyConfig = defaultsDeep(defaults, config);
28
29
 
30
+ const { config: gqlConfig } = strapi.plugin('graphql');
31
+ const gqlEndpoint = gqlConfig('endpoint');
32
+
29
33
  return async (ctx, next) => {
30
34
  // TODO: find a better way later
31
- if (ctx.url === '/graphql') {
35
+ if (ctx.url === gqlEndpoint) {
32
36
  await next();
33
37
  } else {
34
38
  try {
@@ -64,7 +68,7 @@ module.exports = config => {
64
68
  if (files) {
65
69
  if (Array.isArray(files)) {
66
70
  // not awaiting to not slow the request
67
- Promise.all(files.map(file => fse.remove(file.path)));
71
+ Promise.all(files.map((file) => fse.remove(file.path)));
68
72
  } else if (files && files.path) {
69
73
  // not awaiting to not slow the request
70
74
  fse.remove(files.path);
@@ -5,4 +5,4 @@ const compress = require('koa-compress');
5
5
  /**
6
6
  * @type {import('./').MiddlewareFactory}
7
7
  */
8
- module.exports = config => compress(config);
8
+ module.exports = (config) => compress(config);
@@ -15,16 +15,9 @@ const defaults = {
15
15
  /**
16
16
  * @type {import('./').MiddlewareFactory}
17
17
  */
18
- module.exports = config => {
19
- const {
20
- origin,
21
- expose,
22
- maxAge,
23
- credentials,
24
- methods,
25
- headers,
26
- keepHeadersOnError,
27
- } = defaultsDeep(defaults, config);
18
+ module.exports = (config) => {
19
+ const { origin, expose, maxAge, credentials, methods, headers, keepHeadersOnError } =
20
+ defaultsDeep(defaults, config);
28
21
 
29
22
  return cors({
30
23
  async origin(ctx) {
@@ -5,4 +5,4 @@ const ip = require('koa-ip');
5
5
  /**
6
6
  * @type {import('./').MiddlewareFactory}
7
7
  */
8
- module.exports = config => ip(config);
8
+ module.exports = (config) => ip(config);
@@ -1,7 +1,10 @@
1
1
  'use strict';
2
+
3
+ /* eslint-disable no-nested-ternary */
4
+
2
5
  const chalk = require('chalk');
3
6
 
4
- const codeToColor = code => {
7
+ const codeToColor = (code) => {
5
8
  return code >= 500
6
9
  ? chalk.red(code)
7
10
  : code >= 400
@@ -9,7 +9,7 @@ const defaults = {
9
9
  /**
10
10
  * @type {import('./').MiddlewareFactory}
11
11
  */
12
- module.exports = config => {
12
+ module.exports = (config) => {
13
13
  const { poweredBy } = defaultsDeep(defaults, config);
14
14
 
15
15
  return async (ctx, next) => {
@@ -21,8 +21,14 @@ const addQsParser = (app, settings) => {
21
21
  */
22
22
  get() {
23
23
  const qstr = this.querystring;
24
- const cache = (this._querycache = this._querycache || {});
25
- return cache[qstr] || (cache[qstr] = qs.parse(qstr, settings));
24
+ this._querycache = this._querycache || {};
25
+ const cache = this._querycache;
26
+
27
+ if (!cache[qstr]) {
28
+ cache[qstr] = qs.parse(qstr, settings);
29
+ }
30
+
31
+ return cache[qstr];
26
32
  },
27
33
 
28
34
  /*
@@ -10,6 +10,6 @@ module.exports = () => {
10
10
  await next();
11
11
 
12
12
  const delta = Math.ceil(Date.now() - start);
13
- ctx.set('X-Response-Time', delta + 'ms');
13
+ ctx.set('X-Response-Time', `${delta}ms`);
14
14
  };
15
15
  };
@@ -9,7 +9,7 @@ module.exports = (config = {}) => {
9
9
  return async (ctx, next) => {
10
10
  await next();
11
11
 
12
- const status = ctx.status;
12
+ const { status } = ctx;
13
13
  const handler = prop(`handlers.${status}`, config);
14
14
 
15
15
  if (isFunction(handler)) {
@@ -30,22 +30,27 @@ const defaults = {
30
30
  /**
31
31
  * @type {import('./').MiddlewareFactory}
32
32
  */
33
- module.exports = config => (ctx, next) => {
34
- let helmetConfig = defaultsDeep(defaults, config);
35
33
 
36
- if (
37
- ctx.method === 'GET' &&
38
- ['/graphql', '/documentation'].some(str => ctx.path.startsWith(str))
39
- ) {
40
- helmetConfig = merge(helmetConfig, {
41
- contentSecurityPolicy: {
42
- directives: {
43
- 'script-src': ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net'],
44
- 'img-src': ["'self'", 'data:', 'cdn.jsdelivr.net', 'strapi.io'],
34
+ module.exports =
35
+ (config, { strapi }) =>
36
+ (ctx, next) => {
37
+ let helmetConfig = defaultsDeep(defaults, config);
38
+ const { config: gqlConfig } = strapi.plugin('graphql');
39
+ const gqlEndpoint = gqlConfig('endpoint');
40
+
41
+ if (
42
+ ctx.method === 'GET' &&
43
+ [gqlEndpoint, '/documentation'].some((str) => ctx.path.startsWith(str))
44
+ ) {
45
+ helmetConfig = merge(helmetConfig, {
46
+ contentSecurityPolicy: {
47
+ directives: {
48
+ 'script-src': ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net'],
49
+ 'img-src': ["'self'", 'data:', 'cdn.jsdelivr.net', 'strapi.io'],
50
+ },
45
51
  },
46
- },
47
- });
48
- }
52
+ });
53
+ }
49
54
 
50
- return helmet(helmetConfig)(ctx, next);
51
- };
55
+ return helmet(helmetConfig)(ctx, next);
56
+ };
@@ -12,12 +12,12 @@ const defaultConfig = {
12
12
  signed: true,
13
13
  rolling: false,
14
14
  renew: false,
15
- secure: process.env.NODE_ENV === 'production' ? true : false,
15
+ secure: process.env.NODE_ENV === 'production',
16
16
  sameSite: null,
17
17
  };
18
18
 
19
19
  module.exports = (userConfig, { strapi }) => {
20
- const keys = strapi.server.app.keys;
20
+ const { keys } = strapi.server.app;
21
21
  if (!isArray(keys) || isEmpty(keys) || keys.some(isEmpty)) {
22
22
  throw new Error(
23
23
  `App keys are required. Please set app.keys in config/server.js (ex: keys: ['myKeyA', 'myKeyB'])`
@@ -42,11 +42,7 @@ const disableDraftAndPublish = async ({ oldContentTypes, contentTypes }) => {
42
42
 
43
43
  // if d&p was disabled remove unpublish content before sync
44
44
  if (hasDraftAndPublish(oldContentType) && !hasDraftAndPublish(contentType)) {
45
- await strapi.db
46
- .queryBuilder(uid)
47
- .delete()
48
- .where({ published_at: null })
49
- .execute();
45
+ await strapi.db.queryBuilder(uid).delete().where({ published_at: null }).execute();
50
46
  }
51
47
  }
52
48
  };
@@ -8,7 +8,7 @@ const { UnauthorizedError } = require('@strapi/utils').errors;
8
8
  const INVALID_STRATEGY_MSG =
9
9
  'Invalid auth strategy. Expecting an object with properties {name: string, authenticate: function, verify: function}';
10
10
 
11
- const validStrategy = strategy => {
11
+ const validStrategy = (strategy) => {
12
12
  assert(has('authenticate', strategy), INVALID_STRATEGY_MSG);
13
13
  assert(typeof strategy.authenticate === 'function', INVALID_STRATEGY_MSG);
14
14
 
@@ -78,8 +78,6 @@ const createAuthentication = () => {
78
78
  if (typeof auth.strategy.verify === 'function') {
79
79
  return auth.strategy.verify(auth, config);
80
80
  }
81
-
82
- return;
83
81
  },
84
82
  };
85
83
  };
@@ -30,11 +30,11 @@ const createCoreStore = ({ db }) => {
30
30
  };
31
31
  };
32
32
 
33
- const store = function(defaultParams = {}) {
33
+ const store = function (defaultParams = {}) {
34
34
  return {
35
- get: params => store.get(mergeParams(defaultParams, params)),
36
- set: params => store.set(mergeParams(defaultParams, params)),
37
- delete: params => store.delete(mergeParams(defaultParams, params)),
35
+ get: (params) => store.get(mergeParams(defaultParams, params)),
36
+ set: (params) => store.set(mergeParams(defaultParams, params)),
37
+ delete: (params) => store.delete(mergeParams(defaultParams, params)),
38
38
  };
39
39
  };
40
40
 
@@ -8,7 +8,7 @@ const { ApplicationError } = require('@strapi/utils').errors;
8
8
 
9
9
  const omitComponentData = (contentType, data) => {
10
10
  const { attributes } = contentType;
11
- const componentAttributes = Object.keys(attributes).filter(attributeName =>
11
+ const componentAttributes = Object.keys(attributes).filter((attributeName) =>
12
12
  contentTypesUtils.isComponentAttribute(attributes[attributeName])
13
13
  );
14
14
 
@@ -43,7 +43,7 @@ const createComponents = async (uid, data) => {
43
43
  }
44
44
 
45
45
  const components = await Promise.all(
46
- componentValue.map(value => createComponent(componentUID, value))
46
+ componentValue.map((value) => createComponent(componentUID, value))
47
47
  );
48
48
 
49
49
  // TODO: add order
@@ -129,7 +129,7 @@ const updateComponents = async (uid, entityToUpdate, data) => {
129
129
  }
130
130
 
131
131
  const components = await Promise.all(
132
- componentValue.map(value => updateOrCreateComponent(componentUID, value))
132
+ componentValue.map((value) => updateOrCreateComponent(componentUID, value))
133
133
  );
134
134
 
135
135
  componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }, idx) => {
@@ -197,17 +197,11 @@ const deleteOldComponents = async (
197
197
  ) => {
198
198
  const previousValue = await strapi.query(uid).load(entityToUpdate, attributeName);
199
199
 
200
- const idsToKeep = _.castArray(componentValue)
201
- .filter(has('id'))
202
- .map(prop('id'))
203
- .map(toString);
200
+ const idsToKeep = _.castArray(componentValue).filter(has('id')).map(prop('id')).map(toString);
204
201
 
205
- const allIds = _.castArray(previousValue)
206
- .filter(has('id'))
207
- .map(prop('id'))
208
- .map(toString);
202
+ const allIds = _.castArray(previousValue).filter(has('id')).map(prop('id')).map(toString);
209
203
 
210
- idsToKeep.forEach(id => {
204
+ idsToKeep.forEach((id) => {
211
205
  if (!allIds.includes(id)) {
212
206
  throw new ApplicationError(
213
207
  `Some of the provided components in ${attributeName} are not related to the entity`
@@ -242,7 +236,7 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
242
236
  }));
243
237
 
244
238
  idsToKeep.forEach(({ id, __component }) => {
245
- if (!allIds.find(el => el.id === id && el.__component === __component)) {
239
+ if (!allIds.find((el) => el.id === id && el.__component === __component)) {
246
240
  const err = new Error(
247
241
  `Some of the provided components in ${attributeName} are not related to the entity`
248
242
  );
@@ -252,7 +246,7 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
252
246
  });
253
247
 
254
248
  const idsToDelete = allIds.reduce((acc, { id, __component }) => {
255
- if (!idsToKeep.find(el => el.id === id && el.__component === __component)) {
249
+ if (!idsToKeep.find((el) => el.id === id && el.__component === __component)) {
256
250
  acc.push({ id, __component });
257
251
  }
258
252
 
@@ -283,7 +277,7 @@ const deleteComponents = async (uid, entityToDelete) => {
283
277
  }
284
278
 
285
279
  if (Array.isArray(value)) {
286
- await Promise.all(value.map(subValue => deleteComponent(componentUID, subValue)));
280
+ await Promise.all(value.map((subValue) => deleteComponent(componentUID, subValue)));
287
281
  } else {
288
282
  await deleteComponent(componentUID, value);
289
283
  }
@@ -299,7 +293,7 @@ const deleteComponents = async (uid, entityToDelete) => {
299
293
  }
300
294
 
301
295
  if (Array.isArray(value)) {
302
- await Promise.all(value.map(subValue => deleteComponent(subValue.__component, subValue)));
296
+ await Promise.all(value.map((subValue) => deleteComponent(subValue.__component, subValue)));
303
297
  }
304
298
 
305
299
  continue;
@@ -307,9 +301,9 @@ const deleteComponents = async (uid, entityToDelete) => {
307
301
  }
308
302
  };
309
303
 
310
- /***************************
304
+ /** *************************
311
305
  Component queries
312
- ***************************/
306
+ ************************** */
313
307
 
314
308
  // components can have nested compos so this must be recursive
315
309
  const createComponent = async (uid, data) => {
@@ -2,11 +2,8 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const delegate = require('delegates');
5
- const {
6
- InvalidTimeError,
7
- InvalidDateError,
8
- InvalidDateTimeError,
9
- } = require('@strapi/database').errors;
5
+ const { InvalidTimeError, InvalidDateError, InvalidDateTimeError } =
6
+ require('@strapi/database').errors;
10
7
  const {
11
8
  webhook: webhookUtils,
12
9
  contentTypes: contentTypesUtils,
@@ -37,51 +34,6 @@ const updatePipeline = (data, context) => {
37
34
  return applyTransforms(data, context);
38
35
  };
39
36
 
40
- module.exports = ctx => {
41
- const implementation = createDefaultImplementation(ctx);
42
-
43
- const service = {
44
- implementation,
45
- decorate(decorator) {
46
- if (typeof decorator !== 'function') {
47
- throw new Error(`Decorator must be a function, received ${typeof decorator}`);
48
- }
49
-
50
- this.implementation = Object.assign({}, this.implementation, decorator(this.implementation));
51
- return this;
52
- },
53
- };
54
-
55
- const delegator = delegate(service, 'implementation');
56
-
57
- // delegate every method in implementation
58
- Object.keys(service.implementation).forEach(key => delegator.method(key));
59
-
60
- // wrap methods to handle Database Errors
61
- service.decorate(oldService => {
62
- const newService = _.mapValues(
63
- oldService,
64
- (method, methodName) =>
65
- async function(...args) {
66
- try {
67
- return await oldService[methodName].call(this, ...args);
68
- } catch (error) {
69
- if (
70
- databaseErrorsToTransform.some(errorToTransform => error instanceof errorToTransform)
71
- ) {
72
- throw new ValidationError(error.message);
73
- }
74
- throw error;
75
- }
76
- }
77
- );
78
-
79
- return newService;
80
- });
81
-
82
- return service;
83
- };
84
-
85
37
  /**
86
38
  * @type {import('.').default}
87
39
  */
@@ -276,7 +228,19 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
276
228
  // select / populate
277
229
  const query = transformParamsToQuery(uid, wrappedParams);
278
230
 
279
- return db.query(uid).deleteMany(query);
231
+ const entitiesToDelete = await db.query(uid).findMany(query);
232
+
233
+ if (!entitiesToDelete.length) {
234
+ return null;
235
+ }
236
+
237
+ const deletedEntities = await db.query(uid).deleteMany(query);
238
+ await Promise.all(entitiesToDelete.map((entity) => deleteComponents(uid, entity)));
239
+
240
+ // Trigger webhooks. One for each entity
241
+ await Promise.all(entitiesToDelete.map((entity) => this.emitEvent(uid, ENTRY_DELETE, entity)));
242
+
243
+ return deletedEntities;
280
244
  },
281
245
 
282
246
  load(uid, entity, field, params = {}) {
@@ -303,8 +267,58 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
303
267
  Object.assign(loadParams, transformParamsToQuery('plugin::upload.file', params));
304
268
  break;
305
269
  }
270
+ default: {
271
+ break;
272
+ }
306
273
  }
307
274
 
308
275
  return db.query(uid).load(entity, field, loadParams);
309
276
  },
310
277
  });
278
+
279
+ module.exports = (ctx) => {
280
+ const implementation = createDefaultImplementation(ctx);
281
+
282
+ const service = {
283
+ implementation,
284
+ decorate(decorator) {
285
+ if (typeof decorator !== 'function') {
286
+ throw new Error(`Decorator must be a function, received ${typeof decorator}`);
287
+ }
288
+
289
+ this.implementation = { ...this.implementation, ...decorator(this.implementation) };
290
+ return this;
291
+ },
292
+ };
293
+
294
+ const delegator = delegate(service, 'implementation');
295
+
296
+ // delegate every method in implementation
297
+ Object.keys(service.implementation).forEach((key) => delegator.method(key));
298
+
299
+ // wrap methods to handle Database Errors
300
+ service.decorate((oldService) => {
301
+ const newService = _.mapValues(
302
+ oldService,
303
+ (method, methodName) =>
304
+ async function (...args) {
305
+ try {
306
+ return await oldService[methodName].call(this, ...args);
307
+ } catch (error) {
308
+ if (
309
+ databaseErrorsToTransform.some(
310
+ (errorToTransform) => error instanceof errorToTransform
311
+ )
312
+ ) {
313
+ throw new ValidationError(error.message);
314
+ }
315
+ throw error;
316
+ }
317
+ }
318
+ );
319
+
320
+ return newService;
321
+ });
322
+
323
+ return service;
324
+ };