@strapi/strapi 4.0.0-next.6 → 4.0.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 (166) hide show
  1. package/README.md +12 -12
  2. package/bin/strapi.js +41 -60
  3. package/lib/Strapi.js +234 -114
  4. package/lib/commands/build.js +16 -6
  5. package/lib/commands/console.js +1 -1
  6. package/lib/commands/content-types/list.js +22 -0
  7. package/lib/commands/develop.js +17 -18
  8. package/lib/commands/generate-template.js +4 -5
  9. package/lib/commands/hooks/list.js +22 -0
  10. package/lib/commands/middlewares/list.js +22 -0
  11. package/lib/commands/new.js +3 -1
  12. package/lib/commands/policies/list.js +22 -0
  13. package/lib/commands/routes/list.js +28 -0
  14. package/lib/commands/services/list.js +22 -0
  15. package/lib/commands/watchAdmin.js +18 -8
  16. package/lib/container.js +6 -6
  17. package/lib/core/app-configuration/config-loader.js +1 -37
  18. package/lib/core/app-configuration/index.js +6 -46
  19. package/lib/core/app-configuration/load-config-file.js +43 -0
  20. package/lib/core/bootstrap.js +5 -117
  21. package/lib/core/domain/component/index.js +24 -0
  22. package/lib/core/domain/component/validator.js +29 -0
  23. package/lib/core/domain/content-type/index.js +140 -0
  24. package/lib/core/domain/content-type/validator.js +64 -0
  25. package/lib/core/domain/module/index.js +108 -0
  26. package/lib/core/domain/module/validation.js +33 -0
  27. package/lib/core/loaders/admin.js +16 -0
  28. package/lib/core/loaders/apis.js +159 -0
  29. package/lib/core/{load-components.js → loaders/components.js} +5 -7
  30. package/lib/core/loaders/index.js +11 -0
  31. package/lib/core/loaders/middlewares.js +36 -0
  32. package/lib/core/loaders/plugins/get-enabled-plugins.js +116 -0
  33. package/lib/core/loaders/plugins/index.js +123 -0
  34. package/lib/core/loaders/policies.js +28 -0
  35. package/lib/core/loaders/src-index.js +39 -0
  36. package/lib/core/registries/apis.js +29 -0
  37. package/lib/core/{app-configuration/config-provider.js → registries/config.js} +4 -11
  38. package/lib/core/registries/content-types.js +97 -0
  39. package/lib/core/registries/controllers.d.ts +7 -0
  40. package/lib/core/registries/controllers.js +114 -0
  41. package/lib/core/registries/hooks.d.ts +20 -0
  42. package/lib/core/registries/hooks.js +87 -0
  43. package/lib/core/registries/middlewares.d.ts +5 -0
  44. package/lib/core/registries/middlewares.js +89 -0
  45. package/lib/core/registries/modules.js +44 -0
  46. package/lib/core/registries/plugins.js +28 -0
  47. package/lib/core/registries/policies.d.ts +9 -0
  48. package/lib/core/registries/policies.js +89 -0
  49. package/lib/core/registries/services.d.ts +7 -0
  50. package/lib/core/registries/services.js +114 -0
  51. package/lib/core/utils.js +35 -0
  52. package/lib/core-api/controller/collection-type.js +45 -26
  53. package/lib/core-api/controller/index.d.ts +25 -0
  54. package/lib/core-api/controller/index.js +33 -11
  55. package/lib/core-api/controller/single-type.js +29 -15
  56. package/lib/core-api/controller/transform.js +62 -6
  57. package/lib/core-api/routes/index.js +71 -0
  58. package/lib/core-api/service/collection-type.js +43 -21
  59. package/lib/core-api/service/index.d.ts +21 -0
  60. package/lib/core-api/service/index.js +8 -67
  61. package/lib/core-api/service/pagination.js +130 -0
  62. package/lib/core-api/service/single-type.js +17 -19
  63. package/lib/factories.d.ts +48 -0
  64. package/lib/factories.js +84 -0
  65. package/lib/index.d.ts +10 -31
  66. package/lib/index.js +5 -1
  67. package/lib/middlewares/body.js +33 -0
  68. package/lib/middlewares/compression.js +8 -0
  69. package/lib/middlewares/cors.js +58 -0
  70. package/lib/middlewares/errors.js +40 -0
  71. package/lib/middlewares/favicon.js +19 -0
  72. package/lib/middlewares/index.d.ts +5 -0
  73. package/lib/middlewares/index.js +30 -117
  74. package/lib/middlewares/ip.js +8 -0
  75. package/lib/middlewares/logger.js +27 -0
  76. package/lib/middlewares/powered-by.js +20 -0
  77. package/lib/middlewares/public/index.js +98 -73
  78. package/lib/middlewares/query.js +46 -0
  79. package/lib/middlewares/response-time.js +15 -0
  80. package/lib/middlewares/responses.js +19 -0
  81. package/lib/middlewares/security.js +51 -0
  82. package/lib/middlewares/session/index.js +6 -6
  83. package/lib/migrations/draft-publish.js +57 -0
  84. package/lib/services/auth/index.js +87 -0
  85. package/lib/services/core-store.js +64 -51
  86. package/lib/services/cron.js +54 -0
  87. package/lib/services/entity-service/attributes/index.js +31 -0
  88. package/lib/services/entity-service/attributes/transforms.js +20 -0
  89. package/lib/services/entity-service/components.js +39 -15
  90. package/lib/services/entity-service/index.d.ts +91 -0
  91. package/lib/services/entity-service/index.js +120 -59
  92. package/lib/services/entity-service/params.js +52 -94
  93. package/lib/services/entity-validator/index.js +76 -43
  94. package/lib/services/entity-validator/validators.js +129 -43
  95. package/lib/services/errors.js +77 -0
  96. package/lib/{core → services}/fs.js +10 -2
  97. package/lib/services/metrics/index.js +41 -38
  98. package/lib/services/metrics/sender.js +2 -2
  99. package/lib/services/server/admin-api.js +14 -0
  100. package/lib/services/server/api.js +36 -0
  101. package/lib/services/server/compose-endpoint.js +141 -0
  102. package/lib/services/server/content-api.js +16 -0
  103. package/lib/{server.js → services/server/http-server.js} +0 -0
  104. package/lib/services/server/index.js +127 -0
  105. package/lib/services/server/koa.js +64 -0
  106. package/lib/services/server/middleware.js +122 -0
  107. package/lib/services/server/policy.js +32 -0
  108. package/lib/services/server/register-middlewares.js +110 -0
  109. package/lib/services/server/register-routes.js +106 -0
  110. package/lib/services/server/routing.js +120 -0
  111. package/lib/services/utils/upload-files.js +1 -1
  112. package/lib/services/webhook-runner.js +1 -1
  113. package/lib/utils/ee.js +3 -3
  114. package/lib/utils/get-dirs.js +17 -0
  115. package/lib/utils/index.js +2 -0
  116. package/lib/utils/is-initialized.js +1 -1
  117. package/lib/utils/run-checks.js +0 -15
  118. package/lib/utils/signals.js +24 -0
  119. package/lib/utils/startup-logger.js +2 -2
  120. package/lib/utils/update-notifier/index.js +3 -2
  121. package/package.json +93 -96
  122. package/lib/commands/generate.js +0 -76
  123. package/lib/core/index.js +0 -17
  124. package/lib/core/load-apis.js +0 -20
  125. package/lib/core/load-extensions.js +0 -71
  126. package/lib/core/load-functions.js +0 -21
  127. package/lib/core/load-middlewares.js +0 -130
  128. package/lib/core/load-modules.js +0 -55
  129. package/lib/core/load-plugins.js +0 -68
  130. package/lib/core/load-policies.js +0 -36
  131. package/lib/core/walk.js +0 -27
  132. package/lib/core-api/index.js +0 -39
  133. package/lib/load/check-reserved-filename.js +0 -10
  134. package/lib/load/load-config-files.js +0 -22
  135. package/lib/load/require-file-parse.js +0 -15
  136. package/lib/middlewares/boom/defaults.json +0 -5
  137. package/lib/middlewares/boom/index.js +0 -147
  138. package/lib/middlewares/cors/index.js +0 -66
  139. package/lib/middlewares/cron/defaults.json +0 -5
  140. package/lib/middlewares/cron/index.js +0 -43
  141. package/lib/middlewares/favicon/defaults.json +0 -7
  142. package/lib/middlewares/favicon/index.js +0 -32
  143. package/lib/middlewares/gzip/defaults.json +0 -6
  144. package/lib/middlewares/gzip/index.js +0 -19
  145. package/lib/middlewares/helmet/defaults.json +0 -18
  146. package/lib/middlewares/helmet/index.js +0 -9
  147. package/lib/middlewares/ip/defaults.json +0 -7
  148. package/lib/middlewares/ip/index.js +0 -25
  149. package/lib/middlewares/language/defaults.json +0 -9
  150. package/lib/middlewares/language/index.js +0 -40
  151. package/lib/middlewares/logger/defaults.json +0 -5
  152. package/lib/middlewares/logger/index.js +0 -37
  153. package/lib/middlewares/parser/defaults.json +0 -11
  154. package/lib/middlewares/parser/index.js +0 -71
  155. package/lib/middlewares/poweredBy/defaults.json +0 -5
  156. package/lib/middlewares/poweredBy/index.js +0 -16
  157. package/lib/middlewares/public/defaults.json +0 -8
  158. package/lib/middlewares/responseTime/defaults.json +0 -5
  159. package/lib/middlewares/responseTime/index.js +0 -25
  160. package/lib/middlewares/responses/defaults.json +0 -5
  161. package/lib/middlewares/responses/index.js +0 -18
  162. package/lib/middlewares/router/defaults.json +0 -7
  163. package/lib/middlewares/router/index.js +0 -64
  164. package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
  165. package/lib/middlewares/router/utils/routerChecker.js +0 -92
  166. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -1,11 +1,16 @@
