@webiny/api-headless-cms 0.0.0-unstable.c2780f51fe → 0.0.0-unstable.c7dec08bb0

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/constants.d.ts +1 -0
  2. package/constants.js +8 -0
  3. package/constants.js.map +1 -0
  4. package/context.js +37 -5
  5. package/context.js.map +1 -1
  6. package/crud/contentEntry/referenceFieldsMapping.js +34 -5
  7. package/crud/contentEntry/referenceFieldsMapping.js.map +1 -1
  8. package/crud/contentEntry.crud.d.ts +8 -4
  9. package/crud/contentEntry.crud.js +329 -64
  10. package/crud/contentEntry.crud.js.map +1 -1
  11. package/crud/contentModel/validateModelFields.js +1 -1
  12. package/crud/contentModel/validateModelFields.js.map +1 -1
  13. package/crud/contentModel/validation.d.ts +76 -76
  14. package/crud/contentModel.crud.d.ts +2 -0
  15. package/crud/contentModel.crud.js +22 -12
  16. package/crud/contentModel.crud.js.map +1 -1
  17. package/crud/contentModelGroup/validation.d.ts +4 -4
  18. package/crud/contentModelGroup.crud.d.ts +2 -0
  19. package/crud/contentModelGroup.crud.js +42 -27
  20. package/crud/contentModelGroup.crud.js.map +1 -1
  21. package/crud/settings.crud.d.ts +2 -0
  22. package/crud/settings.crud.js +2 -6
  23. package/crud/settings.crud.js.map +1 -1
  24. package/graphql/getSchema.js +1 -1
  25. package/graphql/getSchema.js.map +1 -1
  26. package/graphql/index.d.ts +1 -1
  27. package/graphql/schema/baseSchema.js +31 -0
  28. package/graphql/schema/baseSchema.js.map +1 -1
  29. package/graphql/schema/contentEntries.js +6 -1
  30. package/graphql/schema/contentEntries.js.map +1 -1
  31. package/graphql/schema/createFieldResolvers.d.ts +1 -1
  32. package/graphql/schema/createFieldResolvers.js +6 -12
  33. package/graphql/schema/createFieldResolvers.js.map +1 -1
  34. package/graphql/schema/createFieldTypePluginRecords.d.ts +3 -0
  35. package/graphql/schema/createFieldTypePluginRecords.js +13 -0
  36. package/graphql/schema/createFieldTypePluginRecords.js.map +1 -0
  37. package/graphql/schema/createManageResolvers.d.ts +1 -1
  38. package/graphql/schema/createManageResolvers.js +14 -0
  39. package/graphql/schema/createManageResolvers.js.map +1 -1
  40. package/graphql/schema/createManageSDL.js +51 -28
  41. package/graphql/schema/createManageSDL.js.map +1 -1
  42. package/graphql/schema/createReadSDL.js +23 -19
  43. package/graphql/schema/createReadSDL.js.map +1 -1
  44. package/graphql/schema/resolvers/manage/resolveDelete.d.ts +2 -1
  45. package/graphql/schema/resolvers/manage/resolveDelete.js +13 -3
  46. package/graphql/schema/resolvers/manage/resolveDelete.js.map +1 -1
  47. package/graphql/schema/resolvers/manage/resolveDeleteMultiple.d.ts +7 -0
  48. package/graphql/schema/resolvers/manage/resolveDeleteMultiple.js +20 -0
  49. package/graphql/schema/resolvers/manage/resolveDeleteMultiple.js.map +1 -0
  50. package/graphql/schema/resolvers/manage/resolveGetUniqueFieldValues.d.ts +4 -0
  51. package/graphql/schema/resolvers/manage/resolveGetUniqueFieldValues.js +18 -0
  52. package/graphql/schema/resolvers/manage/resolveGetUniqueFieldValues.js.map +1 -0
  53. package/graphql/schema/resolvers/manage/resolveMove.d.ts +8 -0
  54. package/graphql/schema/resolvers/manage/resolveMove.js +30 -0
  55. package/graphql/schema/resolvers/manage/resolveMove.js.map +1 -0
  56. package/graphql/schema/schemaPlugins.js +2 -11
  57. package/graphql/schema/schemaPlugins.js.map +1 -1
  58. package/graphql/system.d.ts +2 -5
  59. package/graphql/system.js +1 -11
  60. package/graphql/system.js.map +1 -1
  61. package/graphqlFields/dynamicZone/dynamicZoneField.js +43 -28
  62. package/graphqlFields/dynamicZone/dynamicZoneField.js.map +1 -1
  63. package/graphqlFields/number.js +1 -0
  64. package/graphqlFields/number.js.map +1 -1
  65. package/graphqlFields/object.js +2 -2
  66. package/graphqlFields/object.js.map +1 -1
  67. package/graphqlFields/text.js +2 -0
  68. package/graphqlFields/text.js.map +1 -1
  69. package/index.d.ts +1 -1
  70. package/package.json +31 -35
  71. package/plugins/CmsModelPlugin.d.ts +3 -1
  72. package/plugins/CmsModelPlugin.js.map +1 -1
  73. package/types.d.ts +101 -17
  74. package/types.js +11 -0
  75. package/types.js.map +1 -1
  76. package/utils/converters/valueKeyStorageConverter.js +5 -2
  77. package/utils/converters/valueKeyStorageConverter.js.map +1 -1
  78. package/utils/createTypeFromFields.js +1 -1
  79. package/utils/createTypeFromFields.js.map +1 -1
  80. package/utils/getBaseFieldType.d.ts +1 -3
  81. package/utils/getBaseFieldType.js.map +1 -1
  82. package/utils/getEntryDescription.d.ts +1 -1
  83. package/utils/getEntryDescription.js.map +1 -1
  84. package/utils/getEntryImage.d.ts +1 -1
  85. package/utils/getEntryImage.js.map +1 -1
  86. package/utils/getEntryTitle.d.ts +1 -1
  87. package/utils/getEntryTitle.js.map +1 -1
  88. package/utils/permissions/EntriesPermissions.d.ts +4 -0
  89. package/utils/permissions/EntriesPermissions.js +9 -0
  90. package/utils/permissions/EntriesPermissions.js.map +1 -0
  91. package/utils/permissions/ModelGroupsPermissions.d.ts +11 -0
  92. package/utils/permissions/ModelGroupsPermissions.js +48 -0
  93. package/utils/permissions/ModelGroupsPermissions.js.map +1 -0
  94. package/utils/permissions/ModelsPermissions.d.ts +20 -0
  95. package/utils/permissions/ModelsPermissions.js +91 -0
  96. package/utils/permissions/ModelsPermissions.js.map +1 -0
  97. package/utils/permissions/SettingsPermissions.d.ts +4 -0
  98. package/utils/permissions/SettingsPermissions.js +9 -0
  99. package/utils/permissions/SettingsPermissions.js.map +1 -0
  100. package/utils/renderFields.d.ts +2 -1
  101. package/utils/renderFields.js +2 -1
  102. package/utils/renderFields.js.map +1 -1
  103. package/utils/renderGetFilterFields.d.ts +2 -2
  104. package/utils/renderGetFilterFields.js +7 -20
  105. package/utils/renderGetFilterFields.js.map +1 -1
  106. package/utils/renderInputFields.d.ts +2 -1
  107. package/utils/renderInputFields.js +14 -6
  108. package/utils/renderInputFields.js.map +1 -1
  109. package/utils/renderListFilterFields.d.ts +3 -1
  110. package/utils/renderListFilterFields.js +16 -21
  111. package/utils/renderListFilterFields.js.map +1 -1
  112. package/utils/renderSortEnum.d.ts +3 -2
  113. package/utils/renderSortEnum.js +5 -1
  114. package/utils/renderSortEnum.js.map +1 -1
  115. package/crud/contentModel/createFieldModels.d.ts +0 -2
  116. package/crud/contentModel/createFieldModels.js +0 -26
  117. package/crud/contentModel/createFieldModels.js.map +0 -1
  118. package/crud/contentModel/fieldIdValidation.d.ts +0 -1
  119. package/crud/contentModel/fieldIdValidation.js +0 -25
  120. package/crud/contentModel/fieldIdValidation.js.map +0 -1
  121. package/crud/contentModel/idValidation.d.ts +0 -1
  122. package/crud/contentModel/idValidation.js +0 -22
  123. package/crud/contentModel/idValidation.js.map +0 -1
  124. package/crud/contentModel/models.d.ts +0 -4
  125. package/crud/contentModel/models.js +0 -192
  126. package/crud/contentModel/models.js.map +0 -1
  127. package/crud/contentModel/systemFields.d.ts +0 -1
  128. package/crud/contentModel/systemFields.js +0 -8
  129. package/crud/contentModel/systemFields.js.map +0 -1
  130. package/upgrades/5.33.0/index.d.ts +0 -3
  131. package/upgrades/5.33.0/index.js +0 -182
  132. package/upgrades/5.33.0/index.js.map +0 -1
  133. package/upgrades/index.d.ts +0 -1
  134. package/upgrades/index.js +0 -12
  135. package/upgrades/index.js.map +0 -1
  136. package/utils/access.d.ts +0 -8
  137. package/utils/access.js +0 -76
  138. package/utils/access.js.map +0 -1
  139. package/utils/ownership.d.ts +0 -8
  140. package/utils/ownership.js +0 -33
  141. package/utils/ownership.js.map +0 -1
  142. package/utils/permissions.d.ts +0 -7
  143. package/utils/permissions.js +0 -91
  144. package/utils/permissions.js.map +0 -1
  145. package/utils/pluralizedTypeName.d.ts +0 -1
  146. package/utils/pluralizedTypeName.js +0 -26
  147. package/utils/pluralizedTypeName.js.map +0 -1
