@strapi/strapi 4.3.4 → 4.3.7

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 +18 -9
  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 +10 -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 +22 -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 +34 -20
  76. package/lib/services/entity-service/index.js +73 -52
  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 +6 -6
  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 +15 -15
@@ -3,7 +3,7 @@
3
3
  const { isNil, isPlainObject } = require('lodash/fp');
4
4
  const { parseMultipartData } = require('@strapi/utils');
5
5
 
6
- const parseBody = ctx => {
6
+ const parseBody = (ctx) => {
7
7
  if (ctx.is('multipart')) {
8
8
  return parseMultipartData(ctx);
9
9
  }
@@ -26,7 +26,7 @@ const transformResponse = (resource, meta = {}, { contentType } = {}) => {
26
26
 
27
27
  const transformComponent = (data, component) => {
28
28
  if (Array.isArray(data)) {
29
- return data.map(datum => transformComponent(datum, component));
29
+ return data.map((datum) => transformComponent(datum, component));
30
30
  }
31
31
 
32
32
  const res = transformEntry(data, component);
@@ -45,7 +45,7 @@ const transformEntry = (entry, type) => {
45
45
  }
46
46
 
47
47
  if (Array.isArray(entry)) {
48
- return entry.map(singleEntry => transformEntry(singleEntry, type));
48
+ return entry.map((singleEntry) => transformEntry(singleEntry, type));
49
49
  }
50
50
 
51
51
  if (!isPlainObject(entry)) {
@@ -71,7 +71,7 @@ const transformEntry = (entry, type) => {
71
71
  attributeValues[key] = property;
72
72
  }
73
73
 
74
- attributeValues[key] = property.map(subProperty => {
74
+ attributeValues[key] = property.map((subProperty) => {
75
75
  return transformComponent(subProperty, strapi.components[subProperty.__component]);
76
76
  });
77
77
  } else if (attribute && attribute.type === 'media') {
@@ -14,7 +14,7 @@ const {
14
14
  transformPaginationResponse,
15
15
  } = require('./pagination');
16
16
 
17
- const setPublishedAt = data => {
17
+ const setPublishedAt = (data) => {
18
18
  data[PUBLISHED_AT_ATTRIBUTE] = propOr(new Date(), PUBLISHED_AT_ATTRIBUTE, data);
19
19
  };
20
20
 
@@ -12,11 +12,14 @@ export interface SingleTypeService extends BaseService {
12
12
 
13
13
  export interface CollectionTypeService extends BaseService {
14
14
  find?(params: object): Promise<Entity[]> | Entity;
15
- findOne?(entityId: string,params: object): Promise<Entity> | Entity;
15
+ findOne?(entityId: string, params: object): Promise<Entity> | Entity;
16
16
  create?(params: object): Promise<Entity> | Entity;
17
- update?(entityId: string,params: object): Promise<Entity> | Entity;
18
- delete?(entityId: string,params: object): Promise<Entity> | Entity;
17
+ update?(entityId: string, params: object): Promise<Entity> | Entity;
18
+ delete?(entityId: string, params: object): Promise<Entity> | Entity;
19
19
  }
20
20
 
21
21
  export type Service = SingleTypeService | CollectionTypeService;
22
22
 
23
+ export type GenericService = Partial<Service> & {
24
+ [method: string | number | symbol]: <T = any>(...args: any) => T;
25
+ };
@@ -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,19 @@ 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
+ let gqlEndpoint;
31
+ if (strapi.plugin('graphql')) {
32
+ const { config: gqlConfig } = strapi.plugin('graphql');
33
+ gqlEndpoint = gqlConfig('endpoint');
34
+ }
35
+
29
36
  return async (ctx, next) => {
30
37
  // TODO: find a better way later
31
- if (ctx.url === '/graphql') {
38
+ if (gqlEndpoint && ctx.url === gqlEndpoint) {
32
39
  await next();
33
40
  } else {
34
41
  try {
@@ -64,7 +71,7 @@ module.exports = config => {
64
71
  if (files) {
65
72
  if (Array.isArray(files)) {
66
73
  // not awaiting to not slow the request
67
- Promise.all(files.map(file => fse.remove(file.path)));
74
+ Promise.all(files.map((file) => fse.remove(file.path)));
68
75
  } else if (files && files.path) {
69
76
  // not awaiting to not slow the request
70
77
  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,28 @@ 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 specialPaths = ['/documentation'];
39
+
40
+ if (strapi.plugin('graphql')) {
41
+ const { config: gqlConfig } = strapi.plugin('graphql');
42
+ specialPaths.push(gqlConfig('endpoint'));
43
+ }
44
+
45
+ if (ctx.method === 'GET' && specialPaths.some((str) => ctx.path.startsWith(str))) {
46
+ helmetConfig = merge(helmetConfig, {
47
+ contentSecurityPolicy: {
48
+ directives: {
49
+ 'script-src': ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net'],
50
+ 'img-src': ["'self'", 'data:', 'cdn.jsdelivr.net', 'strapi.io'],
51
+ },
45
52
  },
46
- },
47
- });
48
- }
53
+ });
54
+ }
49
55
 
50
- return helmet(helmetConfig)(ctx, next);
51
- };
56
+ return helmet(helmetConfig)(ctx, next);
57
+ };
@@ -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
 
@@ -5,10 +5,11 @@ const { has, prop, omit, toString } = require('lodash/fp');
5
5
 
6
6
  const { contentTypes: contentTypesUtils } = require('@strapi/utils');
7
7
  const { ApplicationError } = require('@strapi/utils').errors;
8
+ const { getComponentAttributes } = require('@strapi/utils').contentTypes;
8
9
 
9
10
  const omitComponentData = (contentType, data) => {
10
11
  const { attributes } = contentType;
11
- const componentAttributes = Object.keys(attributes).filter(attributeName =>
12
+ const componentAttributes = Object.keys(attributes).filter((attributeName) =>
12
13
  contentTypesUtils.isComponentAttribute(attributes[attributeName])
13
14
  );
14
15
 
@@ -43,7 +44,7 @@ const createComponents = async (uid, data) => {
43
44
  }
44
45
 
45
46
  const components = await Promise.all(
46
- componentValue.map(value => createComponent(componentUID, value))
47
+ componentValue.map((value) => createComponent(componentUID, value))
47
48
  );
48
49
 
49
50
  // TODO: add order
@@ -100,6 +101,18 @@ const createComponents = async (uid, data) => {
100
101
  return componentBody;
101
102
  };
102
103
 
104
+ /**
105
+ * @param {str} uid
106
+ * @param {object} entity
107
+ * @return {Promise<{uid: string, entity: object}>}
108
+ */
109
+ const getComponents = async (uid, entity) => {
110
+ const componentAttributes = getComponentAttributes(strapi.getModel(uid));
111
+
112
+ if (_.isEmpty(componentAttributes)) return {};
113
+ return strapi.query(uid).load(entity, componentAttributes);
114
+ };
115
+
103
116
  /*
104
117
  delete old components
105
118
  create or update
@@ -129,7 +142,7 @@ const updateComponents = async (uid, entityToUpdate, data) => {
129
142
  }
130
143
 
131
144
  const components = await Promise.all(
132
- componentValue.map(value => updateOrCreateComponent(componentUID, value))
145
+ componentValue.map((value) => updateOrCreateComponent(componentUID, value))
133
146
  );
134
147
 
135
148
  componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }, idx) => {
@@ -197,17 +210,11 @@ const deleteOldComponents = async (
197
210
  ) => {
198
211
  const previousValue = await strapi.query(uid).load(entityToUpdate, attributeName);
199
212
 
200
- const idsToKeep = _.castArray(componentValue)
201
- .filter(has('id'))
202
- .map(prop('id'))
203
- .map(toString);
213
+ const idsToKeep = _.castArray(componentValue).filter(has('id')).map(prop('id')).map(toString);
204
214
 
205
- const allIds = _.castArray(previousValue)
206
- .filter(has('id'))
207
- .map(prop('id'))
208
- .map(toString);
215
+ const allIds = _.castArray(previousValue).filter(has('id')).map(prop('id')).map(toString);
209
216
 
210
- idsToKeep.forEach(id => {
217
+ idsToKeep.forEach((id) => {
211
218
  if (!allIds.includes(id)) {
212
219
  throw new ApplicationError(
213
220
  `Some of the provided components in ${attributeName} are not related to the entity`
@@ -242,7 +249,7 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
242
249
  }));
243
250
 
244
251
  idsToKeep.forEach(({ id, __component }) => {
245
- if (!allIds.find(el => el.id === id && el.__component === __component)) {
252
+ if (!allIds.find((el) => el.id === id && el.__component === __component)) {
246
253
  const err = new Error(
247
254
  `Some of the provided components in ${attributeName} are not related to the entity`
248
255
  );
@@ -252,7 +259,7 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
252
259
  });
253
260
 
254
261
  const idsToDelete = allIds.reduce((acc, { id, __component }) => {
255
- if (!idsToKeep.find(el => el.id === id && el.__component === __component)) {
262
+ if (!idsToKeep.find((el) => el.id === id && el.__component === __component)) {
256
263
  acc.push({ id, __component });
257
264
  }
258
265
 
@@ -276,14 +283,17 @@ const deleteComponents = async (uid, entityToDelete) => {
276
283
  if (attribute.type === 'component') {
277
284
  const { component: componentUID } = attribute;
278
285
 
279
- const value = await strapi.query(uid).load(entityToDelete, attributeName);
286
+ // Load attribute value if it's not already loaded
287
+ const value =
288
+ entityToDelete[attributeName] ||
289
+ (await strapi.query(uid).load(entityToDelete, attributeName));
280
290
 
281
291
  if (!value) {
282
292
  continue;
283
293
  }
284
294
 
285
295
  if (Array.isArray(value)) {
286
- await Promise.all(value.map(subValue => deleteComponent(componentUID, subValue)));
296
+ await Promise.all(value.map((subValue) => deleteComponent(componentUID, subValue)));
287
297
  } else {
288
298
  await deleteComponent(componentUID, value);
289
299
  }
@@ -292,14 +302,16 @@ const deleteComponents = async (uid, entityToDelete) => {
292
302
  }
293
303
 
294
304
  if (attribute.type === 'dynamiczone') {
295
- const value = await strapi.query(uid).load(entityToDelete, attributeName);
305
+ const value =
306
+ entityToDelete[attributeName] ||
307
+ (await strapi.query(uid).load(entityToDelete, attributeName));
296
308
 
297
309
  if (!value) {
298
310
  continue;
299
311
  }
300
312
 
301
313
  if (Array.isArray(value)) {
302
- await Promise.all(value.map(subValue => deleteComponent(subValue.__component, subValue)));
314
+ await Promise.all(value.map((subValue) => deleteComponent(subValue.__component, subValue)));
303
315
  }
304
316
 
305
317
  continue;
@@ -307,9 +319,9 @@ const deleteComponents = async (uid, entityToDelete) => {
307
319
  }
308
320
  };
309
321
 
310
- /***************************
322
+ /** *************************
311
323
  Component queries
312
- ***************************/
324
+ ************************** */
313
325
 
314
326
  // components can have nested compos so this must be recursive
315
327
  const createComponent = async (uid, data) => {
@@ -358,7 +370,9 @@ const deleteComponent = async (uid, componentToDelete) => {
358
370
 
359
371
  module.exports = {
360
372
  omitComponentData,
373
+ getComponents,
361
374
  createComponents,
362
375
  updateComponents,
363
376
  deleteComponents,
377
+ deleteComponent,
364
378
  };