@strapi/strapi 4.0.0-next.7 → 4.0.1

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 (157) hide show
  1. package/README.md +14 -14
  2. package/bin/strapi.js +46 -60
  3. package/lib/Strapi.js +193 -98
  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 -36
  18. package/lib/core/bootstrap.js +25 -0
  19. package/lib/core/domain/content-type/index.js +26 -29
  20. package/lib/core/domain/content-type/validator.js +22 -3
  21. package/lib/core/domain/module/index.js +42 -11
  22. package/lib/core/domain/module/validation.js +16 -19
  23. package/lib/core/loaders/admin.js +16 -0
  24. package/lib/core/loaders/apis.js +159 -0
  25. package/lib/core/loaders/{load-components.js → components.js} +5 -6
  26. package/lib/core/loaders/index.js +11 -0
  27. package/lib/core/loaders/middlewares.js +36 -0
  28. package/lib/core/{load-plugins → loaders/plugins}/get-enabled-plugins.js +55 -19
  29. package/lib/core/loaders/plugins/get-user-plugins-config.js +37 -0
  30. package/lib/core/{load-plugins → loaders/plugins}/index.js +35 -19
  31. package/lib/core/loaders/policies.js +28 -0
  32. package/lib/core/loaders/src-index.js +39 -0
  33. package/lib/core/registries/apis.js +29 -0
  34. package/lib/core/registries/content-types.js +66 -10
  35. package/lib/core/registries/controllers.d.ts +7 -0
  36. package/lib/core/registries/controllers.js +92 -7
  37. package/lib/core/registries/hooks.d.ts +20 -0
  38. package/lib/core/registries/hooks.js +87 -0
  39. package/lib/core/registries/middlewares.d.ts +5 -0
  40. package/lib/core/registries/middlewares.js +65 -5
  41. package/lib/core/registries/modules.js +3 -3
  42. package/lib/core/registries/plugins.js +2 -2
  43. package/lib/core/registries/policies.d.ts +9 -0
  44. package/lib/core/registries/policies.js +65 -5
  45. package/lib/core/registries/services.d.ts +7 -0
  46. package/lib/core/registries/services.js +88 -17
  47. package/lib/core/utils.js +35 -0
  48. package/lib/core-api/controller/collection-type.js +45 -26
  49. package/lib/core-api/controller/index.d.ts +25 -0
  50. package/lib/core-api/controller/index.js +33 -11
  51. package/lib/core-api/controller/single-type.js +29 -15
  52. package/lib/core-api/controller/transform.js +62 -6
  53. package/lib/core-api/routes/index.js +71 -0
  54. package/lib/core-api/service/collection-type.js +43 -21
  55. package/lib/core-api/service/index.d.ts +21 -0
  56. package/lib/core-api/service/index.js +8 -67
  57. package/lib/core-api/service/pagination.js +125 -0
  58. package/lib/core-api/service/single-type.js +17 -19
  59. package/lib/factories.d.ts +48 -0
  60. package/lib/factories.js +84 -0
  61. package/lib/index.d.ts +10 -31
  62. package/lib/index.js +5 -1
  63. package/lib/middlewares/body.js +33 -0
  64. package/lib/middlewares/compression.js +8 -0
  65. package/lib/middlewares/cors.js +58 -0
  66. package/lib/middlewares/errors.js +40 -0
  67. package/lib/middlewares/favicon.js +19 -0
  68. package/lib/middlewares/index.d.ts +5 -0
  69. package/lib/middlewares/index.js +30 -116
  70. package/lib/middlewares/ip.js +8 -0
  71. package/lib/middlewares/logger.js +27 -0
  72. package/lib/middlewares/powered-by.js +20 -0
  73. package/lib/middlewares/public/index.js +72 -77
  74. package/lib/middlewares/query.js +46 -0
  75. package/lib/middlewares/response-time.js +15 -0
  76. package/lib/middlewares/responses.js +19 -0
  77. package/lib/middlewares/security.js +51 -0
  78. package/lib/middlewares/session/index.js +6 -6
  79. package/lib/migrations/draft-publish.js +57 -0
  80. package/lib/services/auth/index.js +87 -0
  81. package/lib/services/core-store.js +64 -51
  82. package/lib/services/cron.js +54 -0
  83. package/lib/services/entity-service/attributes/index.js +31 -0
  84. package/lib/services/entity-service/attributes/transforms.js +20 -0
  85. package/lib/services/entity-service/components.js +39 -15
  86. package/lib/services/entity-service/index.d.ts +91 -0
  87. package/lib/services/entity-service/index.js +120 -59
  88. package/lib/services/entity-service/params.js +52 -94
  89. package/lib/services/entity-validator/index.js +76 -43
  90. package/lib/services/entity-validator/validators.js +131 -43
  91. package/lib/services/errors.js +77 -0
  92. package/lib/{core → services}/fs.js +1 -1
  93. package/lib/services/metrics/index.js +38 -36
  94. package/lib/services/server/admin-api.js +14 -0
  95. package/lib/services/server/api.js +36 -0
  96. package/lib/services/server/compose-endpoint.js +141 -0
  97. package/lib/services/server/content-api.js +16 -0
  98. package/lib/{server.js → services/server/http-server.js} +0 -0
  99. package/lib/services/server/index.js +127 -0
  100. package/lib/services/server/koa.js +64 -0
  101. package/lib/services/server/middleware.js +122 -0
  102. package/lib/services/server/policy.js +32 -0
  103. package/lib/services/server/register-middlewares.js +110 -0
  104. package/lib/services/server/register-routes.js +106 -0
  105. package/lib/services/server/routing.js +120 -0
  106. package/lib/services/webhook-runner.js +1 -1
  107. package/lib/utils/ee.js +3 -3
  108. package/lib/utils/get-dirs.js +17 -0
  109. package/lib/utils/index.js +2 -0
  110. package/lib/utils/is-initialized.js +1 -1
  111. package/lib/utils/signals.js +24 -0
  112. package/lib/utils/startup-logger.js +2 -2
  113. package/lib/utils/update-notifier/index.js +3 -2
  114. package/package.json +94 -97
  115. package/lib/commands/generate.js +0 -76
  116. package/lib/core/loaders/bootstrap.js +0 -176
  117. package/lib/core/loaders/load-apis.js +0 -20
  118. package/lib/core/loaders/load-functions.js +0 -21
  119. package/lib/core/loaders/load-middlewares.js +0 -136
  120. package/lib/core/loaders/load-modules.js +0 -21
  121. package/lib/core/loaders/load-policies.js +0 -36
  122. package/lib/core/loaders/walk.js +0 -27
  123. package/lib/core-api/index.js +0 -39
  124. package/lib/load/check-reserved-filename.js +0 -10
  125. package/lib/load/load-config-files.js +0 -22
  126. package/lib/load/require-file-parse.js +0 -15
  127. package/lib/middlewares/boom/defaults.json +0 -5
  128. package/lib/middlewares/boom/index.js +0 -147
  129. package/lib/middlewares/cors/index.js +0 -66
  130. package/lib/middlewares/cron/defaults.json +0 -5
  131. package/lib/middlewares/cron/index.js +0 -43
  132. package/lib/middlewares/favicon/defaults.json +0 -7
  133. package/lib/middlewares/favicon/index.js +0 -32
  134. package/lib/middlewares/gzip/defaults.json +0 -6
  135. package/lib/middlewares/gzip/index.js +0 -19
  136. package/lib/middlewares/helmet/defaults.json +0 -18
  137. package/lib/middlewares/helmet/index.js +0 -9
  138. package/lib/middlewares/ip/defaults.json +0 -7
  139. package/lib/middlewares/ip/index.js +0 -25
  140. package/lib/middlewares/language/defaults.json +0 -9
  141. package/lib/middlewares/language/index.js +0 -40
  142. package/lib/middlewares/logger/defaults.json +0 -5
  143. package/lib/middlewares/logger/index.js +0 -37
  144. package/lib/middlewares/parser/defaults.json +0 -11
  145. package/lib/middlewares/parser/index.js +0 -71
  146. package/lib/middlewares/poweredBy/defaults.json +0 -5
  147. package/lib/middlewares/poweredBy/index.js +0 -16
  148. package/lib/middlewares/public/defaults.json +0 -8
  149. package/lib/middlewares/responseTime/defaults.json +0 -5
  150. package/lib/middlewares/responseTime/index.js +0 -25
  151. package/lib/middlewares/responses/defaults.json +0 -5
  152. package/lib/middlewares/responses/index.js +0 -18
  153. package/lib/middlewares/router/defaults.json +0 -7
  154. package/lib/middlewares/router/index.js +0 -56
  155. package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
  156. package/lib/middlewares/router/utils/routerChecker.js +0 -96
  157. 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,31 +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