@@ -7,31 +7,26 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.createContentEntryCrud = exports.STATUS_UNPUBLISHED = exports.STATUS_PUBLISHED = exports.STATUS_DRAFT = void 0;
8
8
  var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
9
9
  var _merge = _interopRequireDefault(require("lodash/merge"));
10
- var _mdbid = _interopRequireDefault(require("mdbid"));
10
+ var _utils = require("@webiny/utils");
11
11
  var _error = _interopRequireDefault(require("@webiny/error"));
12
12
  var _handlerGraphql = require("@webiny/handler-graphql");
13
+ var _types = require("../types");
13
14
  var _entryDataValidation = require("./contentEntry/entryDataValidation");
14
15
  var _pubsub = require("@webiny/pubsub");
15
16
  var _beforeCreate = require("./contentEntry/beforeCreate");
16
17
  var _beforeUpdate = require("./contentEntry/beforeUpdate");
17
- var _utils = require("@webiny/utils");
18
18
  var _afterDelete = require("./contentEntry/afterDelete");
19
19
  var _referenceFieldsMapping = require("./contentEntry/referenceFieldsMapping");
20
- var _permissions = require("../utils/permissions");
21
- var _access = require("../utils/access");
22
- var _ownership = require("../utils/ownership");
23
20
  var _entryStorage = require("../utils/entryStorage");