1
1
  'use strict';
2
2
 
3
- const { transformResponse } = require('./transform');
3
+ const { isObject } = require('lodash/fp');
4
+ const { ValidationError } = require('@strapi/utils').errors;
5
+
6
+ const { parseBody } = require('./transform');
4
7
 
5
8
  /**
6
9
  * Returns a single type controller to handle default core-api actions
7
10
  */
8
- const createSingleTypeController = ({ service, parseMultipartData, sanitize }) => {
11
+ const createSingleTypeController = ({ contentType }) => {
12
+ const { uid } = contentType;
13
+
9
14
  return {
10
15
  /**
11
16
  * Retrieve single type content
@@ -14,8 +19,11 @@ const createSingleTypeController = ({ service, parseMultipartData, sanitize }) =
14
19
  */
15
20
  async find(ctx) {
16
21
  const { query } = ctx;
17
- const entity = await service.find({ params: query });
18
- return transformResponse(sanitize(entity));
22
+
23
+ const entity = await strapi.service(uid).find(query);
24
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
25
+
26
+ return this.transformResponse(sanitizedEntity);
19
27
  },
20
28
 
21
29
  /**
@@ -24,24 +32,30 @@ const createSingleTypeController = ({ service, parseMultipartData, sanitize }) =
24
32
  * @return {Object}
25
33
  */
26
34
  async update(ctx) {
27
- const { body, query } = ctx.request;
28
-
29
- let entity;
30
- if (ctx.is('multipart')) {
31
- const { data, files } = parseMultipartData(ctx);
32
- entity = await service.createOrUpdate({ params: query, data, files });
33
- } else {
34
- entity = await service.createOrUpdate({ params: query, data: body });
35
+ const { query } = ctx.request;
36
+ const { data, files } = parseBody(ctx);
37
+
38
+ if (!isObject(data)) {
39
+ throw new ValidationError('Missing "data" payload in the request body');
35
40
  }
36
41
 
37
- return transformResponse(sanitize(entity));
42
+ const sanitizedInputData = await this.sanitizeInput(data, ctx);
43
+
44
+ const entity = await strapi
45
+ .service(uid)
46
+ .createOrUpdate({ ...query, data: sanitizedInputData, files });
47
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
48
+
49
+ return this.transformResponse(sanitizedEntity);
38
50
  },
39
51
 
40
52
  async delete(ctx) {
41
53
  const { query } = ctx;
42
54
 
43
- const entity = await service.delete({ params: query });
44
- return transformResponse(sanitize(entity));
55
+ const entity = await strapi.service(uid).delete(query);
56
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
57
+
58
+ return this.transformResponse(sanitizedEntity);
45
59
  },
46
60
  };
47
61
  };
@@ -1,41 +1,97 @@
1
1
  'use strict';
2
2
 
3
3
  const { isNil, isPlainObject } = require('lodash/fp');
4
+ const { parseMultipartData } = require('@strapi/utils');
4
5
 
5
- const transformResponse = (resource, meta = {}) => {
6
+ const parseBody = ctx => {
7
+ if (ctx.is('multipart')) {
8
+ return parseMultipartData(ctx);
9
+ }
10
+
11
+ const { data } = ctx.request.body || {};
12
+
13
+ return { data };
14
+ };
15
+
16
+ const transformResponse = (resource, meta = {}, { contentType } = {}) => {
6
17
  if (isNil(resource)) {
7
18
  return resource;
8
19
  }
9
20
 
10
21
  return {
11
- data: transformEntry(resource),
22
+ data: transformEntry(resource, contentType),
12
23
  meta,
13
24
  };
14
25
  };
15
26
 
16
- const transformEntry = entry => {
27
+ const transformComponent = (data, component) => {
28
+ if (Array.isArray(data)) {
29
+ return data.map(datum => transformComponent(datum, component));
30
+ }
31
+
32
+ const res = transformEntry(data, component);
33
+
34
+ if (isNil(res)) {
35
+ return res;
36
+ }
37
+
38
+ const { id, attributes } = res;
39
+ return { id, ...attributes };
40
+ };
41
+
42
+ const transformEntry = (entry, type) => {
17
43
  if (isNil(entry)) {
18
44
  return entry;
19
45
  }
20
46
 
21
47
  if (Array.isArray(entry)) {
22
- return entry.map(singleEntry => transformEntry(singleEntry));
48
+ return entry.map(singleEntry => transformEntry(singleEntry, type));
23
49
  }
24
50
 
25
51
  if (!isPlainObject(entry)) {
26
52
  throw new Error('Entry must be an object');
27
53
  }
28
54
 
29
- const { id, ...attributes } = entry;
55
+ const { id, ...properties } = entry;
56
+
57
+ const attributeValues = {};
58
+
59
+ for (const key in properties) {
60
+ const property = properties[key];
61
+ const attribute = type && type.attributes[key];
62
+
63
+ if (attribute && attribute.type === 'relation') {
64
+ const data = transformEntry(property, strapi.contentType(attribute.target));
65
+
66
+ attributeValues[key] = { data };
67
+ } else if (attribute && attribute.type === 'component') {
68
+ attributeValues[key] = transformComponent(property, strapi.components[attribute.component]);
69
+ } else if (attribute && attribute.type === 'dynamiczone') {
70
+ if (isNil(property)) {
71
+ attributeValues[key] = property;
72
+ }
73
+
74
+ attributeValues[key] = property.map(subProperty => {
75
+ return transformComponent(subProperty, strapi.components[subProperty.__component]);
76
+ });
77
+ } else if (attribute && attribute.type === 'media') {
78
+ const data = transformEntry(property, strapi.contentType('plugin::upload.file'));
79
+
80
+ attributeValues[key] = { data };
81
+ } else {
82
+ attributeValues[key] = property;
83
+ }
84
+ }
30
85
 
31
86
  return {
32
87
  id,
33
- attributes,
88
+ attributes: attributeValues,
34
89
  // NOTE: not necessary for now
35
90
  // meta: {},
36
91
  };
37
92
  };
38
93
 
39
94
  module.exports = {
95
+ parseBody,
40
96
  transformResponse,
41
97
  };
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ const { isSingleType } = require('@strapi/utils').contentTypes;
4
+
5
+ const createRoutes = ({ contentType }) => {
6
+ if (isSingleType(contentType)) {
7
+ return getSingleTypeRoutes(contentType);
8
+ }
9
+
10
+ return getCollectionTypeRoutes(contentType);
11
+ };
12
+
13
+ const getSingleTypeRoutes = ({ uid, info }) => {
14
+ return {
15
+ find: {
16
+ method: 'GET',
17
+ path: `/${info.singularName}`,
18
+ handler: `${uid}.find`,
19
+ config: {},
20
+ },
21
+ update: {
22
+ method: 'PUT',
23
+ path: `/${info.singularName}`,
24
+ handler: `${uid}.update`,
25
+ config: {},
26
+ },
27
+ delete: {
28
+ method: 'DELETE',
29
+ path: `/${info.singularName}`,
30
+ handler: `${uid}.delete`,
31
+ config: {},
32
+ },
33
+ };
34
+ };
35
+
36
+ const getCollectionTypeRoutes = ({ uid, info }) => {
37
+ return {
38
+ find: {
39
+ method: 'GET',
40
+ path: `/${info.pluralName}`,
41
+ handler: `${uid}.find`,
42
+ config: {},
43
+ },
44
+ findOne: {
45
+ method: 'GET',
46
+ path: `/${info.pluralName}/:id`,
47
+ handler: `${uid}.findOne`,
48
+ config: {},
49
+ },
50
+ create: {
51
+ method: 'POST',
52
+ path: `/${info.pluralName}`,
53
+ handler: `${uid}.create`,
54
+ config: {},
55
+ },
56
+ update: {
57
+ method: 'PUT',
58
+ path: `/${info.pluralName}/:id`,
59
+ handler: `${uid}.update`,
60
+ config: {},
61
+ },
62
+ delete: {
63
+ method: 'DELETE',
64
+ path: `/${info.pluralName}/:id`,
65
+ handler: `${uid}.delete`,
66
+ config: {},
67
+ },
68
+ };
69
+ };
70
+
71
+ module.exports = { createRoutes };
@@ -7,6 +7,13 @@ const {
7
7
  constants: { PUBLISHED_AT_ATTRIBUTE },
8
8
  } = require('@strapi/utils').contentTypes;
9
9
 
10
+ const {
11
+ getPaginationInfo,
12
+ convertPagedToStartLimit,
13
+ shouldCount,
14
+ transformPaginationResponse,
15
+ } = require('./pagination');
16
+
10
17
  const setPublishedAt = data => {
11
18
  data[PUBLISHED_AT_ATTRIBUTE] = propOr(new Date(), PUBLISHED_AT_ATTRIBUTE, data);
12
19
  };
@@ -15,42 +22,57 @@ const setPublishedAt = data => {
15
22
  *
16
23
  * Returns a collection type service to handle default core-api actions
17
24
  */
18
- const createCollectionTypeService = ({ model, strapi, utils }) => {
19
- const { uid } = model;
20
-
21
- const { sanitizeInput, getFetchParams } = utils;
25
+ const createCollectionTypeService = ({ contentType }) => {
26
+ const { uid } = contentType;
22
27
 
23
28
  return {
24
- find(opts = {}) {
25
- const params = getFetchParams(opts.params);
29
+ async find(params = {}) {
30
+ const fetchParams = this.getFetchParams(params);
26
31
 
27
- return strapi.entityService.findPage(uid, { params });
28
- },
32
+ const paginationInfo = getPaginationInfo(fetchParams);
33
+
34
+ const results = await strapi.entityService.findMany(uid, {
35
+ ...fetchParams,
36
+ ...convertPagedToStartLimit(paginationInfo),
37
+ });
29
38
 
30
- findOne(entityId, opts = {}) {
31
- const params = getFetchParams(opts.params);
39
+ if (shouldCount(fetchParams)) {
40
+ const count = await strapi.entityService.count(uid, { ...fetchParams, ...paginationInfo });
41
+
42
+ return {
43
+ results,
44
+ pagination: transformPaginationResponse(paginationInfo, count),
45
+ };
46
+ }
47
+
48
+ return {
49
+ results,
50
+ pagination: paginationInfo,
51
+ };
52
+ },
32
53
 
33
- return strapi.entityService.findOne(uid, entityId, { params });
54
+ findOne(entityId, params = {}) {
55
+ return strapi.entityService.findOne(uid, entityId, this.getFetchParams(params));
34
56
  },
35
57
 
36
- create({ params, data, files } = {}) {
37
- const sanitizedData = sanitizeInput(data);
58
+ create(params = {}) {
59
+ const { data } = params;
38
60
 
39
- if (hasDraftAndPublish(model)) {
40
- setPublishedAt(sanitizedData);
61
+ if (hasDraftAndPublish(contentType)) {
62
+ setPublishedAt(data);
41
63
  }
42
64
 
43
- return strapi.entityService.create(uid, { params, data: sanitizedData, files });
65
+ return strapi.entityService.create(uid, { ...params, data });
44
66
  },
45
67
 
46
- update(entityId, { params, data, files } = {}) {
47
- const sanitizedData = sanitizeInput(data);
68
+ update(entityId, params = {}) {
69
+ const { data } = params;
48
70
 
49
- return strapi.entityService.update(uid, entityId, { params, data: sanitizedData, files });
71
+ return strapi.entityService.update(uid, entityId, { ...params, data });
50
72
  },
51
73
 
52
- delete(entityId, { params } = {}) {
53
- return strapi.entityService.delete(uid, entityId, { params });
74
+ delete(entityId, params = {}) {
75
+ return strapi.entityService.delete(uid, entityId, params);
54
76
  },
55
77
  };
56
78
  };
@@ -0,0 +1,21 @@
1
+ type Entity = object;
2
+
3
+ interface BaseService {
4
+ getFetchParams(params: object): object;
5
+ }
6
+
7
+ export interface SingleTypeService extends BaseService {
8
+ find(params: object): Promise<Entity>;
9
+ createOrUpdate(params: object): Promise<Entity>;
10
+ delete(params: object): Promise<Entity>;
11
+ }
12
+
13
+ export interface CollectionTypeService extends BaseService {
14
+ find(params: object): Promise<Entity[]>;
15
+ findOne(params: object): Promise<Entity>;
16
+ create(params: object): Promise<Entity>;
17
+ update(params: object): Promise<Entity>;
18
+ delete(params: object): Promise<Entity>;
19
+ }
20
+
21
+ export type Service = SingleTypeService | CollectionTypeService;
@@ -1,10 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const _ = require('lodash');
4
-
5
3
  const {
6
4
  isSingleType,
7
- getNonWritableAttributes,
8
5
  constants: { DP_PUB_STATE_LIVE },
9
6
  } = require('@strapi/utils').contentTypes;
10
7
 
@@ -16,62 +13,18 @@ const createCollectionTypeService = require('./collection-type');
16
13
  * @param {{ model: object, strapi: object }} context
17
14
  * @returns {object}
18
15
  */
19
- const createService = ({ model, strapi }) => {
20
- const utils = createUtils({ model });
21
-
22
- if (isSingleType(model)) {
23
- return createSingleTypeService({ model, strapi, utils });
24
- }
16
+ const createService = ({ contentType }) => {
17
+ const proto = { getFetchParams };
25
18
 
26
- return createCollectionTypeService({ model, strapi, utils });
27
- };
28
-
29
- /**
30
- * Default limit values from config
31
- * @return {{maxLimit: number, defaultLimit: number}}
32
- */
33
- const getLimitConfigDefaults = () => ({
34
- defaultLimit: _.toNumber(strapi.config.get('api.rest.defaultLimit', 100)),
35
- maxLimit: _.toNumber(strapi.config.get('api.rest.maxLimit')) || null,
36
- });
19
+ let service;
37
20
 
38
- /**
39
- * if there is max limit set and limit exceeds this number, return configured max limit
40
- * @param {number} limit - limit you want to cap
41
- * @param {number?} maxLimit - maxlimit used has capping
42
- * @returns {number}
43
- */
44
- const applyMaxLimit = (limit, maxLimit) => {
45
- if (maxLimit && (limit === -1 || limit > maxLimit)) {
46
- return maxLimit;
21
+ if (isSingleType(contentType)) {
22
+ service = createSingleTypeService({ contentType });
23
+ } else {
24
+ service = createCollectionTypeService({ contentType });
47
25
  }
48
26
 
49
- return limit;
50
- };
51
-
52
- const applyDefaultPagination = params => {
53
- const { defaultLimit, maxLimit } = getLimitConfigDefaults();
54
-
55
- if (_.isUndefined(params.pagination) || !_.isPlainObject(params.pagination)) {
56
- return {
57
- limit: defaultLimit,
58
- };
59
- }
60
-
61
- const { pagination } = params;
62
-
63
- if (!_.isUndefined(pagination.pageSize)) {
64
- return {
65
- page: pagination.page,
66
- pageSize: applyMaxLimit(_.toNumber(pagination.pageSize), maxLimit),
67
- };
68
- }
69
-
70
- const limit = _.isUndefined(pagination.limit) ? defaultLimit : _.toNumber(pagination.limit);
71
- return {
72
- start: pagination.start,
73
- limit: applyMaxLimit(limit, maxLimit),
74
- };
27
+ return Object.assign(Object.create(proto), service);
75
28
  };
76
29
 
77
30
  /**
@@ -83,18 +36,6 @@ const getFetchParams = (params = {}) => {
83
36
  return {
84
37
  publicationState: DP_PUB_STATE_LIVE,
85
38
  ...params,
86
- pagination: applyDefaultPagination(params),
87
- };
88
- };
89
-
90
- /**
91
- * Mixins
92
- */
93
- const createUtils = ({ model }) => {
94
- return {
95
- // make sure to keep the call to getNonWritableAttributes dynamic
96
- sanitizeInput: data => _.omit(data, getNonWritableAttributes(model)),
97
- getFetchParams,
98
39
  };
99
40
  };
100
41
 
@@ -0,0 +1,130 @@
1
+ 'use strict';
2
+
3
+ const { has, toNumber, isUndefined } = require('lodash/fp');
4
+ const { ValidationError } = require('@strapi/utils').errors;
5
+
6
+ /**
7
+ * Default limit values from config
8
+ * @return {{maxLimit: number, defaultLimit: number}}
9
+ */
10
+ const getLimitConfigDefaults = () => ({
11
+ defaultLimit: toNumber(strapi.config.get('api.rest.defaultLimit', 25)),
12
+ maxLimit: toNumber(strapi.config.get('api.rest.maxLimit')) || null,
13
+ });
14
+
15
+ /**
16
+ * if there is max limit set and limit exceeds this number, return configured max limit
17
+ * @param {number} limit - limit you want to cap
18
+ * @param {number?} maxLimit - maxlimit used has capping
19
+ * @returns {number}
20
+ */
21
+ const applyMaxLimit = (limit, maxLimit) => {
22
+ if (maxLimit && (limit === -1 || limit > maxLimit)) {
23
+ return maxLimit;
24
+ }
25
+
26
+ return limit;
27
+ };
28
+
29
+ const shouldCount = params => {
30
+ if (has('pagination.withCount', params)) {
31
+ const { withCount } = params.pagination;
32
+
33
+ if (typeof withCount === 'boolean') {
34
+ return withCount;
35
+ }
36
+
37
+ if (['true', 't', '1', 1].includes(withCount)) {
38
+ return true;
39
+ }
40
+
41
+ if (['false', 'f', '0', 0].includes(withCount)) {
42
+ return false;
43
+ }
44
+
45
+ throw new ValidationError(
46
+ 'Invalid withCount parameter. Expected "t","1","true","false","0","f"'
47
+ );
48
+ }
49
+
50
+ return Boolean(strapi.config.get('api.rest.withCount', true));
51
+ };
52
+
53
+ const isOffsetPagination = pagination => has('start', pagination) || has('limit', pagination);
54
+ const isPagedPagination = pagination => has('page', pagination) || has('pageSize', pagination);
55
+
56
+ const getPaginationInfo = params => {
57
+ const { defaultLimit, maxLimit } = getLimitConfigDefaults();
58
+
59
+ const { pagination } = params;
60
+
61
+ const isPaged = isPagedPagination(pagination);
62
+ const isOffset = isOffsetPagination(pagination);
63
+
64
+ if (!isPaged && !isOffset) {
65
+ return {
66
+ page: 1,
67
+ pageSize: defaultLimit,
68
+ };
69
+ }
70
+
71
+ if (isOffset && isPaged) {
72
+ throw new ValidationError(
73
+ 'Invalid pagination parameters. Expected either start/limit or page/pageSize'
74
+ );
75
+ }
76
+
77
+ if (isPagedPagination(pagination)) {
78
+ const pageSize = isUndefined(pagination.pageSize)
79
+ ? defaultLimit
80
+ : Math.max(1, toNumber(pagination.pageSize));
81
+
82
+ return {
83
+ page: Math.max(1, toNumber(pagination.page || 1)),
84
+ pageSize: applyMaxLimit(pageSize, maxLimit),
85
+ };
86
+ }
87
+
88
+ const limit = isUndefined(pagination.limit)
89
+ ? defaultLimit
90
+ : Math.max(1, toNumber(pagination.limit));
91
+
92
+ return {
93
+ start: Math.max(0, toNumber(pagination.start || 0)),
94
+ limit: applyMaxLimit(limit, maxLimit),
95
+ };
96
+ };
97
+
98
+ const convertPagedToStartLimit = pagination => {
99
+ if (isPagedPagination(pagination)) {
100
+ const { page, pageSize } = pagination;
101
+ return {
102
+ start: (page - 1) * pageSize,
103
+ limit: pageSize,
104
+ };
105
+ }
106
+
107
+ return pagination;
108
+ };
109
+
110
+ const transformPaginationResponse = (paginationInfo, count) => {
111
+ if (paginationInfo.page) {
112
+ return {
113
+ ...paginationInfo,
114
+ pageCount: Math.ceil(count / paginationInfo.pageSize),
115
+ total: count,
116
+ };
117
+ }
118
+
119
+ return {
120
+ ...paginationInfo,
121
+ total: count,
122
+ };
123
+ };
124
+
125
+ module.exports = {
126
+ getPaginationInfo,
127
+ convertPagedToStartLimit,
128
+ transformPaginationResponse,
129
+ shouldCount,
130
+ };
@@ -1,20 +1,24 @@
1
1
  'use strict';
2
2
 
3
+ const { ValidationError } = require('@strapi/utils').errors;
4
+
3
5
  /**
4
6
  * Returns a single type service to handle default core-api actions
5
7
  */
6
- const createSingleTypeService = ({ model, strapi, utils }) => {
7
- const { uid } = model;
8
- const { sanitizeInput, getFetchParams } = utils;
8
+ const createSingleTypeService = ({ contentType }) => {
9
+ const { uid } = contentType;
9
10
 
11
+ /**
12
+ * @type {import('./').SingleTypeService}
13
+ */
10
14
  return {
11
15
  /**
12
16
  * Returns singleType content
13
17
  *
14
18
  * @return {Promise}
15
19
  */
16
- find({ params } = {}) {
17
- return strapi.entityService.find(uid, { params: getFetchParams(params) });
20
+ find(params = {}) {
21
+ return strapi.entityService.findMany(uid, this.getFetchParams(params));
18
22
  },
19
23
 
20
24
  /**
@@ -22,25 +26,19 @@ const createSingleTypeService = ({ model, strapi, utils }) => {
22
26
  *
23
27
  * @return {Promise}
24
28
  */
25
- async createOrUpdate({ params, data, files } = {}) {
26
- const entity = await this.find({ params });
27
-
28
- const sanitizedData = sanitizeInput(data);
29
+ async createOrUpdate({ data, ...params } = {}) {
30
+ const entity = await this.find(params);
29
31
 
30
32
  if (!entity) {
31
33
  const count = await strapi.query(uid).count();
32
34
  if (count >= 1) {
33
- throw strapi.errors.badRequest('singleType.alreadyExists');
35
+ throw new ValidationError('singleType.alreadyExists');
34
36
  }
35
37
 
36
- return strapi.entityService.create(uid, { params, data: sanitizedData, files });
37
- } else {
38
- return strapi.entityService.update(uid, entity.id, {
39
- params,
40
- data: sanitizedData,
41
- files,
42
- });
38
+ return strapi.entityService.create(uid, { ...params, data });
43
39
  }
40
+
41
+ return strapi.entityService.update(uid, entity.id, { ...params, data });
44
42
  },
45
43
 
46
44
  /**
@@ -48,8 +46,8 @@ const createSingleTypeService = ({ model, strapi, utils }) => {
48
46
  *
49
47
  * @return {Promise}
50
48
  */
51
- async delete({ params } = {}) {
52
- const entity = await this.find({ params });
49
+ async delete(params = {}) {
50
+ const entity = await this.find(params);
53
51
 
54
52
  if (!entity) return;
55
53