- async find(uid, opts) {
105
+ async findMany(uid, opts) {
65
106
  const { kind } = strapi.getModel(uid);
66
107
 
67
- const { params } = await this.wrapOptions(opts, { uid, action: 'find' });
108
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findMany' });
68
109
 
69
- const query = transformParamsToQuery(uid, params);
110
+ const query = transformParamsToQuery(uid, wrappedParams);
70
111
 
71
112
  if (kind === 'singleType') {
72
113
  return db.query(uid).findOne(query);
@@ -76,41 +117,20 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
76
117
  },
77
118
 
78
119
  async findPage(uid, opts) {
79
- const { params } = await this.wrapOptions(opts, { uid, action: 'findPage' });
120
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findPage' });
80
121
 
81
- const query = transformParamsToQuery(uid, params);
122
+ const query = transformParamsToQuery(uid, wrappedParams);
82
123
 
83
124
  return db.query(uid).findPage(query);
84
125
  },
85
126
 
127
+ // TODO: streamline the logic based on the populate option
86
128
  async findWithRelationCounts(uid, opts) {
87
- const model = strapi.getModel(uid);
88
-
89
- const { params } = await this.wrapOptions(opts, { uid, action: 'findWithRelationCounts' });
129
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findWithRelationCounts' });
90
130
 
91
- const query = transformParamsToQuery(uid, params);
131
+ const query = transformParamsToQuery(uid, wrappedParams);
92
132
 
93
- const { attributes } = model;
94
-
95
- const populate = (query.populate || []).reduce((populate, attributeName) => {
96
- const attribute = attributes[attributeName];
97
-
98
- if (
99
- MANY_RELATIONS.includes(attribute.relation) &&
100
- contentTypesUtils.isVisibleAttribute(model, attributeName)
101
- ) {
102
- populate[attributeName] = { count: true };
103
- } else {
104
- populate[attributeName] = true;
105
- }
106
-
107
- return populate;
108
- }, {});
109
-
110
- const { results, pagination } = await db.query(uid).findPage({
111
- ...query,
112
- populate,
113
- });
133
+ const { results, pagination } = await db.query(uid).findPage(query);
114
134
 
115
135
  return {
116
136
  results,
@@ -119,23 +139,24 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
119
139
  },
120
140
 
121
141
  async findOne(uid, entityId, opts) {
122
- const { params } = await this.wrapOptions(opts, { uid, action: 'findOne' });
142
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'findOne' });
123
143
 
124
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
144
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
125
145
 
126
146
  return db.query(uid).findOne({ ...query, where: { id: entityId } });
127
147
  },
128
148
 
129
149
  async count(uid, opts) {
130
- const { params } = await this.wrapOptions(opts, { uid, action: 'count' });
150
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'count' });
131
151
 