24
21
  var _searchableFields = require("./contentEntry/searchableFields");
25
- /**
26
- * Package mdbid does not have types.
27
- */
28
- // @ts-ignore
29
-
30
- const STATUS_DRAFT = "draft";
22
+ var _filterAsync = require("../utils/filterAsync");
23
+ var _apiSecurity = require("@webiny/api-security/");
24
+ var _constants = require("../constants");
25
+ const STATUS_DRAFT = _types.CONTENT_ENTRY_STATUS.DRAFT;
31
26
  exports.STATUS_DRAFT = STATUS_DRAFT;
32
- const STATUS_PUBLISHED = "published";
27
+ const STATUS_PUBLISHED = _types.CONTENT_ENTRY_STATUS.PUBLISHED;
33
28
  exports.STATUS_PUBLISHED = STATUS_PUBLISHED;
34
- const STATUS_UNPUBLISHED = "unpublished";
29
+ const STATUS_UNPUBLISHED = _types.CONTENT_ENTRY_STATUS.UNPUBLISHED;
35
30
  exports.STATUS_UNPUBLISHED = STATUS_UNPUBLISHED;
36
31
  /**
37
32
  * Used for some fields to convert their values.
@@ -133,7 +128,7 @@ const createEntryMeta = (input, original) => {
133
128
  return (0, _utils.removeUndefinedValues)((0, _utils.removeNullValues)(meta));
134
129
  };
135
130
  const createEntryId = input => {
136
- let entryId = (0, _mdbid.default)();
131
+ let entryId = (0, _utils.mdbid)();
137
132
  if (input.id) {
138
133
  if (input.id.match(/^([a-zA-Z0-9])([a-zA-Z0-9\-]+)([a-zA-Z0-9])$/) === null) {
139
134
  throw new _error.default("The provided ID is not valid. It must be a string which can be A-Z, a-z, 0-9, - and it cannot start or end with a -.", "INVALID_ID", {
@@ -175,9 +170,19 @@ const allowedEntryStatus = ["draft", "published", "unpublished"];
175
170
  const transformEntryStatus = status => {
176
171
  return allowedEntryStatus.includes(status) ? status : "draft";
177
172
  };
173
+ const createSort = sort => {
174
+ if (!Array.isArray(sort)) {
175
+ return ["createdOn_DESC"];
176
+ } else if (sort.filter(s => !!s).length === 0) {
177
+ return ["createdOn_DESC"];
178
+ }
179
+ return sort;
180
+ };
178
181
  const createContentEntryCrud = params => {
179
182
  const {
180
183
  storageOperations,
184
+ entriesPermissions,
185
+ modelsPermissions,
181
186
  context,
182
187
  getIdentity,
183
188
  getTenant,
@@ -247,6 +252,12 @@ const createContentEntryCrud = params => {
247
252
  const onEntryRevisionBeforeDelete = (0, _pubsub.createTopic)("cms.onEntryRevisionBeforeDelete");
248
253
  const onEntryRevisionAfterDelete = (0, _pubsub.createTopic)("cms.onEntryRevisionAfterDelete");
249
254
  const onEntryRevisionDeleteError = (0, _pubsub.createTopic)("cms.onEntryRevisionDeleteError");
255
+ /**
256
+ * Delete multiple entries
257
+ */
258
+ const onEntryBeforeDeleteMultiple = (0, _pubsub.createTopic)("cms.onEntryBeforeDeleteMultiple");
259
+ const onEntryAfterDeleteMultiple = (0, _pubsub.createTopic)("cms.onEntryAfterDeleteMultiple");
260
+ const onEntryDeleteMultipleError = (0, _pubsub.createTopic)("cms.onEntryDeleteMultipleError");
250
261
 
