@webiny/api-headless-cms-ddb 0.0.0-unstable.aa00eecd97 → 0.0.0-unstable.ac6ebf63c6

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 (167) hide show
  1. package/README.md +6 -20
  2. package/definitions/entry.d.ts +4 -5
  3. package/definitions/entry.js +7 -88
  4. package/definitions/entry.js.map +1 -1
  5. package/definitions/group.d.ts +4 -5
  6. package/definitions/group.js +7 -59
  7. package/definitions/group.js.map +1 -1
  8. package/definitions/model.d.ts +4 -5
  9. package/definitions/model.js +7 -103
  10. package/definitions/model.js.map +1 -1
  11. package/definitions/table.d.ts +4 -6
  12. package/definitions/table.js +8 -26
  13. package/definitions/table.js.map +1 -1
  14. package/definitions/types.d.ts +59 -0
  15. package/definitions/types.js +3 -0
  16. package/definitions/types.js.map +1 -0
  17. package/dynamoDb/index.d.ts +1 -1
  18. package/dynamoDb/index.js +7 -12
  19. package/dynamoDb/index.js.map +1 -1
  20. package/dynamoDb/path/locationFolderId.d.ts +2 -0
  21. package/dynamoDb/path/locationFolderId.js +34 -0
  22. package/dynamoDb/path/locationFolderId.js.map +1 -0
  23. package/dynamoDb/path/plainObject.d.ts +1 -1
  24. package/dynamoDb/path/plainObject.js +7 -14
  25. package/dynamoDb/path/plainObject.js.map +1 -1
  26. package/dynamoDb/storage/longText.js +13 -18
  27. package/dynamoDb/storage/longText.js.map +1 -1
  28. package/dynamoDb/storage/richText.js +28 -71
  29. package/dynamoDb/storage/richText.js.map +1 -1
  30. package/dynamoDb/transformValue/datetime.d.ts +1 -1
  31. package/dynamoDb/transformValue/datetime.js +7 -12
  32. package/dynamoDb/transformValue/datetime.js.map +1 -1
  33. package/index.d.ts +2 -2
  34. package/index.js +40 -82
  35. package/index.js.map +1 -1
  36. package/operations/entry/dataLoader/DataLoaderCache.d.ts +1 -3
  37. package/operations/entry/dataLoader/DataLoaderCache.js +8 -17
  38. package/operations/entry/dataLoader/DataLoaderCache.js.map +1 -1
  39. package/operations/entry/dataLoader/constants.js +3 -8
  40. package/operations/entry/dataLoader/constants.js.map +1 -1
  41. package/operations/entry/dataLoader/createBatchScheduleFn.js +6 -10
  42. package/operations/entry/dataLoader/createBatchScheduleFn.js.map +1 -1
  43. package/operations/entry/dataLoader/getAllEntryRevisions.d.ts +3 -3
  44. package/operations/entry/dataLoader/getAllEntryRevisions.js +13 -24
  45. package/operations/entry/dataLoader/getAllEntryRevisions.js.map +1 -1
  46. package/operations/entry/dataLoader/getLatestRevisionByEntryId.d.ts +3 -3
  47. package/operations/entry/dataLoader/getLatestRevisionByEntryId.js +24 -35
  48. package/operations/entry/dataLoader/getLatestRevisionByEntryId.js.map +1 -1
  49. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.d.ts +3 -3
  50. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.js +24 -35
  51. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.js.map +1 -1
  52. package/operations/entry/dataLoader/getRevisionById.d.ts +3 -3
  53. package/operations/entry/dataLoader/getRevisionById.js +26 -37
  54. package/operations/entry/dataLoader/getRevisionById.js.map +1 -1
  55. package/operations/entry/dataLoader/index.d.ts +5 -5
  56. package/operations/entry/dataLoader/index.js +12 -31
  57. package/operations/entry/dataLoader/index.js.map +1 -1
  58. package/operations/entry/dataLoader/types.d.ts +3 -4
  59. package/operations/entry/dataLoader/types.js +2 -4
  60. package/operations/entry/dataLoader/types.js.map +1 -1
  61. package/operations/entry/dataLoaders.d.ts +11 -12
  62. package/operations/entry/dataLoaders.js +25 -34
  63. package/operations/entry/dataLoaders.js.map +1 -1
  64. package/operations/entry/filtering/createExpressions.d.ts +5 -5
  65. package/operations/entry/filtering/createExpressions.js +29 -30
  66. package/operations/entry/filtering/createExpressions.js.map +1 -1
  67. package/operations/entry/filtering/createFields.d.ts +6 -4
  68. package/operations/entry/filtering/createFields.js +87 -86
  69. package/operations/entry/filtering/createFields.js.map +1 -1
  70. package/operations/entry/filtering/extractSort.d.ts +6 -6
  71. package/operations/entry/filtering/extractSort.js +40 -23
  72. package/operations/entry/filtering/extractSort.js.map +1 -1
  73. package/operations/entry/filtering/filter.d.ts +6 -6
  74. package/operations/entry/filtering/filter.js +36 -29
  75. package/operations/entry/filtering/filter.js.map +1 -1
  76. package/operations/entry/filtering/fullTextSearch.d.ts +3 -3
  77. package/operations/entry/filtering/fullTextSearch.js +8 -14
  78. package/operations/entry/filtering/fullTextSearch.js.map +1 -1
  79. package/operations/entry/filtering/getValue.js +6 -10
  80. package/operations/entry/filtering/getValue.js.map +1 -1
  81. package/operations/entry/filtering/index.d.ts +2 -2
  82. package/operations/entry/filtering/index.js +3 -18
  83. package/operations/entry/filtering/index.js.map +1 -1
  84. package/operations/entry/filtering/mapPlugins.d.ts +2 -2
  85. package/operations/entry/filtering/mapPlugins.js +5 -11
  86. package/operations/entry/filtering/mapPlugins.js.map +1 -1
  87. package/operations/entry/filtering/plugins/defaultFilterCreate.d.ts +2 -2
  88. package/operations/entry/filtering/plugins/defaultFilterCreate.js +10 -14
  89. package/operations/entry/filtering/plugins/defaultFilterCreate.js.map +1 -1
  90. package/operations/entry/filtering/plugins/index.d.ts +1 -1
  91. package/operations/entry/filtering/plugins/index.js +8 -12
  92. package/operations/entry/filtering/plugins/index.js.map +1 -1
  93. package/operations/entry/filtering/plugins/objectFilterCreate.d.ts +2 -2
  94. package/operations/entry/filtering/plugins/objectFilterCreate.js +17 -19
  95. package/operations/entry/filtering/plugins/objectFilterCreate.js.map +1 -1
  96. package/operations/entry/filtering/plugins/refFilterCreate.d.ts +3 -2
  97. package/operations/entry/filtering/plugins/refFilterCreate.js +22 -19
  98. package/operations/entry/filtering/plugins/refFilterCreate.js.map +1 -1
  99. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.d.ts +2 -0
  100. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.js +57 -0
  101. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.js.map +1 -0
  102. package/operations/entry/filtering/sort.d.ts +6 -6
  103. package/operations/entry/filtering/sort.js +18 -20
  104. package/operations/entry/filtering/sort.js.map +1 -1
  105. package/operations/entry/filtering/systemFields.d.ts +2 -4
  106. package/operations/entry/filtering/systemFields.js +88 -42
  107. package/operations/entry/filtering/systemFields.js.map +1 -1
  108. package/operations/entry/filtering/transform.d.ts +1 -1
  109. package/operations/entry/filtering/transform.js +3 -8
  110. package/operations/entry/filtering/transform.js.map +1 -1
  111. package/operations/entry/filtering/types.d.ts +3 -3
  112. package/operations/entry/filtering/types.js +2 -4
  113. package/operations/entry/filtering/types.js.map +1 -1
  114. package/operations/entry/filtering/values.d.ts +1 -1
  115. package/operations/entry/filtering/values.js +6 -12
  116. package/operations/entry/filtering/values.js.map +1 -1
  117. package/operations/entry/filtering/where.js +6 -10
  118. package/operations/entry/filtering/where.js.map +1 -1
  119. package/operations/entry/index.d.ts +3 -4
  120. package/operations/entry/index.js +635 -339
  121. package/operations/entry/index.js.map +1 -1
  122. package/operations/entry/keys.d.ts +40 -2
  123. package/operations/entry/keys.js +55 -30
  124. package/operations/entry/keys.js.map +1 -1
  125. package/operations/group/index.d.ts +4 -4
  126. package/operations/group/index.js +44 -61
  127. package/operations/group/index.js.map +1 -1
  128. package/operations/model/index.d.ts +3 -3
  129. package/operations/model/index.js +37 -46
  130. package/operations/model/index.js.map +1 -1
  131. package/package.json +21 -28
  132. package/plugins/CmsEntryFieldFilterPathPlugin.d.ts +4 -3
  133. package/plugins/CmsEntryFieldFilterPathPlugin.js +15 -16
  134. package/plugins/CmsEntryFieldFilterPathPlugin.js.map +1 -1
  135. package/plugins/CmsEntryFieldFilterPlugin.d.ts +10 -10
  136. package/plugins/CmsEntryFieldFilterPlugin.js +11 -14
  137. package/plugins/CmsEntryFieldFilterPlugin.js.map +1 -1
  138. package/plugins/CmsEntryFieldSortingPlugin.d.ts +2 -2
  139. package/plugins/CmsEntryFieldSortingPlugin.js +6 -15
  140. package/plugins/CmsEntryFieldSortingPlugin.js.map +1 -1
  141. package/plugins/CmsFieldFilterValueTransformPlugin.d.ts +2 -2
  142. package/plugins/CmsFieldFilterValueTransformPlugin.js +5 -13
  143. package/plugins/CmsFieldFilterValueTransformPlugin.js.map +1 -1
  144. package/plugins/index.d.ts +4 -4
  145. package/plugins/index.js +5 -48
  146. package/plugins/index.js.map +1 -1
  147. package/types.d.ts +18 -22
  148. package/types.js +5 -12
  149. package/types.js.map +1 -1
  150. package/definitions/settings.d.ts +0 -9
  151. package/definitions/settings.js +0 -53
  152. package/definitions/settings.js.map +0 -1
  153. package/definitions/system.d.ts +0 -9
  154. package/definitions/system.js +0 -41
  155. package/definitions/system.js.map +0 -1
  156. package/dynamoDb/storage/date.d.ts +0 -3
  157. package/dynamoDb/storage/date.js +0 -91
  158. package/dynamoDb/storage/date.js.map +0 -1
  159. package/operations/entry/systemFields.d.ts +0 -2
  160. package/operations/entry/systemFields.js +0 -72
  161. package/operations/entry/systemFields.js.map +0 -1
  162. package/operations/settings/index.d.ts +0 -7
  163. package/operations/settings/index.js +0 -111
  164. package/operations/settings/index.js.map +0 -1
  165. package/operations/system/index.d.ts +0 -7
  166. package/operations/system/index.js +0 -82
  167. package/operations/system/index.js.map +0 -1