132
- const query = transformParamsToQuery(uid, params);
152
+ const query = transformParamsToQuery(uid, wrappedParams);
133
153
 
134
154
  return db.query(uid).count(query);
135
155
  },
136
156
 
137
157
  async create(uid, opts) {
138
- 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;
139
160
 
140
161
  const model = strapi.getModel(uid);
141
162
 
@@ -143,30 +164,33 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
143
164
  const validData = await entityValidator.validateEntityCreation(model, data, { isDraft });
144
165
 
145
166
  // select / populate
146
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
167
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
147
168
 
148
169
  // TODO: wrap into transaction
149
170
  const componentData = await createComponents(uid, validData);
150
171
 
151
172
  let entity = await db.query(uid).create({
152
173
  ...query,
153
- data: Object.assign(omitComponentData(model, validData), componentData),
174
+ data: creationPipeline(Object.assign(omitComponentData(model, validData), componentData), {
175
+ contentType: model,
176
+ }),
154
177
  });
155
178
 
156
179
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
157
180
  // FIXME: upload in components
158
181
  if (files && Object.keys(files).length > 0) {
159
182
  await this.uploadFiles(uid, entity, files);
160
- entity = await this.findOne(uid, entity.id, { params });
183
+ entity = await this.findOne(uid, entity.id, wrappedParams);
161
184
  }
162
185
 
163
- this.emitEvent(uid, ENTRY_CREATE, entity);
186
+ await this.emitEvent(uid, ENTRY_CREATE, entity);
164
187
 
165
188
  return entity;
166
189
  },