251
262
  /**
252
263
  * Get entry
@@ -273,9 +284,6 @@ const createContentEntryCrud = params => {
273
284
  context,
274
285
  onEntryAfterDelete
275
286
  });
276
- const checkEntryPermissions = check => {
277
- return (0, _permissions.checkPermissions)(context, "cms.contentEntry", check);
278
- };
279
287
 
280
288
  /**
281
289
  * A helper to delete the entire entry.
@@ -315,14 +323,23 @@ const createContentEntryCrud = params => {
315
323
  */
316
324
  const getEntriesByIds = async (model, ids) => {
317
325
  return context.benchmark.measure("headlessCms.crud.entries.getEntriesByIds", async () => {
318
- const permission = await checkEntryPermissions({
326
+ await entriesPermissions.ensure({
319
327
  rwd: "r"
320
328
  });
321
- await (0, _access.checkModelAccess)(context, model);
329
+ await modelsPermissions.ensureCanAccessModel({
330
+ model,
331
+ locale: getLocale().code
332
+ });
322
333
  const entries = await storageOperations.entries.getByIds(model, {
323
334
  ids
324
335
  });
325
- return entries.filter(entry => (0, _ownership.validateOwnership)(context, permission, entry));
336
+ return (0, _filterAsync.filterAsync)(entries, async entry => {
337
+ return entriesPermissions.ensure({
338
+ owns: entry.createdBy
339
+ }, {
340
+ throw: false
341
+ });
342
+ });
326
343
  });
327
344
  };
328
345
  const getEntryById = async (model, id) => {
@@ -340,27 +357,45 @@ const createContentEntryCrud = params => {
340
357
  return entry;
341
358
  };
342
359
  const getPublishedEntriesByIds = async (model, ids) => {
343
- const permission = await checkEntryPermissions({
360
+ await entriesPermissions.ensure({
344
361
  rwd: "r"
345
362
  });
346
- await (0, _access.checkModelAccess)(context, model);
363
+ await modelsPermissions.ensureCanAccessModel({
364
+ model,
365
+ locale: getLocale().code
366
+ });
347
367
  const entries = await storageOperations.entries.getPublishedByIds(model, {
348
368
  ids
349
369
  });
350
- return entries.filter(entry => (0, _ownership.validateOwnership)(context, permission, entry));
370
+ return (0, _filterAsync.filterAsync)(entries, async entry => {
371
+ return entriesPermissions.ensure({
372
+ owns: entry.createdBy
373
+ }, {
374
+ throw: false
375
+ });
376
+ });
351
377
  };
352
378
  const getLatestEntriesByIds = async (model, ids) => {
353
- const permission = await checkEntryPermissions({
379
+ await entriesPermissions.ensure({
354
380
  rwd: "r"
355
381
  });
356
- await (0, _access.checkModelAccess)(context, model);
382
+ await modelsPermissions.ensureCanAccessModel({
383
+ model,
384
+ locale: getLocale().code
385
+ });
357
386
  const entries = await storageOperations.entries.getLatestByIds(model, {
358
387
  ids
359
388
  });
360
- return entries.filter(entry => (0, _ownership.validateOwnership)(context, permission, entry));
389
+ return (0, _filterAsync.filterAsync)(entries, async entry => {
390
+ return entriesPermissions.ensure({
391
+ owns: entry.createdBy
392
+ }, {
393
+ throw: false
394
+ });
395
+ });
361
396
  };
362
397
  const getEntry = async (model, params) => {
363
- await checkEntryPermissions({
398
+ await entriesPermissions.ensure({
364
399
  rwd: "r"
365
400
  });
366
401
  const {
@@ -388,27 +423,35 @@ const createContentEntryCrud = params => {
388
423
  });
389
424
  };
390
425
  const listEntries = async (model, params) => {
391
- const permission = await checkEntryPermissions({
392
- rwd: "r"
426
+ try {
427
+ await entriesPermissions.ensure({
428
+ rwd: "r"
429
+ });
430
+ } catch {
431
+ throw new _apiSecurity.NotAuthorizedError({
432
+ data: {
433
+ reason: 'Not allowed to perform "read" on "cms.contentEntry".'
434
+ }
435
+ });
436
+ }
437
+ await modelsPermissions.ensureCanAccessModel({
438
+ model,
439
+ locale: getLocale().code
393
440
  });
394
- await (0, _access.checkModelAccess)(context, model);
395
441
  const {
396
442
  where: initialWhere,
397
443
  limit: initialLimit
398
444
  } = params;
399
445
  const limit = initialLimit && initialLimit > 0 ? initialLimit : 50;
400
- /**
401
- * We always assign tenant and locale because we do not allow one model to have content through multiple tenants.
402
- */
403
446
  const where = (0, _objectSpread2.default)({}, initialWhere);
404
447
  /**
405
448
  * Possibly only get records which are owned by current user.
406
449
  * Or if searching for the owner set that value - in the case that user can see other entries than their own.
407
450
  */
408
- const ownedBy = permission.own ? getIdentity().id : where.ownedBy;
409
- if (ownedBy !== undefined) {
410
- where.ownedBy = ownedBy;
451
+ if (await entriesPermissions.canAccessOnlyOwnRecords()) {
452
+ where.ownedBy = getIdentity().id;
411
453
  }
454
+
412
455
  /**
413
456
  * Where must contain either latest or published keys.
414
457
  * We cannot list entries without one of those
@@ -438,6 +481,7 @@ const createContentEntryCrud = params => {
438
481
  cursor,
439
482
  items
440
483
  } = await storageOperations.entries.list(model, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, params), {}, {
484
+ sort: createSort(params.sort),
441
485
  limit,
442
486
  where,
443
487
  fields
@@ -466,10 +510,14 @@ const createContentEntryCrud = params => {
466
510
  }
467
511
  };
468
512
  const createEntry = async (model, inputData) => {
469
- await checkEntryPermissions({
513
+ var _inputData$wbyAco_loc;
514
+ await entriesPermissions.ensure({
470
515
  rwd: "w"
471
516
  });
472
- await (0, _access.checkModelAccess)(context, model);
517
+ await modelsPermissions.ensureCanAccessModel({
518
+ model,
519
+ locale: getLocale().code
520
+ });
473
521
 
474
522
  /**
475
523
  * Make sure we only work with fields that are defined in the model.
@@ -512,7 +560,10 @@ const createContentEntryCrud = params => {
512
560
  version,
513
561
  locked: false,
514
562
  status: STATUS_DRAFT,
515
- values: input
563
+ values: input,
564
+ location: {
565
+ folderId: ((_inputData$wbyAco_loc = inputData.wbyAco_location) === null || _inputData$wbyAco_loc === void 0 ? void 0 : _inputData$wbyAco_loc.folderId) || _constants.ROOT_FOLDER
566
+ }
516
567
  };
517
568
  let storageEntry = null;
518
569
  try {
@@ -549,10 +600,13 @@ const createContentEntryCrud = params => {
549
600
  }
550
601
  };
551
602
  const createEntryRevisionFrom = async (model, sourceId, inputData) => {
552
- const permission = await checkEntryPermissions({
603
+ await entriesPermissions.ensure({
553
604
  rwd: "w"
554
605
  });
555
- await (0, _access.checkModelAccess)(context, model);
606
+ await modelsPermissions.ensureCanAccessModel({
607
+ model,
608
+ locale: getLocale().code
609
+ });
556
610
 
557
611
  /**
558
612
  * Make sure we only work with fields that are defined in the model.
@@ -592,7 +646,9 @@ const createContentEntryCrud = params => {
592
646
  input: initialValues,
593
647
  validateEntries: false
594
648
  });
595
- (0, _ownership.checkOwnership)(context, permission, originalEntry);
649
+ await entriesPermissions.ensure({
650
+ owns: originalEntry.createdBy
651
+ });
596
652
  const identity = getIdentity();
597
653
  const latestId = latestStorageEntry ? latestStorageEntry.id : sourceId;
598
654
  const {
@@ -654,10 +710,14 @@ const createContentEntryCrud = params => {
654
710
  }
655
711
  };
656
712
  const updateEntry = async (model, id, inputData, metaInput) => {
657
- const permission = await checkEntryPermissions({
713
+ var _inputData$wbyAco_loc2;
714
+ await entriesPermissions.ensure({
658
715
  rwd: "w"
659
716
  });
660
- await (0, _access.checkModelAccess)(context, model);
717
+ await modelsPermissions.ensureCanAccessModel({
718
+ model,
719
+ locale: getLocale().code
720
+ });
661
721
 
662
722
  /**
663
723
  * Make sure we only work with fields that are defined in the model.
@@ -683,7 +743,9 @@ const createContentEntryCrud = params => {
683
743
  data: input,
684
744
  entry: originalEntry
685
745
  });
686
- (0, _ownership.checkOwnership)(context, permission, originalEntry);
746
+ await entriesPermissions.ensure({
747
+ owns: originalEntry.createdBy
748
+ });
687
749
  const initialValues = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, originalEntry.values), input);
688
750
  const values = await (0, _referenceFieldsMapping.referenceFieldsMapping)({
689
751
  context,
@@ -705,6 +767,12 @@ const createContentEntryCrud = params => {
705
767
  meta,
706
768
  status: transformEntryStatus(originalEntry.status)
707
769
  });
770
+ const folderId = (_inputData$wbyAco_loc2 = inputData.wbyAco_location) === null || _inputData$wbyAco_loc2 === void 0 ? void 0 : _inputData$wbyAco_loc2.folderId;
771
+ if (folderId) {
772
+ entry.location = {
773
+ folderId
774
+ };
775
+ }
708
776
  let storageEntry = null;
709
777
  try {
710
778
  await onEntryBeforeUpdate.publish({
@@ -743,10 +811,14 @@ const createContentEntryCrud = params => {
743
811
  }
744
812
  };
745
813
  const republishEntry = async (model, id) => {
746
- await checkEntryPermissions({
814
+ await entriesPermissions.ensure({
747
815
  rwd: "w"
748
816
  });
749
- await (0, _access.checkModelAccess)(context, model);
817
+ await modelsPermissions.ensureCanAccessModel({
818
+ model,
819
+ locale: getLocale().code
820
+ });
821
+
750
822
  /**
751
823
  * Fetch the entry from the storage.
752
824
  */
@@ -821,10 +893,13 @@ const createContentEntryCrud = params => {
821
893
  }
822
894
  };
823
895
  const deleteEntryRevision = async (model, revisionId) => {
824
- const permission = await checkEntryPermissions({
896
+ await entriesPermissions.ensure({
825
897
  rwd: "d"
826
898
  });
827
- await (0, _access.checkModelAccess)(context, model);
899
+ await modelsPermissions.ensureCanAccessModel({
900
+ model,
901
+ locale: getLocale().code
902
+ });
828
903
  const {
829
904
  id: entryId,
830
905
  version
@@ -842,7 +917,9 @@ const createContentEntryCrud = params => {
842
917
  if (!storageEntryToDelete) {
843
918
  throw new _handlerGraphql.NotFoundError(`Entry "${revisionId}" was not found!`);
844
919
  }
845
- (0, _ownership.checkOwnership)(context, permission, storageEntryToDelete);
920
+ await entriesPermissions.ensure({
921
+ owns: storageEntryToDelete.createdBy
922
+ });
846
923
  const latestEntryRevisionId = latestStorageEntry ? latestStorageEntry.id : null;
847
924
  const entryToDelete = await (0, _entryStorage.entryFromStorageTransform)(context, model, storageEntryToDelete);
848
925
  /**
@@ -894,18 +971,124 @@ const createContentEntryCrud = params => {
894
971
  });
895
972
  }
896
973
  };
897
- const deleteEntry = async (model, entryId) => {
898
- const permission = await checkEntryPermissions({
974
+ const deleteMultipleEntries = async (model, params) => {
975
+ const {
976
+ entries: input
977
+ } = params;
978
+ const maxDeletableEntries = 50;
979
+ const entryIdList = new Set();
980
+ for (const id of input) {
981
+ const {
982
+ id: entryId
983
+ } = (0, _utils.parseIdentifier)(id);
984
+ entryIdList.add(entryId);
985
+ }
986
+ const ids = Array.from(entryIdList);
987
+ if (ids.length > maxDeletableEntries) {
988
+ throw new _error.default("Cannot delete more than 50 entries at once.", "DELETE_ENTRIES_MAX", {
989
+ entries: ids
990
+ });
991
+ }
992
+ await entriesPermissions.ensure({
993
+ rwd: "d"
994
+ });
995
+ await modelsPermissions.ensureCanAccessModel({
996
+ model,
997
+ locale: getLocale().code
998
+ });
999
+ const {
1000
+ items: entries
1001
+ } = await storageOperations.entries.list(model, {
1002
+ where: {
1003
+ latest: true,
1004
+ entryId_in: ids
1005
+ },
1006
+ limit: maxDeletableEntries + 1
1007
+ });
1008
+ /**
1009
+ * We do not want to allow deleting entries that user does not own or cannot access.
1010
+ */
1011
+ const items = (await (0, _filterAsync.filterAsync)(entries, async entry => {
1012
+ return entriesPermissions.ensure({
1013
+ owns: entry.createdBy
1014
+ }, {
1015
+ throw: false
1016
+ });
1017
+ })).map(entry => entry.id);
1018
+ try {
1019
+ await onEntryBeforeDeleteMultiple.publish({
1020
+ entries,
1021
+ ids,
1022
+ model
1023
+ });
1024
+ await storageOperations.entries.deleteMultipleEntries(model, {
1025
+ entries: items
1026
+ });
1027
+ await onEntryAfterDeleteMultiple.publish({
1028
+ entries,
1029
+ ids,
1030
+ model
1031
+ });
1032
+ return items.map(id => {
1033
+ return {
1034
+ id
1035
+ };
1036
+ });
1037
+ } catch (ex) {
1038
+ await onEntryDeleteMultipleError.publish({
1039
+ entries,
1040
+ ids,
1041
+ model,
1042
+ error: ex
1043
+ });
1044
+ throw new _error.default(ex.message, ex.code || "DELETE_ENTRIES_MULTIPLE_ERROR", {
1045
+ error: ex,
1046
+ entries
1047
+ });
1048
+ }
1049
+ };
1050
+ const deleteEntry = async (model, id, options) => {
1051
+ await entriesPermissions.ensure({
899
1052
  rwd: "d"
900
1053
  });
901
- await (0, _access.checkModelAccess)(context, model);
1054
+ await modelsPermissions.ensureCanAccessModel({
1055
+ model,
1056
+ locale: getLocale().code
1057
+ });
1058
+ const {
1059
+ force
1060
+ } = options || {};
902
1061
  const storageEntry = await storageOperations.entries.getLatestRevisionByEntryId(model, {
903
- id: entryId
1062
+ id
904
1063
  });
905
- if (!storageEntry) {
906
- throw new _handlerGraphql.NotFoundError(`Entry "${entryId}" was not found!`);
1064
+ /**
1065
+ * If there is no entry, and we do not force the deletion, just throw an error.
1066
+ */
1067
+ if (!storageEntry && !force) {
1068
+ throw new _handlerGraphql.NotFoundError(`Entry "${id}" was not found!`);
1069
+ }
1070
+ /**
1071
+ * In the case we are forcing the deletion, we do not need the storageEntry to exist as it might be an error when loading single database record.
1072
+ *
1073
+ * This happens, sometimes, in the Elasticsearch system as the entry might get deleted from the DynamoDB but not from the Elasticsearch.
1074
+ * This is due to high load on the Elasticsearch at the time of the deletion.
1075
+ */
1076
+ //
1077
+ else if (!storageEntry && force) {
1078
+ const {
1079
+ id: entryId
1080
+ } = (0, _utils.parseIdentifier)(id);
1081
+ return await deleteEntryHelper({
1082
+ model,
1083
+ entry: {
1084
+ id,
1085
+ entryId
1086
+ }
1087
+ });
907
1088
  }
908
- (0, _ownership.checkOwnership)(context, permission, storageEntry);
1089
+ await entriesPermissions.ensure({
1090
+ owns: storageEntry.createdBy
1091
+ });
909
1092
  const entry = await (0, _entryStorage.entryFromStorageTransform)(context, model, storageEntry);
910
1093
  return await deleteEntryHelper({
911
1094
  model,
@@ -913,17 +1096,22 @@ const createContentEntryCrud = params => {
913
1096
  });
914
1097
  };
915
1098
  const publishEntry = async (model, id) => {
916
- const permission = await checkEntryPermissions({
1099
+ await entriesPermissions.ensure({
917
1100
  pw: "p"
918
1101
  });
919
- await (0, _access.checkModelAccess)(context, model);
1102
+ await modelsPermissions.ensureCanAccessModel({
1103
+ model,
1104
+ locale: getLocale().code
1105
+ });
920
1106
  const originalStorageEntry = await storageOperations.entries.getRevisionById(model, {
921
1107
  id
922
1108
  });
923
1109
  if (!originalStorageEntry) {
924
1110
  throw new _handlerGraphql.NotFoundError(`Entry "${id}" in the model "${model.modelId}" was not found.`);
925
1111
  }
926
- (0, _ownership.checkOwnership)(context, permission, originalStorageEntry);
1112
+ await entriesPermissions.ensure({
1113
+ owns: originalStorageEntry.createdBy
1114
+ });
927
1115
  const originalEntry = await (0, _entryStorage.entryFromStorageTransform)(context, model, originalStorageEntry);
928
1116
  const currentDate = new Date().toISOString();
929
1117
  const entry = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, originalEntry), {}, {
@@ -965,7 +1153,7 @@ const createContentEntryCrud = params => {
965
1153
  }
966
1154
  };
967
1155
  const unpublishEntry = async (model, id) => {
968
- const permission = await checkEntryPermissions({
1156
+ await entriesPermissions.ensure({
969
1157
  pw: "u"
970
1158
  });
971
1159
  const {
@@ -982,7 +1170,9 @@ const createContentEntryCrud = params => {
982
1170
  entry: originalStorageEntry
983
1171
  });
984
1172
  }
985
- (0, _ownership.checkOwnership)(context, permission, originalStorageEntry);
1173
+ await entriesPermissions.ensure({
1174
+ owns: originalStorageEntry.createdBy
1175
+ });
986
1176
  const originalEntry = await (0, _entryStorage.entryFromStorageTransform)(context, model, originalStorageEntry);
987
1177
  const entry = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, originalEntry), {}, {
988
1178
  status: STATUS_UNPUBLISHED
@@ -1018,6 +1208,71 @@ const createContentEntryCrud = params => {
1018
1208
  });
1019
1209
  }
1020
1210
  };
1211
+ const getUniqueFieldValues = async (model, params) => {
1212
+ await entriesPermissions.ensure({
1213
+ rwd: "r"
1214
+ });
1215
+ await modelsPermissions.ensureCanAccessModel({
1216
+ model,
1217
+ locale: getLocale().code
1218
+ });
1219
+ const {
1220
+ where: initialWhere,
1221
+ fieldId
1222
+ } = params;
1223
+ const where = (0, _objectSpread2.default)({}, initialWhere);
1224
+ /**
1225
+ * Possibly only get records which are owned by current user.
1226
+ * Or if searching for the owner set that value - in the case that user can see other entries than their own.
1227
+ */
1228
+ if (await entriesPermissions.canAccessOnlyOwnRecords()) {
1229
+ where.ownedBy = getIdentity().id;
1230
+ }
1231
+
1232
+ /**
1233
+ * Where must contain either latest or published keys.
1234
+ * We cannot list entries without one of those
1235
+ */
1236
+ if (where.latest && where.published) {
1237
+ throw new _error.default("Cannot list entries that are both published and latest.", "LIST_ENTRIES_ERROR", {
1238
+ where
1239
+ });
1240
+ } else if (!where.latest && !where.published) {
1241
+ throw new _error.default("Cannot list entries if we do not have latest or published defined.", "LIST_ENTRIES_ERROR", {
1242
+ where
1243
+ });
1244
+ }
1245
+ /**
1246
+ * We need to verify that the field in question is searchable.
1247
+ */
1248
+ const fields = (0, _searchableFields.getSearchableFields)({
1249
+ fields: model.fields,
1250
+ plugins: context.plugins,
1251
+ input: []
1252
+ });
1253
+ if (!fields.includes(fieldId)) {
1254
+ throw new _error.default("Cannot list unique entry field values if the field is not searchable.", "LIST_UNIQUE_ENTRY_VALUES_ERROR", {
1255
+ fieldId
1256
+ });
1257
+ }
1258
+ try {
1259
+ return await storageOperations.entries.getUniqueFieldValues(model, {
1260
+ where,
1261
+ fieldId
1262
+ });
1263
+ } catch (ex) {
1264
+ throw new _error.default("Error while fetching unique entry values from storage.", "LIST_UNIQUE_ENTRY_VALUES_ERROR", {
1265
+ error: {
1266
+ message: ex.message,
1267
+ code: ex.code,
1268
+ data: ex.data
1269
+ },
1270
+ model,
1271
+ where,
1272
+ fieldId
1273
+ });
1274
+ }
1275
+ };
1021
1276
  return {
1022
1277
  /**
1023
1278
  * Deprecated - will be removed in 5.35.0
@@ -1192,9 +1447,14 @@ const createContentEntryCrud = params => {
1192
1447
  return deleteEntryRevision(model, id);
1193
1448
  });
1194
1449
  },
1195
- async deleteEntry(model, entryId) {
1450
+ async deleteEntry(model, entryId, options) {
1196
1451
  return context.benchmark.measure("headlessCms.crud.entries.deleteEntry", async () => {
1197
- return deleteEntry(model, entryId);
1452
+ return deleteEntry(model, entryId, options);
1453
+ });
1454
+ },
1455
+ async deleteMultipleEntries(model, ids) {
1456
+ return context.benchmark.measure("headlessCms.crud.entries.deleteMultipleEntries", async () => {
1457
+ return deleteMultipleEntries(model, ids);
1198
1458
  });
1199
1459
  },
1200
1460
  async publishEntry(model, id) {
@@ -1206,6 +1466,11 @@ const createContentEntryCrud = params => {
1206
1466
  return context.benchmark.measure("headlessCms.crud.entries.unpublishEntry", async () => {
1207
1467
  return unpublishEntry(model, id);
1208
1468
  });
1469
+ },
1470
+ async getUniqueFieldValues(model, params) {
1471
+ return context.benchmark.measure("headlessCms.crud.entries.getUniqueFieldValues", async () => {
1472
+ return getUniqueFieldValues(model, params);
1473
+ });
1209
1474
  }
1210
1475
  };
1211
1476
  };