@@ -1,32 +1,13 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.createEntriesStorageOperations = void 0;
8
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
9
- var _error = _interopRequireDefault(require("@webiny/error"));
10
- var _dataLoaders = require("./dataLoaders");
11
- var _types = require("@webiny/api-headless-cms/types");
12
- var _keys = require("./keys");
13
- var _batchWrite = require("@webiny/db-dynamodb/utils/batchWrite");
14
- var _query = require("@webiny/db-dynamodb/utils/query");
15
- var _cleanup = require("@webiny/db-dynamodb/utils/cleanup");
16
- var _cursor = require("@webiny/utils/cursor");
17
- var _zeroPad = require("@webiny/utils/zeroPad");
18
- var _apiHeadlessCms = require("@webiny/api-headless-cms");
19
- var _createFields = require("./filtering/createFields");
20
- var _filtering = require("./filtering");
21
- const createType = () => {
22
- return "cms.entry";
23
- };
24
- const createLatestType = () => {
25
- return `${createType()}.l`;
26
- };
27
- const createPublishedType = () => {
28
- return `${createType()}.p`;
29
- };
1
+ import WebinyError from "@webiny/error";
2
+ import { DataLoadersHandler } from "./dataLoaders.js";
3
+ import { CONTENT_ENTRY_STATUS } from "@webiny/api-headless-cms/types/index.js";
4
+ import { createEntryLatestKeys, createEntryPublishedKeys, createEntryRevisionKeys, createGSIPartitionKey, createPartitionKey, createPublishedSortKey, createRevisionSortKey } from "./keys.js";
5
+ import { decodeCursor, encodeCursor } from "@webiny/utils";
6
+ import { StorageOperationsCmsModelPlugin, StorageTransformPlugin } from "@webiny/api-headless-cms";
7
+ import { createFields } from "./filtering/createFields.js";
8
+ import { filter, sort } from "./filtering/index.js";
9
+ import { isDeletedEntryMetaField, isEntryLevelEntryMetaField, isRestoredEntryMetaField, pickEntryMetaFields } from "@webiny/api-headless-cms/constants.js";
10
+ import { getBaseFieldType } from "@webiny/api-headless-cms/utils/getBaseFieldType.js";
30
11
  const convertToStorageEntry = params => {
31
12
  const {
32
13
  model,
@@ -36,9 +17,10 @@ const convertToStorageEntry = params => {
36
17
  fields: model.fields,
37
18
  values: storageEntry.values
38
19
  });
39
- return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
20
+ return {
21
+ ...storageEntry,
40
22
  values
41
- });
23
+ };
42
24
  };
43
25
  const convertFromStorageEntry = params => {
44
26
  const {
@@ -49,12 +31,13 @@ const convertFromStorageEntry = params => {
49
31
  fields: model.fields,
50
32
  values: storageEntry.values
51
33
  });
52
- return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
34
+ return {
35
+ ...storageEntry,
53
36
  values
54
- });
37
+ };
55
38
  };
