@strapi/strapi 4.0.0-next.8 → 4.0.2

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 (147) hide show
  1. package/README.md +14 -14
  2. package/bin/strapi.js +46 -60
  3. package/lib/Strapi.js +152 -74
  4. package/lib/commands/build.js +19 -8
  5. package/lib/commands/console.js +1 -1
  6. package/lib/commands/content-types/list.js +22 -0
  7. package/lib/commands/controllers/list.js +22 -0
  8. package/lib/commands/develop.js +22 -27
  9. package/lib/commands/generate-template.js +4 -5
  10. package/lib/commands/hooks/list.js +22 -0
  11. package/lib/commands/middlewares/list.js +22 -0
  12. package/lib/commands/new.js +3 -1
  13. package/lib/commands/policies/list.js +22 -0
  14. package/lib/commands/routes/list.js +28 -0
  15. package/lib/commands/services/list.js +22 -0
  16. package/lib/commands/watchAdmin.js +18 -9
  17. package/lib/core/app-configuration/index.js +3 -19
  18. package/lib/core/bootstrap.js +3 -95
  19. package/lib/core/domain/content-type/index.js +5 -11
  20. package/lib/core/domain/module/index.js +42 -11
  21. package/lib/core/domain/module/validation.js +16 -19
  22. package/lib/core/loaders/admin.js +2 -2
  23. package/lib/core/loaders/apis.js +148 -9
  24. package/lib/core/loaders/components.js +4 -6
  25. package/lib/core/loaders/index.js +1 -0
  26. package/lib/core/loaders/middlewares.js +23 -123
  27. package/lib/core/loaders/plugins/get-enabled-plugins.js +55 -19
  28. package/lib/core/loaders/plugins/get-user-plugins-config.js +37 -0
  29. package/lib/core/loaders/plugins/index.js +30 -16
  30. package/lib/core/loaders/policies.js +1 -1
  31. package/lib/core/loaders/src-index.js +39 -0
  32. package/lib/core/registries/apis.js +29 -0
  33. package/lib/core/registries/content-types.js +61 -12
  34. package/lib/core/registries/controllers.d.ts +7 -0
  35. package/lib/core/registries/controllers.js +91 -7
  36. package/lib/core/registries/hooks.d.ts +20 -0
  37. package/lib/core/registries/hooks.js +87 -0
  38. package/lib/core/registries/middlewares.d.ts +5 -0
  39. package/lib/core/registries/middlewares.js +64 -5
  40. package/lib/core/registries/modules.js +3 -3
  41. package/lib/core/registries/plugins.js +2 -2
  42. package/lib/core/registries/policies.d.ts +9 -0
  43. package/lib/core/registries/policies.js +64 -5
  44. package/lib/core/registries/services.d.ts +7 -0
  45. package/lib/core/registries/services.js +86 -17
  46. package/lib/core/utils.js +22 -0
  47. package/lib/core-api/controller/collection-type.js +45 -26
  48. package/lib/core-api/controller/index.d.ts +25 -0
  49. package/lib/core-api/controller/index.js +33 -11
  50. package/lib/core-api/controller/single-type.js +29 -15
  51. package/lib/core-api/controller/transform.js +62 -6
  52. package/lib/core-api/routes/index.js +71 -0
  53. package/lib/core-api/service/collection-type.js +43 -21
  54. package/lib/core-api/service/index.d.ts +21 -0
  55. package/lib/core-api/service/index.js +8 -67
  56. package/lib/core-api/service/pagination.js +125 -0
  57. package/lib/core-api/service/single-type.js +17 -19
  58. package/lib/factories.d.ts +48 -0
  59. package/lib/factories.js +84 -0
  60. package/lib/index.d.ts +10 -31
  61. package/lib/index.js +5 -1
  62. package/lib/middlewares/body.js +33 -0
  63. package/lib/middlewares/compression.js +8 -0
  64. package/lib/middlewares/cors.js +58 -0
  65. package/lib/middlewares/errors.js +40 -0
  66. package/lib/middlewares/favicon.js +19 -0
  67. package/lib/middlewares/index.d.ts +5 -0
  68. package/lib/middlewares/index.js +30 -116
  69. package/lib/middlewares/ip.js +8 -0
  70. package/lib/middlewares/logger.js +27 -0
  71. package/lib/middlewares/powered-by.js +20 -0
  72. package/lib/middlewares/public/index.js +72 -77
  73. package/lib/middlewares/query.js +46 -0
  74. package/lib/middlewares/response-time.js +15 -0
  75. package/lib/middlewares/responses.js +19 -0
  76. package/lib/middlewares/security.js +51 -0
  77. package/lib/middlewares/session/index.js +6 -6
  78. package/lib/migrations/draft-publish.js +57 -0
  79. package/lib/services/auth/index.js +87 -0
  80. package/lib/services/core-store.js +64 -49
  81. package/lib/services/cron.js +54 -0
  82. package/lib/services/entity-service/attributes/index.js +31 -0
  83. package/lib/services/entity-service/attributes/transforms.js +20 -0
  84. package/lib/services/entity-service/components.js +39 -15
  85. package/lib/services/entity-service/index.d.ts +91 -0
  86. package/lib/services/entity-service/index.js +118 -60
  87. package/lib/services/entity-service/params.js +52 -94
  88. package/lib/services/entity-validator/index.js +76 -43
  89. package/lib/services/entity-validator/validators.js +131 -43
  90. package/lib/services/errors.js +77 -0
  91. package/lib/services/fs.js +1 -1
  92. package/lib/services/metrics/index.js +38 -36
  93. package/lib/services/server/admin-api.js +14 -0
  94. package/lib/services/server/api.js +36 -0
  95. package/lib/services/server/compose-endpoint.js +141 -0
  96. package/lib/services/server/content-api.js +16 -0
  97. package/lib/{server.js → services/server/http-server.js} +0 -0
  98. package/lib/services/server/index.js +127 -0
  99. package/lib/services/server/koa.js +64 -0
  100. package/lib/services/server/middleware.js +122 -0
  101. package/lib/services/server/policy.js +32 -0
  102. package/lib/services/server/register-middlewares.js +110 -0
  103. package/lib/services/server/register-routes.js +106 -0
  104. package/lib/services/server/routing.js +120 -0
  105. package/lib/services/webhook-runner.js +1 -1
  106. package/lib/utils/ee.js +3 -3
  107. package/lib/utils/get-dirs.js +17 -0
  108. package/lib/utils/index.js +2 -0
  109. package/lib/utils/signals.js +24 -0
  110. package/lib/utils/update-notifier/index.js +2 -1
  111. package/package.json +94 -97
  112. package/lib/commands/generate.js +0 -76
  113. package/lib/core/app-configuration/load-functions.js +0 -28
  114. package/lib/core-api/index.js +0 -39
  115. package/lib/load/check-reserved-filename.js +0 -10
  116. package/lib/load/load-config-files.js +0 -22
  117. package/lib/load/require-file-parse.js +0 -15
  118. package/lib/middlewares/boom/defaults.json +0 -5
  119. package/lib/middlewares/boom/index.js +0 -147
  120. package/lib/middlewares/cors/index.js +0 -66
  121. package/lib/middlewares/cron/defaults.json +0 -5
  122. package/lib/middlewares/cron/index.js +0 -43
  123. package/lib/middlewares/favicon/defaults.json +0 -7
  124. package/lib/middlewares/favicon/index.js +0 -32
  125. package/lib/middlewares/gzip/defaults.json +0 -6
  126. package/lib/middlewares/gzip/index.js +0 -19
  127. package/lib/middlewares/helmet/defaults.json +0 -18
  128. package/lib/middlewares/helmet/index.js +0 -9
  129. package/lib/middlewares/ip/defaults.json +0 -7
  130. package/lib/middlewares/ip/index.js +0 -25
  131. package/lib/middlewares/language/defaults.json +0 -9
  132. package/lib/middlewares/language/index.js +0 -40
  133. package/lib/middlewares/logger/defaults.json +0 -5
  134. package/lib/middlewares/logger/index.js +0 -37
  135. package/lib/middlewares/parser/defaults.json +0 -11
  136. package/lib/middlewares/parser/index.js +0 -71
  137. package/lib/middlewares/poweredBy/defaults.json +0 -5
  138. package/lib/middlewares/poweredBy/index.js +0 -16
  139. package/lib/middlewares/public/defaults.json +0 -8
  140. package/lib/middlewares/responseTime/defaults.json +0 -5
  141. package/lib/middlewares/responseTime/index.js +0 -25
  142. package/lib/middlewares/responses/defaults.json +0 -5
  143. package/lib/middlewares/responses/index.js +0 -18
  144. package/lib/middlewares/router/defaults.json +0 -7
  145. package/lib/middlewares/router/index.js +0 -58
  146. package/lib/middlewares/router/utils/composeEndpoint.js +0 -177
  147. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -1,12 +1,18 @@