167
190
 
168
191
  async update(uid, entityId, opts) {
169
- 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;
170
194
 
171
195
  const model = strapi.getModel(uid);
172
196
 
@@ -178,11 +202,16 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
178
202
 
179
203
  const isDraft = contentTypesUtils.isDraft(entityToUpdate, model);
180
204
 
181
- const validData = await entityValidator.validateEntityUpdate(model, data, {
182
- isDraft,
183
- });
205
+ const validData = await entityValidator.validateEntityUpdate(
206
+ model,
207
+ data,
208
+ {
209
+ isDraft,
210
+ },
211
+ entityToUpdate
212
+ );
184
213
 
185
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
214
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
186
215
 
187
216
  // TODO: wrap in transaction
188
217
  const componentData = await updateComponents(uid, entityToUpdate, validData);
@@ -190,26 +219,28 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
190
219
  let entity = await db.query(uid).update({
191
220
  ...query,
192
221
  where: { id: entityId },
193
- data: Object.assign(omitComponentData(model, validData), componentData),
222
+ data: updatePipeline(Object.assign(omitComponentData(model, validData), componentData), {
223
+ contentType: model,
224
+ }),
194
225
  });
195
226
 
196
227
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
197
228
  // FIXME: upload in components
198
229
  if (files && Object.keys(files).length > 0) {
199
230
  await this.uploadFiles(uid, entity, files);
200
- entity = await this.findOne(uid, entity.id, { params });
231
+ entity = await this.findOne(uid, entity.id, wrappedParams);
201
232
  }
202
233
 
203
- this.emitEvent(uid, ENTRY_UPDATE, entity);
234
+ await this.emitEvent(uid, ENTRY_UPDATE, entity);
204
235
 
205
236
  return entity;
206
237
  },
207
238
 
208
239
  async delete(uid, entityId, opts) {
209
- const { params } = await this.wrapOptions(opts, { uid, action: 'delete' });
240
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
210
241
 
211
242
  // select / populate
212
- const query = transformParamsToQuery(uid, pickSelectionParams(params));
243
+ const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
213
244
 
214
245
  const entityToDelete = await db.query(uid).findOne({
215
246
  ...query,
@@ -223,17 +254,47 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
223
254
  await deleteComponents(uid, entityToDelete);
224
255
  await db.query(uid).delete({ where: { id: entityToDelete.id } });
225
256
 
226
- this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
257
+ await this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
227
258
 
228
259
  return entityToDelete;
229
260
  },
230
261
 
262
+ // FIXME: used only for the CM to be removed
231
263
  async deleteMany(uid, opts) {
232
- const { params } = await this.wrapOptions(opts, { uid, action: 'delete' });
264
+ const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
233
265
 
234
266
  // select / populate
235
- const query = transformParamsToQuery(uid, params);
267
+ const query = transformParamsToQuery(uid, wrappedParams);
236
268
 
237
269
  return db.query(uid).deleteMany(query);
238
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
+ },
239
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,