56
- const MAX_LIST_LIMIT = 10000;
57
- const createEntriesStorageOperations = params => {
39
+ const MAX_LIST_LIMIT = 1000000;
40
+ export const createEntriesStorageOperations = params => {
58
41
  const {
59
42
  entity,
60
43
  plugins
@@ -64,23 +47,25 @@ const createEntriesStorageOperations = params => {
64
47
  if (storageOperationsCmsModelPlugin) {
65
48
  return storageOperationsCmsModelPlugin;
66
49
  }
67
- storageOperationsCmsModelPlugin = plugins.oneByType(_apiHeadlessCms.StorageOperationsCmsModelPlugin.type);
50
+ storageOperationsCmsModelPlugin = plugins.oneByType(StorageOperationsCmsModelPlugin.type);
68
51
  return storageOperationsCmsModelPlugin;
69
52
  };
70
53
  const getStorageOperationsModel = model => {
71
54
  const plugin = getStorageOperationsCmsModelPlugin();
72
55
  return plugin.getModel(model);
73
56
  };
74
- const dataLoaders = new _dataLoaders.DataLoadersHandler({
57
+ const dataLoaders = new DataLoadersHandler({
75
58
  entity
76
59
  });
77
- const storageTransformPlugins = plugins.byType(_apiHeadlessCms.StorageTransformPlugin.type).reduce((collection, plugin) => {
78
- collection[plugin.fieldType] = plugin;
79
- return collection;
80
- }, {});
81
60
  const createStorageTransformCallable = model => {
61
+ // Cache StorageTransformPlugin to optimize execution.
62
+ const storageTransformPlugins = plugins.byType(StorageTransformPlugin.type).reduce((collection, plugin) => {
63
+ collection[plugin.fieldType] = plugin;
64
+ return collection;
65
+ }, {});
82
66
  return (field, value) => {
83
- const plugin = storageTransformPlugins[field.type];
67
+ const fieldType = getBaseFieldType(field);
68
+ const plugin = storageTransformPlugins[fieldType];
84
69
  if (!plugin) {
85
70
  return value;
86
71
  }
@@ -101,61 +86,55 @@ const createEntriesStorageOperations = params => {
101
86
  storageEntry: initialStorageEntry
102
87
  } = params;
103
88
  const model = getStorageOperationsModel(initialModel);
104
- const partitionKey = (0, _keys.createPartitionKey)({
105
- id: entry.id,
106
- locale: model.locale,
107
- tenant: model.tenant
108
- });
109
89
  const isPublished = entry.status === "published";
110
90
  const locked = isPublished ? true : entry.locked;
111
91
  const storageEntry = convertToStorageEntry({
112
92
  model,
113
93
  storageEntry: initialStorageEntry
114
94
  });
95
+ const storageEntryRevisionKeys = createEntryRevisionKeys(entry);
96
+ const storageEntryLatestKeys = createEntryLatestKeys(entry);
115
97
  /**
116
98
  * We need to:
117
99
  * - create new main entry item
118
100
  * - create new or update the latest entry item
119
101
  */
120
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
121
- locked,
122
- PK: partitionKey,
123
- SK: (0, _keys.createRevisionSortKey)(entry),
124
- TYPE: createType(),
125
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
126
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
127
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
128
- locked,
129
- PK: partitionKey,
130
- SK: (0, _keys.createLatestSortKey)(),
131
- TYPE: createLatestType(),
132
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
133
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
134
- }))];
102
+ const entityBatch = entity.createEntityWriter({
103
+ put: [{
104
+ ...storageEntryRevisionKeys,
105
+ data: {
106
+ ...storageEntry,
107
+ locked
108
+ }
109
+ }, {
110
+ ...storageEntryLatestKeys,
111
+ data: {
112
+ ...storageEntry,
113
+ locked
114
+ }
115
+ }]
116
+ });
135
117
 
136
118
  /**
137
119
  * We need to create published entry if
138
120
  */
139
121
  if (isPublished) {
140
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
141
- locked,
142
- PK: partitionKey,
143
- SK: (0, _keys.createPublishedSortKey)(),
144
- TYPE: createLatestType(),
145
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
146
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
147
- })));
122
+ const storageEntryPublishedKeys = createEntryPublishedKeys(storageEntry);
123
+ entityBatch.put({
124
+ ...storageEntryPublishedKeys,
125
+ data: {
126
+ ...storageEntry,
127
+ locked
128
+ }
129
+ });
148
130
  }
149
131
  try {
150
- await (0, _batchWrite.batchWriteAll)({
151
- table: entity.table,
152
- items
153
- });
132
+ await entityBatch.execute();
154
133
  dataLoaders.clearAll({
155
134
  model
156
135
  });
157
136
  } catch (ex) {
158
- throw new _error.default(ex.message || "Could not insert data into the DynamoDB.", ex.code || "CREATE_ENTRY_ERROR", {
137
+ throw new WebinyError(ex.message || "Could not insert data into the DynamoDB.", ex.code || "CREATE_ENTRY_ERROR", {
159
138
  error: ex,
160
139
  entry
161
140
  });
@@ -168,43 +147,63 @@ const createEntriesStorageOperations = params => {
168
147
  storageEntry: initialStorageEntry
169
148
  } = params;
170
149
  const model = getStorageOperationsModel(initialModel);
171
- const partitionKey = (0, _keys.createPartitionKey)({
172
- id: entry.id,
173
- locale: model.locale,
174
- tenant: model.tenant
175
- });
176
150
  const storageEntry = convertToStorageEntry({
177
151
  storageEntry: initialStorageEntry,
178
152
  model
179
153
  });
154
+
180
155
  /**
181
156
  * We need to:
182
157
  * - create the main entry item
183
- * - update the last entry item to a current one
158
+ * - update the latest entry item to the current one
159
+ * - if the entry's status was set to "published":
160
+ * - update the published entry item to the current one
161
+ * - unpublish previously published revision (if any)
184
162
  */
185
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
186
- PK: partitionKey,
187
- SK: (0, _keys.createRevisionSortKey)(storageEntry),
188
- TYPE: createType(),
189
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
190
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
191
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
192
- PK: partitionKey,
193
- SK: (0, _keys.createLatestSortKey)(),
194
- TYPE: createLatestType(),
195
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
196
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
197
- }))];
198
- try {
199
- await (0, _batchWrite.batchWriteAll)({
200
- table: entity.table,
201
- items
163
+ const entityBatch = entity.createEntityWriter({
164
+ put: [{
165
+ ...createEntryRevisionKeys(storageEntry),
166
+ data: {
167
+ ...storageEntry
168
+ }
169
+ }, {
170
+ ...createEntryLatestKeys(storageEntry),
171
+ data: {
172
+ ...storageEntry
173
+ }
174
+ }]
175
+ });
176
+ const isPublished = entry.status === "published";
177
+ if (isPublished) {
178
+ entityBatch.put({
179
+ ...createEntryPublishedKeys(storageEntry),
180
+ data: {
181
+ ...storageEntry
182
+ }
183
+ });
184
+
185
+ // Unpublish previously published revision (if any).
186
+ const [publishedRevisionStorageEntry] = await dataLoaders.getPublishedRevisionByEntryId({
187
+ model,
188
+ ids: [entry.id]
202
189
  });
190
+ if (publishedRevisionStorageEntry) {
191
+ entityBatch.put({
192
+ ...createEntryRevisionKeys(publishedRevisionStorageEntry),
193
+ data: {
194
+ ...publishedRevisionStorageEntry,
195
+ status: CONTENT_ENTRY_STATUS.UNPUBLISHED
196
+ }
197
+ });
198
+ }
199
+ }
200
+ try {
201
+ await entityBatch.execute();
203
202
  dataLoaders.clearAll({
204
203
  model
205
204
  });
206
205
  } catch (ex) {
207
- throw new _error.default(ex.message || "Could not create revision from given entry.", ex.code || "CREATE_REVISION_ERROR", {
206
+ throw new WebinyError(ex.message || "Could not create revision from given entry.", ex.code || "CREATE_REVISION_ERROR", {
208
207
  error: ex,
209
208
  entry,
210
209
  storageEntry
@@ -221,14 +220,8 @@ const createEntriesStorageOperations = params => {
221
220
  storageEntry: initialStorageEntry
222
221
  } = params;
223
222
  const model = getStorageOperationsModel(initialModel);
224
- const partitionKey = (0, _keys.createPartitionKey)({
225
- id: entry.id,
226
- locale: model.locale,
227
- tenant: model.tenant
228
- });
229
223
  const isPublished = entry.status === "published";
230
224
  const locked = isPublished ? true : entry.locked;
231
- const items = [];
232
225
  const storageEntry = convertToStorageEntry({
233
226
  model,
234
227
  storageEntry: initialStorageEntry
@@ -238,104 +231,322 @@ const createEntriesStorageOperations = params => {
238
231
  * - update the current entry
239
232
  * - update the latest entry if the current entry is the latest one
240
233
  */
241
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
242
- locked,
243
- PK: partitionKey,
244
- SK: (0, _keys.createRevisionSortKey)(storageEntry),
245
- TYPE: createType(),
246
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
247
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
248
- })));
234
+
235
+ const entityBatch = entity.createEntityWriter({
236
+ put: [{
237
+ ...createEntryRevisionKeys(storageEntry),
238
+ data: {
239
+ ...storageEntry,
240
+ locked
241
+ }
242
+ }]
243
+ });
249
244
  if (isPublished) {
250
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
251
- locked,
252
- PK: partitionKey,
253
- SK: (0, _keys.createPublishedSortKey)(),
254
- TYPE: createPublishedType(),
255
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
256
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
257
- })));
245
+ entityBatch.put({
246
+ ...createEntryPublishedKeys(storageEntry),
247
+ data: {
248
+ ...storageEntry,
249
+ locked
250
+ }
251
+ });
258
252
  }
259
253
 
260
254
  /**
261
255
  * We need the latest entry to update it as well if necessary.
262
256
  */
263
257
  const latestStorageEntry = await getLatestRevisionByEntryId(model, entry);
264
- if (latestStorageEntry && latestStorageEntry.id === entry.id) {
265
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
266
- locked,
267
- PK: partitionKey,
268
- SK: (0, _keys.createLatestSortKey)(),
269
- TYPE: createLatestType(),
270
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
271
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
272
- })));
258
+ if (latestStorageEntry) {
259
+ const updatingLatestRevision = latestStorageEntry.id === entry.id;
260
+ if (updatingLatestRevision) {
261
+ entityBatch.put({
262
+ ...createEntryLatestKeys(storageEntry),
263
+ data: {
264
+ ...storageEntry,
265
+ locked
266
+ }
267
+ });
268
+ } else {
269
+ /**
270
+ * If not updating latest revision, we still want to update the latest revision's
271
+ * entry-level meta fields to match the current revision's entry-level meta fields.
272
+ */
273
+ const updatedEntryLevelMetaFields = pickEntryMetaFields(entry, isEntryLevelEntryMetaField);
274
+
275
+ /**
276
+ * First we update the regular DynamoDB table. Two updates are needed:
277
+ * - one for the actual revision record
278
+ * - one for the latest record
279
+ */
280
+ entityBatch.put({
281
+ ...createEntryRevisionKeys(latestStorageEntry),
282
+ data: {
283
+ ...latestStorageEntry,
284
+ ...updatedEntryLevelMetaFields
285
+ }
286
+ });
287
+ entityBatch.put({
288
+ ...createEntryLatestKeys(latestStorageEntry),
289
+ data: {
290
+ ...latestStorageEntry,
291
+ ...updatedEntryLevelMetaFields
292
+ }
293
+ });
294
+ }
273
295
  }