1
1
  'use strict';
2
2
 
3
+ const _ = require('lodash');
3
4
  const delegate = require('delegates');
4
5
  const {
5
- sanitizeEntity,
6
+ InvalidTimeError,
7
+ InvalidDateError,
8
+ InvalidDateTimeError,
9
+ } = require('@strapi/database').errors;
10
+ const {
6
11
  webhook: webhookUtils,
7
12
  contentTypes: contentTypesUtils,
8
- relations: relationsUtils,
13
+ sanitize,
9
14
  } = require('@strapi/utils');
15
+ const { ValidationError } = require('@strapi/utils').errors;
10
16
  const uploadFiles = require('../utils/upload-files');
11
17
 
12
18
  const {
@@ -16,12 +22,21 @@ const {
16
22
  deleteComponents,
17
23
  } = require('./components');
18
24
  const { transformParamsToQuery, pickSelectionParams } = require('./params');
19
-
20
- const { MANY_RELATIONS } = relationsUtils.constants;
25
+ const { applyTransforms } = require('./attributes');
21
26
 
22
27
  // TODO: those should be strapi events used by the webhooks not the other way arround
23
28
  const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
24
29
 
30
+ const databaseErrorsToTransform = [InvalidTimeError, InvalidDateTimeError, InvalidDateError];
31
+
32
+ const creationPipeline = (data, context) => {
33
+ return applyTransforms(data, context);
34
+ };
35
+
36
+ const updatePipeline = (data, context) => {
37
+ return applyTransforms(data, context);
38
+ };
39
+
25
40
  module.exports = ctx => {
26
41
  const implementation = createDefaultImplementation(ctx);
27
42
 
@@ -42,32 +57,57 @@ module.exports = ctx => {
42
57
  // delegate every method in implementation
43
58
  Object.keys(service.implementation).forEach(key => delegator.method(key));
44
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
+
45
82
  return service;
46
83
  };
47
84
 
85
+ /**
86
+ * @type {import('.').default}
87
+ */
48
88
  const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) => ({
49
89
  uploadFiles,
50
90
 
51
- async wrapOptions(options = {}) {
91
+ async wrapParams(options = {}) {
52
92
  return options;
53
93
  },
54
94
 
55
- emitEvent(uid, event, entity) {
95
+ async emitEvent(uid, event, entity) {
56
96
  const model = strapi.getModel(uid);
97
+ const sanitizedEntity = await sanitize.sanitizers.defaultSanitizeOutput(model, entity);
57
98
 
58
99
  eventHub.emit(event, {
59
100
  model: model.modelName,
60
- entry: sanitizeEntity(entity, { model }),
101
+ entry: sanitizedEntity,
61
102
  });
62
103
  },
63
104
 
64
- // TODO: rename to findMany
65
- async find(uid, opts) {
105
+ async findMany(uid, opts) {
66
106
  const { kind } = strapi.getModel(uid);
67
107
 
68
- const { params } = await this.wrapOptions(opts, { uid, action: 'find' });
108
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findMany' });
69
109
 
70
- const query = transformParamsToQuery(uid, params);
110
+ const query = transformParamsToQuery(uid, wrappedParams);
71
111
 
72
112
  if (kind === 'singleType') {
73
113
  return db.query(uid).findOne(query);
@@ -77,42 +117,20 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
77
117
  },
78
118
 
79
119
  async findPage(uid, opts) {
80
- const { params } = await this.wrapOptions(opts, { uid, action: 'findPage' });
120
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findPage' });
81
121
 
82
- const query = transformParamsToQuery(uid, params);
122
+ const query = transformParamsToQuery(uid, wrappedParams);
83
123
 
84
124
  return db.query(uid).findPage(query);
85
125
  },
86
126
 
87
127
  // TODO: streamline the logic based on the populate option
88
128
  async findWithRelationCounts(uid, opts) {
89
- const model = strapi.getModel(uid);
90
-
91
- const { params } = await this.wrapOptions(opts, { uid, action: 'findWithRelationCounts' });
129
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findWithRelationCounts' });
92
130
 
93
- const query = transformParamsToQuery(uid, params);
131
+ const query = transformParamsToQuery(uid, wrappedParams);
94
132
 
95
- const { attributes } = model;
96
-
97
- const populate = (query.populate || []).reduce((populate, attributeName) => {
98
- const attribute = attributes[attributeName];
99
-
100
- if (
101
- MANY_RELATIONS.includes(attribute.relation) &&
102
- contentTypesUtils.isVisibleAttribute(model, attributeName)
103
- ) {
104
- populate[attributeName] = { count: true };
105
- } else {
106
- populate[attributeName] = true;
107
- }
108
-
109
- return populate;
110
- }, {});
111
-
112
- const { results, pagination } = await db.query(uid).findPage({
113
- ...query,
114
- populate,
115
- });
133
+ const { results, pagination } = await db.query(uid).findPage(query);
116
134
 
117
135
  return {
118
136
  results,
@@ -121,23 +139,24 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
121
139
  },
122
140
 
123
141
  async findOne(uid, entityId, opts) {
124
- const { params } = await this.wrapOptions(opts, { uid, action: 'findOne' });
142
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findOne' });
125
143
 
126
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
144
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
127
145
 
128
146
  return db.query(uid).findOne({ ...query, where: { id: entityId } });
129
147
  },
130
148
 
131
149
  async count(uid, opts) {
132
- const { params } = await this.wrapOptions(opts, { uid, action: 'count' });
150
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'count' });
133
151
 
134
- const query = transformParamsToQuery(uid, params);
152
+ const query = transformParamsToQuery(uid, wrappedParams);
135
153
 
136
154
  return db.query(uid).count(query);
137
155
  },
138
156
 
139
157
  async create(uid, opts) {
140
- const { params, data, files } = await this.wrapOptions(opts, { uid, action: 'create' });
158
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'create' });
159
+ const { data, files } = wrappedParams;
141
160
 
142
161
  const model = strapi.getModel(uid);
143
162
 
@@ -145,30 +164,33 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
145
164
  const validData = await entityValidator.validateEntityCreation(model, data, { isDraft });
146
165
 
147
166
  // select / populate
148
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
167
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
149
168
 
150
169
  // TODO: wrap into transaction
151
170
  const componentData = await createComponents(uid, validData);
152
171
 
153
172
  let entity = await db.query(uid).create({
154
173
  ...query,
155
- data: Object.assign(omitComponentData(model, validData), componentData),
174
+ data: creationPipeline(Object.assign(omitComponentData(model, validData), componentData), {
175
+ contentType: model,
176
+ }),
156
177
  });
157
178
 
158
179
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
159
180
  // FIXME: upload in components
160
181
  if (files && Object.keys(files).length > 0) {
161
182
  await this.uploadFiles(uid, entity, files);
162
- entity = await this.findOne(uid, entity.id, { params });
183
+ entity = await this.findOne(uid, entity.id, wrappedParams);
163
184
  }
164
185
 
165
- this.emitEvent(uid, ENTRY_CREATE, entity);
186
+ await this.emitEvent(uid, ENTRY_CREATE, entity);
166
187
 
167
188
  return entity;
168
189
  },
169
190
 
170
191
  async update(uid, entityId, opts) {
171
- const { params, data, files } = await this.wrapOptions(opts, { uid, action: 'update' });
192
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'update' });
193
+ const { data, files } = wrappedParams;
172
194
 
173
195
  const model = strapi.getModel(uid);
174
196
 
@@ -180,11 +202,16 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
180
202
 
181
203
  const isDraft = contentTypesUtils.isDraft(entityToUpdate, model);
182
204
 
183
- const validData = await entityValidator.validateEntityUpdate(model, data, {
184
- isDraft,
185
- });
205
+ const validData = await entityValidator.validateEntityUpdate(
206
+ model,
207
+ data,
208
+ {
209
+ isDraft,
210
+ },
211
+ entityToUpdate
212
+ );
186
213
 
187
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
214
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
188
215
 
189
216
  // TODO: wrap in transaction
190
217
  const componentData = await updateComponents(uid, entityToUpdate, validData);
@@ -192,26 +219,28 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
192
219
  let entity = await db.query(uid).update({
193
220
  ...query,
194
221
  where: { id: entityId },
195
- data: Object.assign(omitComponentData(model, validData), componentData),
222
+ data: updatePipeline(Object.assign(omitComponentData(model, validData), componentData), {
223
+ contentType: model,
224
+ }),
196
225
  });
197
226
 
198
227
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
199
228
  // FIXME: upload in components
200
229
  if (files && Object.keys(files).length > 0) {
201
230
  await this.uploadFiles(uid, entity, files);
202
- entity = await this.findOne(uid, entity.id, { params });
231
+ entity = await this.findOne(uid, entity.id, wrappedParams);
203
232
  }
204
233
 
205
- this.emitEvent(uid, ENTRY_UPDATE, entity);
234
+ await this.emitEvent(uid, ENTRY_UPDATE, entity);
206
235
 
207
236
  return entity;
208
237
  },
209
238
 
210
239
  async delete(uid, entityId, opts) {
211
- const { params } = await this.wrapOptions(opts, { uid, action: 'delete' });
240
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
212
241
 
213
242
  // select / populate
214
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
243
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
215
244
 
216
245
  const entityToDelete = await db.query(uid).findOne({
217
246
  ...query,
@@ -225,18 +254,47 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
225
254
  await deleteComponents(uid, entityToDelete);
226
255
  await db.query(uid).delete({ where: { id: entityToDelete.id } });
227
256
 
228
- this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
257
+ await this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
229
258
 
230
259
  return entityToDelete;
231
260
  },
232
261
 
233
262
  // FIXME: used only for the CM to be removed
234
263
  async deleteMany(uid, opts) {
235
- const { params } = await this.wrapOptions(opts, { uid, action: 'delete' });
264
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
236
265
 
237
266
  // select / populate
238
- const query = transformParamsToQuery(uid, params);
267
+ const query = transformParamsToQuery(uid, wrappedParams);
239
268
 
240
269
  return db.query(uid).deleteMany(query);
241
270
  },
271
+
272
+ load(uid, entity, field, params = {}) {
273
+ const { attributes } = strapi.getModel(uid);
274
+
275
+ const attribute = attributes[field];
276
+
277
+ const loadParams = {};
278
+
279
+ switch (attribute.type) {
280
+ case 'relation': {
281
+ Object.assign(loadParams, transformParamsToQuery(attribute.target, params));
282
+ break;
283
+ }
284
+ case 'component': {
285
+ Object.assign(loadParams, transformParamsToQuery(attribute.component, params));
286
+ break;
287
+ }
288
+ case 'dynamiczone': {
289
+ Object.assign(loadParams, transformParamsToQuery(null, params));
290
+ break;
291
+ }
292
+ case 'media': {
293
+ Object.assign(loadParams, transformParamsToQuery('plugin::upload.file', params));
294
+ break;
295
+ }
296
+ }
297
+
298
+ return db.query(uid).load(entity, field, loadParams);
299
+ },
242
300
  });
@@ -1,136 +1,94 @@
1
1
  'use strict';
2
2
 
3
- const _ = require('lodash');
4
- const { pick } = require('lodash/fp');
3
+ const { pick, isNil, toNumber, isInteger } = require('lodash/fp');
4
+ const { PaginationError } = require('@strapi/utils').errors;
5
5
 
6
6
  const {
7
7
  convertSortQueryParams,
8
8
  convertLimitQueryParams,
9
9
  convertStartQueryParams,
10
- } = require('@strapi/utils/lib/convert-rest-query-params');
10
+ convertPopulateQueryParams,
11
+ convertFiltersQueryParams,
12
+ convertFieldsQueryParams,
13
+ convertPublicationStateParams,
14
+ } = require('@strapi/utils/lib/convert-query-params');
11
15
 
12
- const { contentTypes: contentTypesUtils } = require('@strapi/utils');
13
-
14
- const { PUBLISHED_AT_ATTRIBUTE } = contentTypesUtils.constants;
16
+ const pickSelectionParams = pick(['fields', 'populate']);
15
17
 
16
- const transformParamsToQuery = (uid, params = {}) => {
17
- const model = strapi.getModel(uid);
18
+ const transformParamsToQuery = (uid, params) => {
19
+ // NOTE: can be a CT, a Compo or nothing in the case of polymorphism (DZ & morph relations)
20
+ const type = strapi.getModel(uid);
18
21
 
19
22
  const query = {};
20
23
 
21
- // TODO: check invalid values add defaults ....
22
-
23
- const {
24
- start,
25
- page,
26
- pageSize,
27
- limit,
28
- sort,
29
- filters,
30
- fields,
31
- populate,
32
- publicationState,
33
- _q,
34
- _where,
35
- ...rest
36
- } = params;
37
-
38
- if (_q) {
39
- query._q = _q;
40
- }
24
+ const { _q, sort, filters, fields, populate, page, pageSize, start, limit } = params;
41
25
 
42
- if (page) {
43
- query.page = Number(page);
44
- }
45
-
46
- if (pageSize) {
47
- query.pageSize = Number(pageSize);
26
+ if (!isNil(_q)) {
27
+ query._q = _q;
48
28
  }
49
29
 
50
- if (start) {
51
- query.offset = convertStartQueryParams(start);
30
+ if (!isNil(sort)) {
31
+ query.orderBy = convertSortQueryParams(sort);
52
32
  }
53
33
 
54
- if (limit) {
55
- query.limit = convertLimitQueryParams(limit);
34
+ if (!isNil(filters)) {
35
+ query.where = convertFiltersQueryParams(filters);
56
36
  }
57
37
 
58
- if (sort) {
59
- query.orderBy = convertSortQueryParams(sort);
38
+ if (!isNil(fields)) {
39
+ query.select = convertFieldsQueryParams(fields);
60
40
  }
61
41
 
62
- if (filters) {
63
- query.where = filters;
42
+ if (!isNil(populate)) {
43
+ query.populate = convertPopulateQueryParams(populate);
64
44
  }
65
45
 
66
- if (_where) {
67
- query.where = {
68
- $and: [_where].concat(query.where || []),
69
- };
70
- }
46
+ const isPagePagination = !isNil(page) || !isNil(pageSize);
47
+ const isOffsetPagination = !isNil(start) || !isNil(limit);
71
48
 
72
- if (fields) {
73
- // TODO: handle *.* syntax
74
- query.select = _.castArray(fields);
49
+ if (isPagePagination && isOffsetPagination) {
50
+ throw new PaginationError(
51
+ 'Invalid pagination attributes. You cannot use page and offset pagination in the same query'
52
+ );
75
53
  }
76
54
 
77
- if (populate) {
78
- // TODO: handle *.* syntax
79
- const { populate } = params;
55
+ if (!isNil(page)) {
56
+ const pageVal = toNumber(page);
80
57
 
81
- if (populate === '*') {
82
- query.populate = true;
83
- } else if (typeof populate === 'object') {
84
- query.populate = populate;
85
- } else {
86
- query.populate = _.castArray(populate);
58
+ if (!isInteger(pageVal) || pageVal <= 0) {
59
+ throw new PaginationError(
60
+ `Invalid 'page' parameter. Expected an integer > 0, received: ${page}`
61
+ );
87
62
  }
88
- }
89
63
 
90
- // TODO: move to layer above ?
91
- if (publicationState && contentTypesUtils.hasDraftAndPublish(model)) {
92
- const { publicationState = 'live' } = params;
93
-
94
- const liveClause = {
95
- [PUBLISHED_AT_ATTRIBUTE]: {
96
- $notNull: true,
97
- },
98
- };
64
+ query.page = pageVal;
65
+ }
99
66
 
100
- if (publicationState === 'live') {
101
- query.where = {
102
- $and: [liveClause].concat(query.where || []),
103
- };
67
+ if (!isNil(pageSize)) {
68
+ const pageSizeVal = toNumber(pageSize);
104
69
 
105
- // TODO: propagate nested publicationState filter somehow
70
+ if (!isInteger(pageSizeVal) || pageSizeVal <= 0) {
71
+ throw new PaginationError(
72
+ `Invalid 'pageSize' parameter. Expected an integer > 0, received: ${page}`
73
+ );
106
74
  }
107
- }
108
75
 
109
- const finalQuery = {
110
- ...convertOldQuery(rest),
111
- ...query,
112
- };
76
+ query.pageSize = pageSizeVal;
77
+ }
113
78
 
114
- return finalQuery;
115
- };
79
+ if (!isNil(start)) {
80
+ query.offset = convertStartQueryParams(start);
81
+ }
116
82
 
117
- // TODO: to remove once the front is migrated
118
- const convertOldQuery = params => {
119
- const obj = {};
83
+ if (!isNil(limit)) {
84
+ query.limit = convertLimitQueryParams(limit);
85
+ }
120
86
 
121
- Object.keys(params).forEach(key => {
122
- if (key.startsWith('_')) {
123
- obj[key.slice(1)] = params[key];
124
- } else {
125
- obj[key] = params[key];
126
- }
127
- });
87
+ convertPublicationStateParams(type, params, query);
128
88
 
129
- return obj;
89
+ return query;
130
90
  };
131
91
 
132
- const pickSelectionParams = pick(['fields', 'populate']);
133
-
134
92
  module.exports = {
135
93
  transformParamsToQuery,
136
94
  pickSelectionParams,