274
296
  try {
275
- await (0, _batchWrite.batchWriteAll)({
276
- table: entity.table,
277
- items
278
- });
297
+ await entityBatch.execute();
279
298
  dataLoaders.clearAll({
280
299
  model
281
300
  });
282
301
  return initialStorageEntry;
283
302
  } catch (ex) {
284
- throw new _error.default(ex.message || "Could not update entry.", ex.code || "UPDATE_ERROR", {
303
+ throw new WebinyError(ex.message || "Could not update entry.", ex.code || "UPDATE_ERROR", {
285
304
  error: ex,
286
305
  entry,
287
306
  latestStorageEntry
288
307
  });
289
308
  }
290
309
  };
310
+ const move = async (initialModel, id, folderId) => {
311
+ /**
312
+ * We need to:
313
+ * - load all the revisions of the entry, including published and latest
314
+ * - update all the revisions (published and latest ) of the entry with new folderId
315
+ */
316
+ const model = getStorageOperationsModel(initialModel);
317
+ /**
318
+ * First we need to load all the revisions and published / latest entry.
319
+ */
320
+ const partitionKey = createPartitionKey({
321
+ id,
322
+ tenant: model.tenant
323
+ });
324
+ const records = await entity.queryAll({
325
+ partitionKey,
326
+ options: {
327
+ gte: " "
328
+ }
329
+ });
330
+ /**
331
+ * Then create the batch writes for the DynamoDB, with the updated folderId.
332
+ */
333
+ const entityBatch = entity.createEntityWriter({
334
+ put: records.map(item => {
335
+ return {
336
+ ...item,
337
+ data: {
338
+ ...item.data,
339
+ location: {
340
+ ...item.data.location,
341
+ folderId
342
+ }
343
+ }
344
+ };
345
+ })
346
+ });
347
+
348
+ /**
349
+ * And finally write it...
350
+ */
351
+ try {
352
+ await entityBatch.execute();
353
+ } catch (ex) {
354
+ throw WebinyError.from(ex, {
355
+ message: "Could not move records to a new folder.",
356
+ data: {
357
+ id,
358
+ folderId
359
+ }
360
+ });
361
+ }
362
+ };
363
+ const moveToBin = async (initialModel, params) => {
364
+ const {
365
+ entry,
366
+ storageEntry: initialStorageEntry
367
+ } = params;
368
+ const model = getStorageOperationsModel(initialModel);
369
+
370
+ /**
371
+ * First we need to load all the revisions and published / latest entries.
372
+ */
373
+
374
+ let records = [];
375
+ try {
376
+ records = await entity.queryAll({
377
+ partitionKey: createPartitionKey({
378
+ id: entry.id,
379
+ tenant: model.tenant
380
+ }),
381
+ options: {
382
+ gte: " "
383
+ }
384
+ });
385
+ } catch (ex) {
386
+ throw new WebinyError(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
387
+ error: ex,
388
+ id: entry.id
389
+ });
390
+ }
391
+ if (records.length === 0) {
392
+ return;
393
+ }
394
+ const storageEntry = convertToStorageEntry({
395
+ model,
396
+ storageEntry: initialStorageEntry
397
+ });
398
+
399
+ /**
400
+ * Let's pick the `deleted` meta fields from the storage entry.
401
+ */
402
+ const updatedDeletedMetaFields = pickEntryMetaFields(storageEntry, isDeletedEntryMetaField);
403
+
404
+ /**
405
+ * Then create the batch writes for the DynamoDB, with the updated data.
406
+ */
407
+ const entityBatch = entity.createEntityWriter({
408
+ put: records.map(record => {
409
+ return {
410
+ ...record,
411
+ data: {
412
+ ...record.data,
413
+ ...updatedDeletedMetaFields,
414
+ wbyDeleted: storageEntry.wbyDeleted,
415
+ location: storageEntry.location,
416
+ binOriginalFolderId: storageEntry.binOriginalFolderId
417
+ }
418
+ };
419
+ })
420
+ });
421
+ /**
422
+ * And finally write it...
423
+ */
424
+ try {
425
+ await entityBatch.execute();
426
+ } catch (ex) {
427
+ throw new WebinyError(ex.message || "Could not move the entry to the bin.", ex.code || "MOVE_ENTRY_TO_BIN_ERROR", {
428
+ error: ex,
429
+ entry,
430
+ storageEntry
431
+ });
432
+ }
433
+ };
291
434
  const deleteEntry = async (initialModel, params) => {
292
435
  const {
293
436
  entry
294
437
  } = params;
295
438
  const id = entry.id || entry.entryId;
296
439
  const model = getStorageOperationsModel(initialModel);
297
- const queryAllParams = {
298
- entity,
299
- partitionKey: (0, _keys.createPartitionKey)({
300
- id,
301
- locale: model.locale,
302
- tenant: model.tenant
303
- }),
304
- options: {
305
- gte: " "
306
- }
307
- };
440
+ const partitionKey = createPartitionKey({
441
+ id,
442
+ tenant: model.tenant
443
+ });
308
444
  let records = [];
309
445
  try {
310
- records = await (0, _query.queryAll)(queryAllParams);
446
+ records = await entity.queryAll({
447
+ partitionKey,
448
+ options: {
449
+ gte: " "
450
+ }
451
+ });
311
452
  } catch (ex) {
312
- throw new _error.default(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
453
+ throw new WebinyError(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
313
454
  error: ex,
314
455
  id
315
456
  });
316
457
  }
317
- const items = records.map(item => {
318
- return entity.deleteBatch({
319
- PK: item.PK,
320
- SK: item.SK
321
- });
458
+ const entityBatch = entity.createEntityWriter({
459
+ delete: records.map(item => {
460
+ return {
461
+ PK: item.PK,
462
+ SK: item.SK
463
+ };
464
+ })
322
465
  });
323
466
  try {
324
- await (0, _batchWrite.batchWriteAll)({
325
- table: entity.table,
326
- items
327
- });
467
+ await entityBatch.execute();
328
468
  dataLoaders.clearAll({
329
469
  model
330
470
  });
331
471
  } catch (ex) {
332
- throw new _error.default(ex.message || "Could not delete the entry.", ex.code || "DELETE_ENTRY_ERROR", {
472
+ throw new WebinyError(ex.message || "Could not delete the entry.", ex.code || "DELETE_ENTRY_ERROR", {
333
473
  error: ex,
334
- partitionKey: queryAllParams.partitionKey,
474
+ partitionKey,
335
475
  id
336
476
  });
337
477
  }
338
478
  };
479
+ const restoreFromBin = async (initialModel, params) => {
480
+ const {
481
+ entry,
482
+ storageEntry: initialStorageEntry
483
+ } = params;
484
+ const model = getStorageOperationsModel(initialModel);
485
+
486
+ /**
487
+ * First we need to load all the revisions and published / latest entries.
488
+ */
489
+ let records = [];
490
+ try {
491
+ records = await entity.queryAll({
492
+ partitionKey: createPartitionKey({
493
+ id: entry.id,
494
+ tenant: model.tenant
495
+ }),
496
+ options: {
497
+ gte: " "
498
+ }
499
+ });
500
+ } catch (ex) {
501
+ throw new WebinyError(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
502
+ error: ex,
503
+ id: entry.id
504
+ });
505
+ }
506
+ if (records.length === 0) {
507
+ return initialStorageEntry;
508
+ }
509
+ const storageEntry = convertToStorageEntry({
510
+ model,
511
+ storageEntry: initialStorageEntry
512
+ });
513
+
514
+ /**
515
+ * Let's pick the `restored` meta fields from the storage entry.
516
+ */
517
+ const updatedRestoredMetaFields = pickEntryMetaFields(storageEntry, isRestoredEntryMetaField);
518
+ const entityBatch = entity.createEntityWriter({
519
+ put: records.map(record => {
520
+ return {
521
+ ...record,
522
+ data: {
523
+ ...record.data,
524
+ ...updatedRestoredMetaFields,
525
+ wbyDeleted: storageEntry.wbyDeleted,
526
+ location: storageEntry.location,
527
+ binOriginalFolderId: storageEntry.binOriginalFolderId
528
+ }
529
+ };
530
+ })
531
+ });
532
+
533
+ /**
534
+ * And finally write it...
535
+ */
536
+ try {
537
+ await entityBatch.execute();
538
+ dataLoaders.clearAll({
539
+ model
540
+ });
541
+ return initialStorageEntry;
542
+ } catch (ex) {
543
+ throw new WebinyError(ex.message || "Could not restore the entry from the bin.", ex.code || "RESTORE_ENTRY_ERROR", {
544
+ error: ex,
545
+ entry,
546
+ storageEntry
547
+ });
548
+ }
549
+ };
339
550
  const deleteRevision = async (initialModel, params) => {
340
551
  const {
341
552
  entry,
@@ -343,49 +554,55 @@ const createEntriesStorageOperations = params => {
343
554
  latestStorageEntry: initialLatestStorageEntry
344
555
  } = params;
345
556
  const model = getStorageOperationsModel(initialModel);
346
- const partitionKey = (0, _keys.createPartitionKey)({
557
+ const partitionKey = createPartitionKey({
347
558
  id: entry.id,
348
- locale: model.locale,
349
559
  tenant: model.tenant
350
560
  });
351
- const items = [entity.deleteBatch({
352
- PK: partitionKey,
353
- SK: (0, _keys.createRevisionSortKey)(entry)
354
- })];
561
+ const entityBatch = entity.createEntityWriter({
562
+ delete: [{
563
+ PK: partitionKey,
564
+ SK: createRevisionSortKey(entry)
565
+ }]
566
+ });
355
567
  const publishedStorageEntry = await getPublishedRevisionByEntryId(model, entry);
356
568
 
357
569
  /**
358
570
  * If revision we are deleting is the published one as well, we need to delete those records as well.
359
571
  */
360
572
  if (publishedStorageEntry && entry.id === publishedStorageEntry.id) {
361
- items.push(entity.deleteBatch({
573
+ entityBatch.delete({
362
574
  PK: partitionKey,
363
- SK: (0, _keys.createPublishedSortKey)()
364
- }));
575
+ SK: createPublishedSortKey()
576
+ });
365
577
  }
366
578
  if (initialLatestStorageEntry) {
367
579
  const latestStorageEntry = convertToStorageEntry({
368
580
  storageEntry: initialLatestStorageEntry,
369
581
  model
370
582
  });
371
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, latestStorageEntry), {}, {
372
- PK: partitionKey,
373
- SK: (0, _keys.createLatestSortKey)(),
374
- TYPE: createLatestType(),
375
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
376
- GSI1_SK: (0, _keys.createGSISortKey)(latestStorageEntry)
377
- })));
583
+ entityBatch.put({
584
+ ...createEntryLatestKeys(latestStorageEntry),
585
+ data: {
586
+ ...latestStorageEntry
587
+ }
588
+ });
589
+
590
+ // Do an update on the latest revision. We need to update the latest revision's
591
+ // entry-level meta fields to match the previous revision's entry-level meta fields.
592
+ entityBatch.put({
593
+ ...createEntryRevisionKeys(latestStorageEntry),
594
+ data: {
595
+ ...latestStorageEntry
596
+ }
597
+ });
378
598
  }
379
599
  try {
380
- await (0, _batchWrite.batchWriteAll)({
381
- table: entity.table,
382
- items
383
- });
600
+ await entityBatch.execute();
384
601
  dataLoaders.clearAll({
385
602
  model
386
603
  });
387
604
  } catch (ex) {
388
- throw new _error.default(ex.message, ex.code, {
605
+ throw new WebinyError(ex.message, ex.code, {
389
606
  error: ex,
390
607
  entry,
391
608
  latestEntry
@@ -407,50 +624,37 @@ const createEntriesStorageOperations = params => {
407
624
  /**
408
625
  * Then we need to construct the queries for all the revisions and entries.
409
626
  */
410
- const items = [];
627
+
628
+ const entityBatch = entity.createEntityWriter();
411
629
  for (const id of entries) {
412
- /**
413
- * Latest item.
414
- */
415
- items.push(entity.deleteBatch({
416
- PK: (0, _keys.createPartitionKey)({
417
- id,
418
- locale: model.locale,
419
- tenant: model.tenant
420
- }),
630
+ const partitionKey = createPartitionKey({
631
+ id,
632
+ tenant: model.tenant
633
+ });
634
+ entityBatch.delete({
635
+ PK: partitionKey,
421
636
  SK: "L"
422
- }));
423
- /**
424
- * Published item.
425
- */
426
- items.push(entity.deleteBatch({
427
- PK: (0, _keys.createPartitionKey)({
428
- id,
429
- locale: model.locale,
430
- tenant: model.tenant
431
- }),
637
+ });
638
+ entityBatch.delete({
639
+ PK: partitionKey,
432
640
  SK: "P"
433
- }));
641
+ });
434
642
  }
435
643
  /**
436
644
  * Exact revisions of all the entries
437
645
  */
438
646
  for (const revision of revisions) {
439
- items.push(entity.deleteBatch({
440
- PK: (0, _keys.createPartitionKey)({
647
+ entityBatch.delete({
648
+ PK: createPartitionKey({
441
649
  id: revision.id,
442
- locale: model.locale,
443
650
  tenant: model.tenant
444
651
  }),
445
- SK: (0, _keys.createRevisionSortKey)({
652
+ SK: createRevisionSortKey({
446
653
  version: revision.version
447
654
  })
448
- }));
655
+ });
449
656
  }
450
- await (0, _batchWrite.batchWriteAll)({
451
- table: entity.table,
452
- items
453
- });
657
+ await entityBatch.execute();
454
658
  };
455
659
  const getLatestRevisionByEntryId = async (initialModel, params) => {
456
660
  const model = getStorageOperationsModel(initialModel);
@@ -555,45 +759,36 @@ const createEntriesStorageOperations = params => {
555
759
  entryId,
556
760
  version
557
761
  } = params;
558
- const queryParams = {
559
- entity,
560
- partitionKey: (0, _keys.createPartitionKey)({
561
- tenant: model.tenant,
562
- locale: model.locale,
563
- id: entryId
564
- }),
762
+ const partitionKey = createPartitionKey({
763
+ tenant: model.tenant,
764
+ id: entryId
765
+ });
766
+ const unfilteredRevisions = await entity.queryAll({
767
+ partitionKey,
565
768
  options: {
566
- lt: `REV#${(0, _zeroPad.zeroPad)(version)}`,
567
- /**
568
- * We need to have extra checks because DynamoDB will return published or latest record if there is no REV# record.
569
- */
570
- filters: [{
571
- attr: "TYPE",
572
- eq: createType()
573
- }, {
574
- attr: "version",
575
- lt: version
576
- }],
769
+ beginsWith: `REV#`,
577
770
  reverse: true
578
771
  }
579
- };
772
+ });
773
+ const filteredRevisions = unfilteredRevisions.filter(item => {
774
+ return item.data.version < version;
775
+ });
776
+ const storageEntry = filteredRevisions[0];
777
+ if (!storageEntry) {
778
+ return null;
779
+ }
580
780
  try {
581
- const result = await (0, _query.queryOne)(queryParams);
582
- const storageEntry = (0, _cleanup.cleanupItem)(entity, result);
583
- if (!storageEntry) {
584
- return null;
585
- }
586
781
  return convertFromStorageEntry({
587
- storageEntry,
782
+ storageEntry: storageEntry.data,
588
783
  model
589
784
  });
590
785
  } catch (ex) {
591
- throw new _error.default(ex.message || "Could not get previous version of given entry.", ex.code || "GET_PREVIOUS_VERSION_ERROR", (0, _objectSpread2.default)((0, _objectSpread2.default)({}, params), {}, {
786
+ throw new WebinyError(ex.message || "Could not get previous version of given entry.", ex.code || "GET_PREVIOUS_VERSION_ERROR", {
787
+ ...params,
592
788
  error: ex,
593
- partitionKey: queryParams.partitionKey,
594
- options: queryParams.options,
789
+ partitionKey,
595
790
  model
596
- }));
791
+ });
597
792
  }
598
793
  };
599
794
  const list = async (initialModel, params) => {
@@ -608,22 +803,22 @@ const createEntriesStorageOperations = params => {
608
803
  } = params;
609
804
  const limit = initialLimit <= 0 || initialLimit >= MAX_LIST_LIMIT ? MAX_LIST_LIMIT : initialLimit;
610
805
  const type = initialWhere.published ? "P" : "L";
611
- const queryAllParams = {
612
- entity,
613
- partitionKey: (0, _keys.createGSIPartitionKey)(model, type),
614
- options: {
615
- index: "GSI1",
616
- gte: " "
617
- }
806
+ const partitionKey = createGSIPartitionKey(model, type);
807
+ const options = {
808
+ index: "GSI1",
809
+ gte: " "
618
810
  };
619
811
  let storageEntries = [];
620
812
  try {
621
- storageEntries = await (0, _query.queryAll)(queryAllParams);
813
+ storageEntries = await entity.queryAll({
814
+ partitionKey,
815
+ options
816
+ });
622
817
  } catch (ex) {
623
- throw new _error.default(ex.message, "QUERY_ENTRIES_ERROR", {
818
+ throw new WebinyError(ex.message, "QUERY_ENTRIES_ERROR", {
624
819
  error: ex,
625
- partitionKey: queryAllParams.partitionKey,
626
- options: queryAllParams.options
820
+ partitionKey,
821
+ options
627
822
  });
628
823
  }
629
824
  if (storageEntries.length === 0) {
@@ -634,14 +829,16 @@ const createEntriesStorageOperations = params => {
634
829
  items: []
635
830
  };
636
831
  }
637
- const where = (0, _objectSpread2.default)({}, initialWhere);
832
+ const where = {
833
+ ...initialWhere
834
+ };
638
835
  delete where["published"];
639
836
  delete where["latest"];
640
837
  /**
641
838
  * We need an object containing field, transformers and paths.
642
839
  * Just build it here and pass on into other methods that require it to avoid mapping multiple times.
643
840
  */
644
- const modelFields = (0, _createFields.createFields)({
841
+ const modelFields = createFields({
645
842
  plugins,
646
843
  fields: model.fields
647
844
  });
@@ -653,7 +850,7 @@ const createEntriesStorageOperations = params => {
653
850
  */
654
851
  const records = await Promise.all(storageEntries.map(async storageEntry => {
655
852
  const entry = convertFromStorageEntry({
656
- storageEntry,
853
+ storageEntry: storageEntry.data,
657
854
  model
658
855
  });
659
856
  for (const field of model.fields) {
@@ -665,7 +862,7 @@ const createEntriesStorageOperations = params => {
665
862
  * Filter the read items via the code.
666
863
  * It will build the filters out of the where input and transform the values it is using.
667
864
  */
668
- const filteredItems = (0, _filtering.filter)({
865
+ const filteredItems = filter({
669
866
  items: records,
670
867
  where,
671
868
  plugins,
@@ -681,14 +878,14 @@ const createEntriesStorageOperations = params => {
681
878
  * Sorting is also done via the code.
682
879
  * It takes the sort input and sorts by it via the lodash sortBy method.
683
880
  */
684
- const sortedItems = (0, _filtering.sort)({
881
+ const sortedItems = sort({
685
882
  model,
686
883
  plugins,
687
884
  items: filteredItems,
688
885
  sort: sortBy,
689
886
  fields: modelFields
690
887
  });
691
- const start = parseInt((0, _cursor.decodeCursor)(after) || "0") || 0;
888
+ const start = parseInt(decodeCursor(after) || "0") || 0;
692
889
  const hasMoreItems = totalCount > start + limit;
693
890
  const end = limit > totalCount + start + limit ? undefined : start + limit;
694
891
  const slicedItems = sortedItems.slice(start, end);
@@ -696,21 +893,22 @@ const createEntriesStorageOperations = params => {
696
893
  * Although we do not need a cursor here, we will use it as such to keep it standardized.
697
894
  * Number is simply encoded.
698
895
  */
699
- const cursor = totalCount > start + limit ? (0, _cursor.encodeCursor)(`${start + limit}`) : null;
896
+ const cursor = encodeCursor(`${start + limit}`);
700
897
  return {
701
898
  hasMoreItems,
702
899
  totalCount,
703
900
  cursor,
704
- items: (0, _cleanup.cleanupItems)(entity, slicedItems)
901
+ items: slicedItems
705
902
  };
706
903
  };
707
904
  const get = async (initialModel, params) => {
708
905
  const model = getStorageOperationsModel(initialModel);
709
906
  const {
710
907
  items
711
- } = await list(model, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, params), {}, {
908
+ } = await list(model, {
909
+ ...params,
712
910
  limit: 1
713
- }));
911
+ });
714
912
  return items.shift() || null;
715
913
  };
716
914
  const publish = async (initialModel, params) => {
@@ -719,75 +917,125 @@ const createEntriesStorageOperations = params => {
719
917
  storageEntry: initialStorageEntry
720
918
  } = params;
721
919
  const model = getStorageOperationsModel(initialModel);
722
- const partitionKey = (0, _keys.createPartitionKey)({
723
- id: entry.id,
724
- locale: model.locale,
725
- tenant: model.tenant
726
- });
727
920
 
728
921
  /**
729
922
  * We need the latest and published entries to see if something needs to be updated alongside the publishing one.
730
923
  */
731
924
  const initialLatestStorageEntry = await getLatestRevisionByEntryId(model, entry);
925
+ if (!initialLatestStorageEntry) {
926
+ throw new WebinyError(`Could not publish entry. Could not load latest ("L") record.`, "PUBLISH_ERROR", {
927
+ entry
928
+ });
929
+ }
732
930
  const initialPublishedStorageEntry = await getPublishedRevisionByEntryId(model, entry);
733
931
  const storageEntry = convertToStorageEntry({
734
932
  model,
735
933
  storageEntry: initialStorageEntry
736
934
  });
737
- /**
738
- * We need to update:
739
- * - current entry revision sort key
740
- * - published sort key
741
- * - the latest sort key - if entry updated is actually latest
742
- * - previous published entry to unpublished status - if any previously published entry
743
- */
744
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
745
- PK: partitionKey,
746
- SK: (0, _keys.createRevisionSortKey)(entry),
747
- TYPE: createType(),
748
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
749
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
750
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
751
- PK: partitionKey,
752
- SK: (0, _keys.createPublishedSortKey)(),
753
- TYPE: createPublishedType(),
754
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
755
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
756
- }))];
757
- if (initialLatestStorageEntry && entry.id === initialLatestStorageEntry.id) {
758
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
759
- PK: partitionKey,
760
- SK: (0, _keys.createLatestSortKey)(),
761
- TYPE: createLatestType(),
762
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
763
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
764
- })));
765
- }
766
- if (initialPublishedStorageEntry && initialPublishedStorageEntry.id !== entry.id) {
767
- const publishedStorageEntry = convertToStorageEntry({
768
- storageEntry: initialPublishedStorageEntry,
935
+
936
+ // 1. Update REV# and P records with new data.
937
+ const entityBatch = entity.createEntityWriter({
938
+ put: [{
939
+ ...createEntryRevisionKeys(storageEntry),
940
+ data: {
941
+ ...storageEntry
942
+ }
943
+ }, {
944
+ ...createEntryPublishedKeys(storageEntry),
945
+ data: {
946
+ ...storageEntry
947
+ }
948
+ }]
949
+ });
950
+
951
+ // 2. When it comes to the latest record, we need to perform a couple of different
952
+ // updates, based on whether the entry being published is the latest revision or not.
953
+ const publishedRevisionId = initialPublishedStorageEntry?.id;
954
+ const publishingLatestRevision = entry.id === initialLatestStorageEntry.id;
955
+ if (publishingLatestRevision) {
956
+ // 2.1 If we're publishing the latest revision, we first need to update the L record.
957
+ entityBatch.put({
958
+ ...createEntryLatestKeys(storageEntry),
959
+ data: {
960
+ ...storageEntry
961
+ }
962
+ });
963
+
964
+ // 2.2 Additionally, if we have a previously published entry, we need to mark it as unpublished.
965
+ if (publishedRevisionId && publishedRevisionId !== entry.id) {
966
+ const publishedStorageEntry = convertToStorageEntry({
967
+ storageEntry: initialPublishedStorageEntry,
968
+ model
969
+ });
970
+ entityBatch.put({
971
+ ...createEntryRevisionKeys(publishedStorageEntry),
972
+ data: {
973
+ ...publishedStorageEntry,
974
+ status: CONTENT_ENTRY_STATUS.UNPUBLISHED
975
+ }
976
+ });
977
+ }
978
+ } else {
979
+ // 2.3 If the published revision is not the latest one, the situation is a bit
980
+ // more complex. We first need to update the L and REV# records with the new
981
+ // values of *only entry-level* meta fields.
982
+ const updatedEntryLevelMetaFields = pickEntryMetaFields(entry, isEntryLevelEntryMetaField);
983
+ const latestStorageEntry = convertToStorageEntry({
984
+ storageEntry: initialLatestStorageEntry,
769
985
  model
770
986
  });
771
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, publishedStorageEntry), {}, {
772
- PK: partitionKey,
773
- SK: (0, _keys.createRevisionSortKey)(publishedStorageEntry),
774
- TYPE: createType(),
775
- status: _types.CONTENT_ENTRY_STATUS.UNPUBLISHED,
776
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
777
- GSI1_SK: (0, _keys.createGSISortKey)(publishedStorageEntry)
778
- })));
987
+
988
+ // 2.3.1 Update L record. Apart from updating the entry-level meta fields, we also need
989
+ // to change the status from "published" to "unpublished" (if the status is set to "published").
990
+ let latestRevisionStatus = latestStorageEntry.status;
991
+ if (latestRevisionStatus === CONTENT_ENTRY_STATUS.PUBLISHED) {
992
+ latestRevisionStatus = CONTENT_ENTRY_STATUS.UNPUBLISHED;
993
+ }
994
+ const latestStorageEntryFields = {
995
+ ...latestStorageEntry,
996
+ ...updatedEntryLevelMetaFields,
997
+ status: latestRevisionStatus
998
+ };
999
+ entityBatch.put({
1000
+ ...createEntryLatestKeys(latestStorageEntryFields),
1001
+ data: {
1002
+ ...latestStorageEntryFields
1003
+ }
1004
+ });
1005
+
1006
+ // 2.3.2 Update REV# record.
1007
+ entityBatch.put({
1008
+ ...createEntryRevisionKeys(latestStorageEntryFields),
1009
+ data: {
1010
+ ...latestStorageEntryFields
1011
+ }
1012
+ });
1013
+
1014
+ // 2.3.3 Finally, if we got a published entry, but it wasn't the latest one, we need to take
1015
+ // an extra step and mark it as unpublished.
1016
+ const publishedRevisionDifferentFromLatest = publishedRevisionId && publishedRevisionId !== latestStorageEntry.id;
1017
+ if (publishedRevisionDifferentFromLatest) {
1018
+ const publishedStorageEntry = convertToStorageEntry({
1019
+ storageEntry: initialPublishedStorageEntry,
1020
+ model
1021
+ });
1022
+ entityBatch.put({
1023
+ ...createEntryRevisionKeys(publishedStorageEntry),
1024
+ data: {
1025
+ ...publishedStorageEntry,
1026
+ status: CONTENT_ENTRY_STATUS.UNPUBLISHED
1027
+ }
1028
+ });
1029
+ }
779
1030
  }
780
1031
  try {
781
- await (0, _batchWrite.batchWriteAll)({
782
- table: entity.table,
783
- items
784
- });
1032
+ await entityBatch.execute();
785
1033
  dataLoaders.clearAll({
786
1034
  model
787
1035
  });
788
1036
  return initialStorageEntry;
789
1037
  } catch (ex) {
790
- throw new _error.default(ex.message || "Could not execute the publishing batch.", ex.code || "PUBLISH_ERROR", {
1038
+ throw new WebinyError(ex.message || "Could not execute the publishing batch.", ex.code || "PUBLISH_ERROR", {
791
1039
  entry,
792
1040
  latestStorageEntry: initialLatestStorageEntry,
793
1041
  publishedStorageEntry: initialPublishedStorageEntry
@@ -800,9 +1048,8 @@ const createEntriesStorageOperations = params => {
800
1048
  storageEntry: initialStorageEntry
801
1049
  } = params;
802
1050
  const model = getStorageOperationsModel(initialModel);
803
- const partitionKey = (0, _keys.createPartitionKey)({
1051
+ const partitionKey = createPartitionKey({
804
1052
  id: entry.id,
805
- locale: model.locale,
806
1053
  tenant: model.tenant
807
1054
  });
808
1055
  const storageEntry = convertToStorageEntry({
@@ -815,41 +1062,69 @@ const createEntriesStorageOperations = params => {
815
1062
  * - update current entry revision with new data
816
1063
  * - update the latest entry status - if entry being unpublished is latest
817
1064
  */
818
- const items = [entity.deleteBatch({
819
- PK: partitionKey,
820
- SK: (0, _keys.createPublishedSortKey)()
821
- }), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
822
- PK: partitionKey,
823
- SK: (0, _keys.createRevisionSortKey)(entry),
824
- TYPE: createType(),
825
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
826
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
827
- }))];
1065
+ const entityBatch = entity.createEntityWriter({
1066
+ delete: [{
1067
+ PK: partitionKey,
1068
+ SK: createPublishedSortKey()
1069
+ }],
1070
+ put: [{
1071
+ ...createEntryRevisionKeys(storageEntry),
1072
+ data: {
1073
+ ...storageEntry
1074
+ }
1075
+ }]
1076
+ });
828
1077
 
829
1078
  /**
830
1079
  * We need the latest entry to see if something needs to be updated alongside the unpublishing one.
831
1080
  */
832
- const latestStorageEntry = await getLatestRevisionByEntryId(model, entry);
833
- if (latestStorageEntry && entry.id === latestStorageEntry.id) {
834
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
835
- PK: partitionKey,
836
- SK: (0, _keys.createLatestSortKey)(),
837
- TYPE: createLatestType(),
838
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
839
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
840
- })));
1081
+ const initialLatestStorageEntry = await getLatestRevisionByEntryId(model, entry);
1082
+ if (initialLatestStorageEntry) {
1083
+ const unpublishingLatestRevision = entry.id === initialLatestStorageEntry.id;
1084
+ if (unpublishingLatestRevision) {
1085
+ entityBatch.put({
1086
+ ...createEntryLatestKeys(storageEntry),
1087
+ data: {
1088
+ ...storageEntry
1089
+ }
1090
+ });
1091
+ } else {
1092
+ const latestStorageEntry = convertToStorageEntry({
1093
+ storageEntry: initialLatestStorageEntry,
1094
+ model
1095
+ });
1096
+
1097
+ // If the unpublished revision is not the latest one, we still need to
1098
+ // update the latest record with the new values of entry-level meta fields.
1099
+ const updatedEntryLevelMetaFields = pickEntryMetaFields(entry, isEntryLevelEntryMetaField);
1100
+
1101
+ // 1. Update actual revision record.
1102
+ entityBatch.put({
1103
+ ...createEntryRevisionKeys(latestStorageEntry),
1104
+ data: {
1105
+ ...latestStorageEntry,
1106
+ ...updatedEntryLevelMetaFields
1107
+ }
1108
+ });
1109
+
1110
+ // 2. Update latest record.
1111
+ entityBatch.put({
1112
+ ...createEntryLatestKeys(latestStorageEntry),
1113
+ data: {
1114
+ ...latestStorageEntry,
1115
+ ...updatedEntryLevelMetaFields
1116
+ }
1117
+ });
1118
+ }
841
1119
  }
842
1120
  try {
843
- await (0, _batchWrite.batchWriteAll)({
844
- table: entity.table,
845
- items
846
- });
1121
+ await entityBatch.execute();
847
1122
  dataLoaders.clearAll({
848
1123
  model
849
1124
  });
850
1125
  return initialStorageEntry;
851
1126
  } catch (ex) {
852
- throw new _error.default(ex.message || "Could not execute unpublish batch.", ex.code || "UNPUBLISH_ERROR", {
1127
+ throw new WebinyError(ex.message || "Could not execute unpublish batch.", ex.code || "UNPUBLISH_ERROR", {
853
1128
  entry,
854
1129
  storageEntry
855
1130
  });
@@ -862,7 +1137,7 @@ const createEntriesStorageOperations = params => {
862
1137
  } = params;
863
1138
  const field = model.fields.find(f => f.fieldId === fieldId);
864
1139
  if (!field) {
865
- throw new _error.default(`Could not find field with given "fieldId" value.`, "FIELD_NOT_FOUND", {
1140
+ throw new WebinyError(`Could not find field with given "fieldId" value.`, "FIELD_NOT_FOUND", {
866
1141
  fieldId
867
1142
  });
868
1143
  }
@@ -872,13 +1147,33 @@ const createEntriesStorageOperations = params => {
872
1147
  where,
873
1148
  limit: MAX_LIST_LIMIT
874
1149
  });
875
- return Array.from(new Set(items.map(item => item.values[field.fieldId])));
1150
+ const result = {};
1151
+ for (const item of items) {
1152
+ const fieldValue = item.values[field.fieldId];
1153
+ if (!fieldValue) {
1154
+ continue;
1155
+ }
1156
+ const values = Array.isArray(fieldValue) ? fieldValue : [fieldValue];
1157
+ if (values.length === 0) {
1158
+ continue;
1159
+ }
1160
+ for (const value of values) {
1161
+ result[value] = {
1162
+ value,
1163
+ count: (result[value]?.count || 0) + 1
1164
+ };
1165
+ }
1166
+ }
1167
+ return Object.values(result).sort((a, b) => a.value > b.value ? 1 : b.value > a.value ? -1 : 0).sort((a, b) => b.count - a.count);
876
1168
  };
877
1169
  return {
878
1170
  create,
879
1171
  createRevisionFrom,
880
1172
  update,
1173
+ move,
881
1174
  delete: deleteEntry,
1175
+ moveToBin,
1176
+ restoreFromBin,
882
1177
  deleteRevision,
883
1178
  deleteMultipleEntries,
884
1179
  getPreviousRevision,
@@ -897,4 +1192,5 @@ const createEntriesStorageOperations = params => {
897
1192
  getUniqueFieldValues
898
1193
  };
899
1194
  };
900
- exports.createEntriesStorageOperations = createEntriesStorageOperations;
1195
+
1196
+ //# sourceMappingURL=index.js.map