@webiny/api-headless-cms-ddb 0.0.0-unstable.bca7b3e350 → 0.0.0-unstable.c27f4d8a31

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 (169) hide show
  1. package/README.md +6 -20
  2. package/definitions/entry.d.ts +4 -5
  3. package/definitions/entry.js +6 -89
  4. package/definitions/entry.js.map +1 -1
  5. package/definitions/group.d.ts +4 -5
  6. package/definitions/group.js +6 -63
  7. package/definitions/group.js.map +1 -1
  8. package/definitions/model.d.ts +4 -5
  9. package/definitions/model.js +6 -90
  10. package/definitions/model.js.map +1 -1
  11. package/definitions/table.d.ts +4 -6
  12. package/definitions/table.js +7 -28
  13. package/definitions/table.js.map +1 -1
  14. package/definitions/types.d.ts +60 -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 +8 -20
  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 +6 -20
  25. package/dynamoDb/path/plainObject.js.map +1 -1
  26. package/dynamoDb/storage/longText.js +13 -35
  27. package/dynamoDb/storage/longText.js.map +1 -1
  28. package/dynamoDb/storage/richText.js +27 -88
  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 -19
  32. package/dynamoDb/transformValue/datetime.js.map +1 -1
  33. package/index.d.ts +2 -2
  34. package/index.js +47 -111
  35. package/index.js.map +1 -1
  36. package/operations/entry/dataLoader/DataLoaderCache.d.ts +16 -0
  37. package/operations/entry/dataLoader/DataLoaderCache.js +31 -0
  38. package/operations/entry/dataLoader/DataLoaderCache.js.map +1 -0
  39. package/operations/entry/dataLoader/constants.d.ts +1 -0
  40. package/operations/entry/dataLoader/constants.js +4 -0
  41. package/operations/entry/dataLoader/constants.js.map +1 -0
  42. package/operations/entry/dataLoader/createBatchScheduleFn.d.ts +8 -0
  43. package/operations/entry/dataLoader/createBatchScheduleFn.js +19 -0
  44. package/operations/entry/dataLoader/createBatchScheduleFn.js.map +1 -0
  45. package/operations/entry/dataLoader/getAllEntryRevisions.d.ts +4 -0
  46. package/operations/entry/dataLoader/getAllEntryRevisions.js +35 -0
  47. package/operations/entry/dataLoader/getAllEntryRevisions.js.map +1 -0
  48. package/operations/entry/dataLoader/getLatestRevisionByEntryId.d.ts +4 -0
  49. package/operations/entry/dataLoader/getLatestRevisionByEntryId.js +50 -0
  50. package/operations/entry/dataLoader/getLatestRevisionByEntryId.js.map +1 -0
  51. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.d.ts +4 -0
  52. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.js +50 -0
  53. package/operations/entry/dataLoader/getPublishedRevisionByEntryId.js.map +1 -0
  54. package/operations/entry/dataLoader/getRevisionById.d.ts +4 -0
  55. package/operations/entry/dataLoader/getRevisionById.js +56 -0
  56. package/operations/entry/dataLoader/getRevisionById.js.map +1 -0
  57. package/operations/entry/dataLoader/index.d.ts +8 -0
  58. package/operations/entry/dataLoader/index.js +19 -0
  59. package/operations/entry/dataLoader/index.js.map +1 -0
  60. package/operations/entry/dataLoader/types.d.ts +6 -0
  61. package/operations/entry/dataLoader/types.js +3 -0
  62. package/operations/entry/dataLoader/types.js.map +1 -0
  63. package/operations/entry/dataLoaders.d.ts +14 -30
  64. package/operations/entry/dataLoaders.js +58 -271
  65. package/operations/entry/dataLoaders.js.map +1 -1
  66. package/operations/entry/filtering/createExpressions.d.ts +5 -5
  67. package/operations/entry/filtering/createExpressions.js +30 -68
  68. package/operations/entry/filtering/createExpressions.js.map +1 -1
  69. package/operations/entry/filtering/createFields.d.ts +6 -4
  70. package/operations/entry/filtering/createFields.js +86 -103
  71. package/operations/entry/filtering/createFields.js.map +1 -1
  72. package/operations/entry/filtering/extractSort.d.ts +6 -6
  73. package/operations/entry/filtering/extractSort.js +39 -32
  74. package/operations/entry/filtering/extractSort.js.map +1 -1
  75. package/operations/entry/filtering/filter.d.ts +6 -6
  76. package/operations/entry/filtering/filter.js +36 -64
  77. package/operations/entry/filtering/filter.js.map +1 -1
  78. package/operations/entry/filtering/fullTextSearch.d.ts +3 -3
  79. package/operations/entry/filtering/fullTextSearch.js +6 -24
  80. package/operations/entry/filtering/fullTextSearch.js.map +1 -1
  81. package/operations/entry/filtering/getValue.js +5 -27
  82. package/operations/entry/filtering/getValue.js.map +1 -1
  83. package/operations/entry/filtering/index.d.ts +2 -2
  84. package/operations/entry/filtering/index.js +3 -20
  85. package/operations/entry/filtering/index.js.map +1 -1
  86. package/operations/entry/filtering/mapPlugins.d.ts +2 -2
  87. package/operations/entry/filtering/mapPlugins.js +4 -18
  88. package/operations/entry/filtering/mapPlugins.js.map +1 -1
  89. package/operations/entry/filtering/plugins/defaultFilterCreate.d.ts +2 -2
  90. package/operations/entry/filtering/plugins/defaultFilterCreate.js +9 -20
  91. package/operations/entry/filtering/plugins/defaultFilterCreate.js.map +1 -1
  92. package/operations/entry/filtering/plugins/index.d.ts +1 -1
  93. package/operations/entry/filtering/plugins/index.js +7 -16
  94. package/operations/entry/filtering/plugins/index.js.map +1 -1
  95. package/operations/entry/filtering/plugins/objectFilterCreate.d.ts +2 -2
  96. package/operations/entry/filtering/plugins/objectFilterCreate.js +16 -39
  97. package/operations/entry/filtering/plugins/objectFilterCreate.js.map +1 -1
  98. package/operations/entry/filtering/plugins/refFilterCreate.d.ts +3 -2
  99. package/operations/entry/filtering/plugins/refFilterCreate.js +21 -36
  100. package/operations/entry/filtering/plugins/refFilterCreate.js.map +1 -1
  101. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.d.ts +2 -0
  102. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.js +57 -0
  103. package/operations/entry/filtering/plugins/searchableJsonFilterCreate.js.map +1 -0
  104. package/operations/entry/filtering/sort.d.ts +6 -6
  105. package/operations/entry/filtering/sort.js +17 -34
  106. package/operations/entry/filtering/sort.js.map +1 -1
  107. package/operations/entry/filtering/systemFields.d.ts +2 -4
  108. package/operations/entry/filtering/systemFields.js +102 -43
  109. package/operations/entry/filtering/systemFields.js.map +1 -1
  110. package/operations/entry/filtering/transform.d.ts +1 -1
  111. package/operations/entry/filtering/transform.js +2 -10
  112. package/operations/entry/filtering/transform.js.map +1 -1
  113. package/operations/entry/filtering/types.d.ts +3 -3
  114. package/operations/entry/filtering/types.js +2 -4
  115. package/operations/entry/filtering/types.js.map +1 -1
  116. package/operations/entry/filtering/values.d.ts +1 -1
  117. package/operations/entry/filtering/values.js +5 -17
  118. package/operations/entry/filtering/values.js.map +1 -1
  119. package/operations/entry/filtering/where.js +5 -15
  120. package/operations/entry/filtering/where.js.map +1 -1
  121. package/operations/entry/index.d.ts +3 -4
  122. package/operations/entry/index.js +732 -410
  123. package/operations/entry/index.js.map +1 -1
  124. package/operations/entry/keys.d.ts +40 -2
  125. package/operations/entry/keys.js +54 -47
  126. package/operations/entry/keys.js.map +1 -1
  127. package/operations/group/index.d.ts +4 -4
  128. package/operations/group/index.js +44 -91
  129. package/operations/group/index.js.map +1 -1
  130. package/operations/model/index.d.ts +3 -3
  131. package/operations/model/index.js +36 -70
  132. package/operations/model/index.js.map +1 -1
  133. package/package.json +22 -29
  134. package/plugins/CmsEntryFieldFilterPathPlugin.d.ts +4 -3
  135. package/plugins/CmsEntryFieldFilterPathPlugin.js +14 -29
  136. package/plugins/CmsEntryFieldFilterPathPlugin.js.map +1 -1
  137. package/plugins/CmsEntryFieldFilterPlugin.d.ts +10 -10
  138. package/plugins/CmsEntryFieldFilterPlugin.js +9 -19
  139. package/plugins/CmsEntryFieldFilterPlugin.js.map +1 -1
  140. package/plugins/CmsEntryFieldSortingPlugin.d.ts +2 -2
  141. package/plugins/CmsEntryFieldSortingPlugin.js +5 -24
  142. package/plugins/CmsEntryFieldSortingPlugin.js.map +1 -1
  143. package/plugins/CmsFieldFilterValueTransformPlugin.d.ts +11 -0
  144. package/plugins/CmsFieldFilterValueTransformPlugin.js +16 -0
  145. package/plugins/CmsFieldFilterValueTransformPlugin.js.map +1 -0
  146. package/plugins/index.d.ts +4 -3
  147. package/plugins/index.js +5 -43
  148. package/plugins/index.js.map +1 -1
  149. package/types.d.ts +24 -19
  150. package/types.js +5 -13
  151. package/types.js.map +1 -1
  152. package/definitions/settings.d.ts +0 -9
  153. package/definitions/settings.js +0 -58
  154. package/definitions/settings.js.map +0 -1
  155. package/definitions/system.d.ts +0 -9
  156. package/definitions/system.js +0 -46
  157. package/definitions/system.js.map +0 -1
  158. package/dynamoDb/storage/date.d.ts +0 -3
  159. package/dynamoDb/storage/date.js +0 -109
  160. package/dynamoDb/storage/date.js.map +0 -1
  161. package/operations/entry/systemFields.d.ts +0 -2
  162. package/operations/entry/systemFields.js +0 -74
  163. package/operations/entry/systemFields.js.map +0 -1
  164. package/operations/settings/index.d.ts +0 -7
  165. package/operations/settings/index.js +0 -135
  166. package/operations/settings/index.js.map +0 -1
  167. package/operations/system/index.d.ts +0 -7
  168. package/operations/system/index.js +0 -99
  169. package/operations/system/index.js.map +0 -1
@@ -1,50 +1,13 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
-
5
- Object.defineProperty(exports, "__esModule", {
6
- value: true
7
- });
8
- exports.createEntriesStorageOperations = void 0;
9
-
10
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
11
-
12
- var _error = _interopRequireDefault(require("@webiny/error"));
13
-
14
- var _dataLoaders = require("./dataLoaders");
15
-
16
- var _types = require("@webiny/api-headless-cms/types");
17
-
18
- var _keys = require("./keys");
19
-
20
- var _batchWrite = require("@webiny/db-dynamodb/utils/batchWrite");
21
-
22
- var _query = require("@webiny/db-dynamodb/utils/query");
23
-
24
- var _cleanup = require("@webiny/db-dynamodb/utils/cleanup");
25
-
26
- var _cursor = require("@webiny/utils/cursor");
27
-
28
- var _zeroPad = require("@webiny/utils/zeroPad");
29
-
30
- var _apiHeadlessCms = require("@webiny/api-headless-cms");
31
-
32
- var _createFields = require("./filtering/createFields");
33
-
34
- var _filtering = require("./filtering");
35
-
36
- const createType = () => {
37
- return "cms.entry";
38
- };
39
-
40
- const createLatestType = () => {
41
- return `${createType()}.l`;
42
- };
43
-
44
- const createPublishedType = () => {
45
- return `${createType()}.p`;
46
- };
47
-
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";
48
11
  const convertToStorageEntry = params => {
49
12
  const {
50
13
  model,
@@ -54,11 +17,11 @@ const convertToStorageEntry = params => {
54
17
  fields: model.fields,
55
18
  values: storageEntry.values
56
19
  });
57
- return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
20
+ return {
21
+ ...storageEntry,
58
22
  values
59
- });
23
+ };
60
24
  };
61
-
62
25
  const convertFromStorageEntry = params => {
63
26
  const {
64
27
  model,
@@ -68,160 +31,179 @@ const convertFromStorageEntry = params => {
68
31
  fields: model.fields,
69
32
  values: storageEntry.values
70
33
  });
71
- return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
34
+ return {
35
+ ...storageEntry,
72
36
  values
73
- });
37
+ };
74
38
  };
75
-
76
- const createEntriesStorageOperations = params => {
39
+ const MAX_LIST_LIMIT = 1000000;
40
+ export const createEntriesStorageOperations = params => {
77
41
  const {
78
42
  entity,
79
43
  plugins
80
44
  } = params;
81
- const dataLoaders = new _dataLoaders.DataLoadersHandler({
45
+ let storageOperationsCmsModelPlugin;
46
+ const getStorageOperationsCmsModelPlugin = () => {
47
+ if (storageOperationsCmsModelPlugin) {
48
+ return storageOperationsCmsModelPlugin;
49
+ }
50
+ storageOperationsCmsModelPlugin = plugins.oneByType(StorageOperationsCmsModelPlugin.type);
51
+ return storageOperationsCmsModelPlugin;
52
+ };
53
+ const getStorageOperationsModel = model => {
54
+ const plugin = getStorageOperationsCmsModelPlugin();
55
+ return plugin.getModel(model);
56
+ };
57
+ const dataLoaders = new DataLoadersHandler({
82
58
  entity
83
59
  });
84
- const storageTransformPlugins = plugins.byType(_apiHeadlessCms.StorageTransformPlugin.type).reduce((collection, plugin) => {
85
- collection[plugin.fieldType] = plugin;
86
- return collection;
87
- }, {});
88
-
89
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
+ }, {});
90
66
  return (field, value) => {
91
- const plugin = storageTransformPlugins[field.type];
92
-
67
+ const fieldType = getBaseFieldType(field);
68
+ const plugin = storageTransformPlugins[fieldType];
93
69
  if (!plugin) {
94
70
  return value;
95
71
  }
96
-
97
72
  return plugin.fromStorage({
98
73
  model,
99
74
  field,
100
75
  value,
101
-
102
76
  getStoragePlugin(fieldType) {
103
77
  return storageTransformPlugins[fieldType] || storageTransformPlugins["*"];
104
78
  },
105
-
106
79
  plugins
107
80
  });
108
81
  };
109
82
  };
110
-
111
- const create = async (model, params) => {
83
+ const create = async (initialModel, params) => {
112
84
  const {
113
85
  entry,
114
86
  storageEntry: initialStorageEntry
115
87
  } = params;
116
- const partitionKey = (0, _keys.createPartitionKey)({
117
- id: entry.id,
118
- locale: model.locale,
119
- tenant: model.tenant
120
- });
88
+ const model = getStorageOperationsModel(initialModel);
121
89
  const isPublished = entry.status === "published";
122
90
  const locked = isPublished ? true : entry.locked;
123
91
  const storageEntry = convertToStorageEntry({
124
92
  model,
125
93
  storageEntry: initialStorageEntry
126
94
  });
95
+ const storageEntryRevisionKeys = createEntryRevisionKeys(entry);
96
+ const storageEntryLatestKeys = createEntryLatestKeys(entry);
127
97
  /**
128
98
  * We need to:
129
99
  * - create new main entry item
130
- * - create new or update latest entry item
100
+ * - create new or update the latest entry item
131
101
  */
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
+ });
132
117
 
133
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
134
- locked,
135
- PK: partitionKey,
136
- SK: (0, _keys.createRevisionSortKey)(entry),
137
- TYPE: createType(),
138
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
139
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
140
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
141
- locked,
142
- PK: partitionKey,
143
- SK: (0, _keys.createLatestSortKey)(),
144
- TYPE: createLatestType(),
145
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
146
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
147
- }))];
148
118
  /**
149
119
  * We need to create published entry if
150
120
  */
151
-
152
121
  if (isPublished) {
153
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
154
- locked,
155
- PK: partitionKey,
156
- SK: (0, _keys.createPublishedSortKey)(),
157
- TYPE: createLatestType(),
158
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
159
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
160
- })));
122
+ const storageEntryPublishedKeys = createEntryPublishedKeys(storageEntry);
123
+ entityBatch.put({
124
+ ...storageEntryPublishedKeys,
125
+ data: {
126
+ ...storageEntry,
127
+ locked
128
+ }
129
+ });
161
130
  }
162
-
163
131
  try {
164
- await (0, _batchWrite.batchWriteAll)({
165
- table: entity.table,
166
- items
167
- });
132
+ await entityBatch.execute();
168
133
  dataLoaders.clearAll({
169
134
  model
170
135
  });
171
136
  } catch (ex) {
172
- 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", {
173
138
  error: ex,
174
139
  entry
175
140
  });
176
141
  }
177
-
178
142
  return initialStorageEntry;
179
143
  };
180
-
181
- const createRevisionFrom = async (model, params) => {
144
+ const createRevisionFrom = async (initialModel, params) => {
182
145
  const {
183
146
  entry,
184
147
  storageEntry: initialStorageEntry
185
148
  } = params;
186
- const partitionKey = (0, _keys.createPartitionKey)({
187
- id: entry.id,
188
- locale: model.locale,
189
- tenant: model.tenant
190
- });
149
+ const model = getStorageOperationsModel(initialModel);
191
150
  const storageEntry = convertToStorageEntry({
192
151
  storageEntry: initialStorageEntry,
193
152
  model
194
153
  });
154
+
195
155
  /**
196
156
  * We need to:
197
157
  * - create the main entry item
198
- * - 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)
199
162
  */
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
+ });
200
184
 
201
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
202
- PK: partitionKey,
203
- SK: (0, _keys.createRevisionSortKey)(storageEntry),
204
- TYPE: createType(),
205
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
206
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
207
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
208
- PK: partitionKey,
209
- SK: (0, _keys.createLatestSortKey)(),
210
- TYPE: createLatestType(),
211
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
212
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
213
- }))];
214
-
215
- try {
216
- await (0, _batchWrite.batchWriteAll)({
217
- table: entity.table,
218
- items
185
+ // Unpublish previously published revision (if any).
186
+ const [publishedRevisionStorageEntry] = await dataLoaders.getPublishedRevisionByEntryId({
187
+ model,
188
+ ids: [entry.id]
219
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();
220
202
  dataLoaders.clearAll({
221
203
  model
222
204
  });
223
205
  } catch (ex) {
224
- 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", {
225
207
  error: ex,
226
208
  entry,
227
209
  storageEntry
@@ -230,24 +212,16 @@ const createEntriesStorageOperations = params => {
230
212
  /**
231
213
  * There are no modifications on the entry created so just return the data.
232
214
  */
233
-
234
-
235
215
  return initialStorageEntry;
236
216
  };
237
-
238
- const update = async (model, params) => {
217
+ const update = async (initialModel, params) => {
239
218
  const {
240
219
  entry,
241
220
  storageEntry: initialStorageEntry
242
221
  } = params;
243
- const partitionKey = (0, _keys.createPartitionKey)({
244
- id: entry.id,
245
- locale: model.locale,
246
- tenant: model.tenant
247
- });
222
+ const model = getStorageOperationsModel(initialModel);
248
223
  const isPublished = entry.status === "published";
249
224
  const locked = isPublished ? true : entry.locked;
250
- const items = [];
251
225
  const storageEntry = convertToStorageEntry({
252
226
  model,
253
227
  storageEntry: initialStorageEntry
@@ -258,221 +232,477 @@ const createEntriesStorageOperations = params => {
258
232
  * - update the latest entry if the current entry is the latest one
259
233
  */
260
234
 
261
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
262
- locked,
263
- PK: partitionKey,
264
- SK: (0, _keys.createRevisionSortKey)(storageEntry),
265
- TYPE: createType(),
266
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
267
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
268
- })));
269
-
235
+ const entityBatch = entity.createEntityWriter({
236
+ put: [{
237
+ ...createEntryRevisionKeys(storageEntry),
238
+ data: {
239
+ ...storageEntry,
240
+ locked
241
+ }
242
+ }]
243
+ });
270
244
  if (isPublished) {
271
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
272
- locked,
273
- PK: partitionKey,
274
- SK: (0, _keys.createPublishedSortKey)(),
275
- TYPE: createPublishedType(),
276
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
277
- GSI1_SK: (0, _keys.createGSISortKey)(storageEntry)
278
- })));
245
+ entityBatch.put({
246
+ ...createEntryPublishedKeys(storageEntry),
247
+ data: {
248
+ ...storageEntry,
249
+ locked
250
+ }
251
+ });
279
252
  }
253
+
280
254
  /**
281
- * We need the latest entry to update it as well if neccessary.
255
+ * We need the latest entry to update it as well if necessary.
282
256
  */
283
-
284
-
285
257
  const latestStorageEntry = await getLatestRevisionByEntryId(model, entry);
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);
286
274
 
287
- if (latestStorageEntry && latestStorageEntry.id === entry.id) {
288
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
289
- locked,
290
- PK: partitionKey,
291
- SK: (0, _keys.createLatestSortKey)(),
292
- TYPE: createLatestType(),
293
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
294
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
295
- })));
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
+ }
296
295
  }
297
-
298
296
  try {
299
- await (0, _batchWrite.batchWriteAll)({
300
- table: entity.table,
301
- items
302
- });
297
+ await entityBatch.execute();
303
298
  dataLoaders.clearAll({
304
299
  model
305
300
  });
306
301
  return initialStorageEntry;
307
302
  } catch (ex) {
308
- 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", {
309
304
  error: ex,
310
305
  entry,
311
306
  latestStorageEntry
312
307
  });
313
308
  }
314
309
  };
315
-
316
- const deleteEntry = async (model, params) => {
317
- const {
318
- entry
319
- } = params;
320
- const queryAllParams = {
321
- entity,
322
- partitionKey: (0, _keys.createPartitionKey)({
323
- id: entry.id,
324
- locale: model.locale,
325
- tenant: model.tenant
326
- }),
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,
327
326
  options: {
328
327
  gte: " "
329
328
  }
330
- };
331
- let records = [];
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
+ });
332
347
 
348
+ /**
349
+ * And finally write it...
350
+ */
333
351
  try {
334
- records = await (0, _query.queryAll)(queryAllParams);
352
+ await entityBatch.execute();
335
353
  } catch (ex) {
336
- throw new _error.default(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
337
- error: ex,
338
- entry
354
+ throw WebinyError.from(ex, {
355
+ message: "Could not move records to a new folder.",
356
+ data: {
357
+ id,
358
+ folderId
359
+ }
339
360
  });
340
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
+ */
341
373
 
342
- const items = records.map(item => {
343
- return entity.deleteBatch({
344
- PK: item.PK,
345
- SK: item.SK
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
346
389
  });
390
+ }
391
+ if (records.length === 0) {
392
+ return;
393
+ }
394
+ const storageEntry = convertToStorageEntry({
395
+ model,
396
+ storageEntry: initialStorageEntry
347
397
  });
348
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
+ */
349
424
  try {
350
- await (0, _batchWrite.batchWriteAll)({
351
- table: entity.table,
352
- items
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
353
431
  });
432
+ }
433
+ };
434
+ const deleteEntry = async (initialModel, params) => {
435
+ const {
436
+ entry
437
+ } = params;
438
+ const id = entry.id || entry.entryId;
439
+ const model = getStorageOperationsModel(initialModel);
440
+ const partitionKey = createPartitionKey({
441
+ id,
442
+ tenant: model.tenant
443
+ });
444
+ let records = [];
445
+ try {
446
+ records = await entity.queryAll({
447
+ partitionKey,
448
+ options: {
449
+ gte: " "
450
+ }
451
+ });
452
+ } catch (ex) {
453
+ throw new WebinyError(ex.message || "Could not load all records.", ex.code || "LOAD_ALL_RECORDS_ERROR", {
454
+ error: ex,
455
+ id
456
+ });
457
+ }
458
+ const entityBatch = entity.createEntityWriter({
459
+ delete: records.map(item => {
460
+ return {
461
+ PK: item.PK,
462
+ SK: item.SK
463
+ };
464
+ })
465
+ });
466
+ try {
467
+ await entityBatch.execute();
354
468
  dataLoaders.clearAll({
355
469
  model
356
470
  });
357
471
  } catch (ex) {
358
- 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", {
359
473
  error: ex,
360
- partitionKey: queryAllParams.partitionKey,
361
- entry
474
+ partitionKey,
475
+ id
362
476
  });
363
477
  }
364
478
  };
479
+ const restoreFromBin = async (initialModel, params) => {
480
+ const {
481
+ entry,
482
+ storageEntry: initialStorageEntry
483
+ } = params;
484
+ const model = getStorageOperationsModel(initialModel);
365
485
 
366
- const deleteRevision = async (model, params) => {
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
+ };
550
+ const deleteRevision = async (initialModel, params) => {
367
551
  const {
368
552
  entry,
369
553
  latestEntry,
370
554
  latestStorageEntry: initialLatestStorageEntry
371
555
  } = params;
372
- const partitionKey = (0, _keys.createPartitionKey)({
556
+ const model = getStorageOperationsModel(initialModel);
557
+ const partitionKey = createPartitionKey({
373
558
  id: entry.id,
374
- locale: model.locale,
375
559
  tenant: model.tenant
376
560
  });
377
- const items = [entity.deleteBatch({
378
- PK: partitionKey,
379
- SK: (0, _keys.createRevisionSortKey)(entry)
380
- })];
561
+ const entityBatch = entity.createEntityWriter({
562
+ delete: [{
563
+ PK: partitionKey,
564
+ SK: createRevisionSortKey(entry)
565
+ }]
566
+ });
381
567
  const publishedStorageEntry = await getPublishedRevisionByEntryId(model, entry);
568
+
382
569
  /**
383
570
  * If revision we are deleting is the published one as well, we need to delete those records as well.
384
571
  */
385
-
386
572
  if (publishedStorageEntry && entry.id === publishedStorageEntry.id) {
387
- items.push(entity.deleteBatch({
573
+ entityBatch.delete({
388
574
  PK: partitionKey,
389
- SK: (0, _keys.createPublishedSortKey)()
390
- }));
575
+ SK: createPublishedSortKey()
576
+ });
391
577
  }
392
-
393
578
  if (initialLatestStorageEntry) {
394
579
  const latestStorageEntry = convertToStorageEntry({
395
580
  storageEntry: initialLatestStorageEntry,
396
581
  model
397
582
  });
398
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, latestStorageEntry), {}, {
399
- PK: partitionKey,
400
- SK: (0, _keys.createLatestSortKey)(),
401
- TYPE: createLatestType(),
402
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
403
- GSI1_SK: (0, _keys.createGSISortKey)(latestStorageEntry)
404
- })));
405
- }
583
+ entityBatch.put({
584
+ ...createEntryLatestKeys(latestStorageEntry),
585
+ data: {
586
+ ...latestStorageEntry
587
+ }
588
+ });
406
589
 
407
- try {
408
- await (0, _batchWrite.batchWriteAll)({
409
- table: entity.table,
410
- items
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
+ }
411
597
  });
598
+ }
599
+ try {
600
+ await entityBatch.execute();
412
601
  dataLoaders.clearAll({
413
602
  model
414
603
  });
415
604
  } catch (ex) {
416
- throw new _error.default(ex.message, ex.code, {
605
+ throw new WebinyError(ex.message, ex.code, {
417
606
  error: ex,
418
607
  entry,
419
608
  latestEntry
420
609
  });
421
610
  }
422
611
  };
612
+ const deleteMultipleEntries = async (initialModel, params) => {
613
+ const {
614
+ entries
615
+ } = params;
616
+ const model = getStorageOperationsModel(initialModel);
617
+ /**
618
+ * First we need all the revisions of the entries we want to delete.
619
+ */
620
+ const revisions = await dataLoaders.getAllEntryRevisions({
621
+ model,
622
+ ids: entries
623
+ });
624
+ /**
625
+ * Then we need to construct the queries for all the revisions and entries.
626
+ */
423
627
 
424
- const getLatestRevisionByEntryId = async (model, params) => {
628
+ const entityBatch = entity.createEntityWriter();
629
+ for (const id of entries) {
630
+ const partitionKey = createPartitionKey({
631
+ id,
632
+ tenant: model.tenant
633
+ });
634
+ entityBatch.delete({
635
+ PK: partitionKey,
636
+ SK: "L"
637
+ });
638
+ entityBatch.delete({
639
+ PK: partitionKey,
640
+ SK: "P"
641
+ });
642
+ }
643
+ /**
644
+ * Exact revisions of all the entries
645
+ */
646
+ for (const revision of revisions) {
647
+ entityBatch.delete({
648
+ PK: createPartitionKey({
649
+ id: revision.id,
650
+ tenant: model.tenant
651
+ }),
652
+ SK: createRevisionSortKey({
653
+ version: revision.version
654
+ })
655
+ });
656
+ }
657
+ await entityBatch.execute();
658
+ };
659
+ const getLatestRevisionByEntryId = async (initialModel, params) => {
660
+ const model = getStorageOperationsModel(initialModel);
425
661
  const items = await dataLoaders.getLatestRevisionByEntryId({
426
662
  model,
427
663
  ids: [params.id]
428
664
  });
429
665
  const item = items.shift() || null;
430
-
431
666
  if (!item) {
432
667
  return null;
433
668
  }
434
-
435
669
  return convertFromStorageEntry({
436
670
  storageEntry: item,
437
671
  model
438
672
  });
439
673
  };
440
-
441
- const getPublishedRevisionByEntryId = async (model, params) => {
674
+ const getPublishedRevisionByEntryId = async (initialModel, params) => {
675
+ const model = getStorageOperationsModel(initialModel);
442
676
  const items = await dataLoaders.getPublishedRevisionByEntryId({
443
677
  model,
444
678
  ids: [params.id]
445
679
  });
446
680
  const item = items.shift() || null;
447
-
448
681
  if (!item) {
449
682
  return null;
450
683
  }
451
-
452
684
  return convertFromStorageEntry({
453
685
  storageEntry: item,
454
686
  model
455
687
  });
456
688
  };
457
-
458
- const getRevisionById = async (model, params) => {
689
+ const getRevisionById = async (initialModel, params) => {
690
+ const model = getStorageOperationsModel(initialModel);
459
691
  const items = await dataLoaders.getRevisionById({
460
692
  model,
461
693
  ids: [params.id]
462
694
  });
463
695
  const item = items.shift() || null;
464
-
465
696
  if (!item) {
466
697
  return null;
467
698
  }
468
-
469
699
  return convertFromStorageEntry({
470
700
  storageEntry: item,
471
701
  model
472
702
  });
473
703
  };
474
-
475
- const getRevisions = async (model, params) => {
704
+ const getRevisions = async (initialModel, params) => {
705
+ const model = getStorageOperationsModel(initialModel);
476
706
  const items = await dataLoaders.getAllEntryRevisions({
477
707
  model,
478
708
  ids: [params.id]
@@ -484,8 +714,8 @@ const createEntriesStorageOperations = params => {
484
714
  });
485
715
  });
486
716
  };
487
-
488
- const getByIds = async (model, params) => {
717
+ const getByIds = async (initialModel, params) => {
718
+ const model = getStorageOperationsModel(initialModel);
489
719
  const items = await dataLoaders.getRevisionById({
490
720
  model,
491
721
  ids: params.ids
@@ -497,8 +727,8 @@ const createEntriesStorageOperations = params => {
497
727
  });
498
728
  });
499
729
  };
500
-
501
- const getLatestByIds = async (model, params) => {
730
+ const getLatestByIds = async (initialModel, params) => {
731
+ const model = getStorageOperationsModel(initialModel);
502
732
  const items = await dataLoaders.getLatestRevisionByEntryId({
503
733
  model,
504
734
  ids: params.ids
@@ -510,8 +740,8 @@ const createEntriesStorageOperations = params => {
510
740
  });
511
741
  });
512
742
  };
513
-
514
- const getPublishedByIds = async (model, params) => {
743
+ const getPublishedByIds = async (initialModel, params) => {
744
+ const model = getStorageOperationsModel(initialModel);
515
745
  const items = await dataLoaders.getPublishedRevisionByEntryId({
516
746
  model,
517
747
  ids: params.ids
@@ -523,59 +753,46 @@ const createEntriesStorageOperations = params => {
523
753
  });
524
754
  });
525
755
  };
526
-
527
- const getPreviousRevision = async (model, params) => {
756
+ const getPreviousRevision = async (initialModel, params) => {
757
+ const model = getStorageOperationsModel(initialModel);
528
758
  const {
529
759
  entryId,
530
760
  version
531
761
  } = params;
532
- const queryParams = {
533
- entity,
534
- partitionKey: (0, _keys.createPartitionKey)({
535
- tenant: model.tenant,
536
- locale: model.locale,
537
- id: entryId
538
- }),
762
+ const partitionKey = createPartitionKey({
763
+ tenant: model.tenant,
764
+ id: entryId
765
+ });
766
+ const unfilteredRevisions = await entity.queryAll({
767
+ partitionKey,
539
768
  options: {
540
- lt: `REV#${(0, _zeroPad.zeroPad)(version)}`,
541
-
542
- /**
543
- * We need to have extra checks because DynamoDB will return published or latest record if there is no REV# record.
544
- */
545
- filters: [{
546
- attr: "TYPE",
547
- eq: createType()
548
- }, {
549
- attr: "version",
550
- lt: version
551
- }],
769
+ beginsWith: `REV#`,
552
770
  reverse: true
553
771
  }
554
- };
555
-
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
+ }
556
780
  try {
557
- const result = await (0, _query.queryOne)(queryParams);
558
- const storageEntry = (0, _cleanup.cleanupItem)(entity, result);
559
-
560
- if (!storageEntry) {
561
- return null;
562
- }
563
-
564
781
  return convertFromStorageEntry({
565
- storageEntry,
782
+ storageEntry: storageEntry.data,
566
783
  model
567
784
  });
568
785
  } catch (ex) {
569
- 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,
570
788
  error: ex,
571
- partitionKey: queryParams.partitionKey,
572
- options: queryParams.options,
789
+ partitionKey,
573
790
  model
574
- }));
791
+ });
575
792
  }
576
793
  };
577
-
578
- const list = async (model, params) => {
794
+ const list = async (initialModel, params) => {
795
+ const model = getStorageOperationsModel(initialModel);
579
796
  const {
580
797
  limit: initialLimit = 10,
581
798
  where: initialWhere,
@@ -584,28 +801,26 @@ const createEntriesStorageOperations = params => {
584
801
  fields,
585
802
  search
586
803
  } = params;
587
- const limit = initialLimit <= 0 || initialLimit >= 10000 ? 10000 : initialLimit;
804
+ const limit = initialLimit <= 0 || initialLimit >= MAX_LIST_LIMIT ? MAX_LIST_LIMIT : initialLimit;
588
805
  const type = initialWhere.published ? "P" : "L";
589
- const queryAllParams = {
590
- entity,
591
- partitionKey: (0, _keys.createGSIPartitionKey)(model, type),
592
- options: {
593
- index: "GSI1",
594
- gte: " "
595
- }
806
+ const partitionKey = createGSIPartitionKey(model, type);
807
+ const options = {
808
+ index: "GSI1",
809
+ gte: " "
596
810
  };
597
811
  let storageEntries = [];
598
-
599
812
  try {
600
- storageEntries = await (0, _query.queryAll)(queryAllParams);
813
+ storageEntries = await entity.queryAll({
814
+ partitionKey,
815
+ options
816
+ });
601
817
  } catch (ex) {
602
- throw new _error.default(ex.message, "QUERY_ENTRIES_ERROR", {
818
+ throw new WebinyError(ex.message, "QUERY_ENTRIES_ERROR", {
603
819
  error: ex,
604
- partitionKey: queryAllParams.partitionKey,
605
- options: queryAllParams.options
820
+ partitionKey,
821
+ options
606
822
  });
607
823
  }
608
-
609
824
  if (storageEntries.length === 0) {
610
825
  return {
611
826
  hasMoreItems: false,
@@ -614,16 +829,16 @@ const createEntriesStorageOperations = params => {
614
829
  items: []
615
830
  };
616
831
  }
617
-
618
- const where = (0, _objectSpread2.default)({}, initialWhere);
832
+ const where = {
833
+ ...initialWhere
834
+ };
619
835
  delete where["published"];
620
836
  delete where["latest"];
621
837
  /**
622
838
  * We need an object containing field, transformers and paths.
623
839
  * Just build it here and pass on into other methods that require it to avoid mapping multiple times.
624
840
  */
625
-
626
- const modelFields = (0, _createFields.createFields)({
841
+ const modelFields = createFields({
627
842
  plugins,
628
843
  fields: model.fields
629
844
  });
@@ -633,25 +848,21 @@ const createEntriesStorageOperations = params => {
633
848
  *
634
849
  * This is always being done, but at least its in parallel.
635
850
  */
636
-
637
851
  const records = await Promise.all(storageEntries.map(async storageEntry => {
638
852
  const entry = convertFromStorageEntry({
639
- storageEntry,
853
+ storageEntry: storageEntry.data,
640
854
  model
641
855
  });
642
-
643
856
  for (const field of model.fields) {
644
857
  entry.values[field.fieldId] = await fromStorage(field, entry.values[field.fieldId]);
645
858
  }
646
-
647
859
  return entry;
648
860
  }));
649
861
  /**
650
862
  * Filter the read items via the code.
651
863
  * It will build the filters out of the where input and transform the values it is using.
652
864
  */
653
-
654
- const filteredItems = (0, _filtering.filter)({
865
+ const filteredItems = filter({
655
866
  items: records,
656
867
  where,
657
868
  plugins,
@@ -662,19 +873,19 @@ const createEntriesStorageOperations = params => {
662
873
  }
663
874
  });
664
875
  const totalCount = filteredItems.length;
876
+
665
877
  /**
666
878
  * Sorting is also done via the code.
667
879
  * It takes the sort input and sorts by it via the lodash sortBy method.
668
880
  */
669
-
670
- const sortedItems = (0, _filtering.sort)({
881
+ const sortedItems = sort({
671
882
  model,
672
883
  plugins,
673
884
  items: filteredItems,
674
885
  sort: sortBy,
675
886
  fields: modelFields
676
887
  });
677
- const start = parseInt((0, _cursor.decodeCursor)(after) || "0") || 0;
888
+ const start = parseInt(decodeCursor(after) || "0") || 0;
678
889
  const hasMoreItems = totalCount > start + limit;
679
890
  const end = limit > totalCount + start + limit ? undefined : start + limit;
680
891
  const slicedItems = sortedItems.slice(start, end);
@@ -682,118 +893,163 @@ const createEntriesStorageOperations = params => {
682
893
  * Although we do not need a cursor here, we will use it as such to keep it standardized.
683
894
  * Number is simply encoded.
684
895
  */
685
-
686
- const cursor = totalCount > start + limit ? (0, _cursor.encodeCursor)(`${start + limit}`) : null;
896
+ const cursor = encodeCursor(`${start + limit}`);
687
897
  return {
688
898
  hasMoreItems,
689
899
  totalCount,
690
900
  cursor,
691
- items: (0, _cleanup.cleanupItems)(entity, slicedItems)
901
+ items: slicedItems
692
902
  };
693
903
  };
694
-
695
- const get = async (model, params) => {
904
+ const get = async (initialModel, params) => {
905
+ const model = getStorageOperationsModel(initialModel);
696
906
  const {
697
907
  items
698
- } = await list(model, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, params), {}, {
908
+ } = await list(model, {
909
+ ...params,
699
910
  limit: 1
700
- }));
911
+ });
701
912
  return items.shift() || null;
702
913
  };
703
-
704
- const publish = async (model, params) => {
914
+ const publish = async (initialModel, params) => {
705
915
  const {
706
916
  entry,
707
917
  storageEntry: initialStorageEntry
708
918
  } = params;
709
- const partitionKey = (0, _keys.createPartitionKey)({
710
- id: entry.id,
711
- locale: model.locale,
712
- tenant: model.tenant
713
- });
919
+ const model = getStorageOperationsModel(initialModel);
920
+
714
921
  /**
715
- * We need the latest and published entries to see if something needs to be updated along side the publishing one.
922
+ * We need the latest and published entries to see if something needs to be updated alongside the publishing one.
716
923
  */
717
-
718
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
+ }
719
930
  const initialPublishedStorageEntry = await getPublishedRevisionByEntryId(model, entry);
720
931
  const storageEntry = convertToStorageEntry({
721
932
  model,
722
933
  storageEntry: initialStorageEntry
723
934
  });
724
- /**
725
- * We need to update:
726
- * - current entry revision sort key
727
- * - published sort key
728
- * - latest sort key - if entry updated is actually latest
729
- * - previous published entry to unpublished status - if any previously published entry
730
- */
731
935
 
732
- const items = [entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
733
- PK: partitionKey,
734
- SK: (0, _keys.createRevisionSortKey)(entry),
735
- TYPE: createType(),
736
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
737
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
738
- })), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
739
- PK: partitionKey,
740
- SK: (0, _keys.createPublishedSortKey)(),
741
- TYPE: createPublishedType(),
742
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "P"),
743
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
744
- }))];
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
+ });
745
950
 
746
- if (initialLatestStorageEntry && entry.id === initialLatestStorageEntry.id) {
747
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
748
- PK: partitionKey,
749
- SK: (0, _keys.createLatestSortKey)(),
750
- TYPE: createLatestType(),
751
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
752
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
753
- })));
754
- }
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
+ });
755
963
 
756
- if (initialPublishedStorageEntry && initialPublishedStorageEntry.id !== entry.id) {
757
- const publishedStorageEntry = convertToStorageEntry({
758
- storageEntry: initialPublishedStorageEntry,
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,
759
985
  model
760
986
  });
761
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, publishedStorageEntry), {}, {
762
- PK: partitionKey,
763
- SK: (0, _keys.createRevisionSortKey)(publishedStorageEntry),
764
- TYPE: createType(),
765
- status: _types.CONTENT_ENTRY_STATUS.UNPUBLISHED,
766
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
767
- GSI1_SK: (0, _keys.createGSISortKey)(publishedStorageEntry)
768
- })));
769
- }
770
987
 
771
- try {
772
- await (0, _batchWrite.batchWriteAll)({
773
- table: entity.table,
774
- items
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
+ }
775
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
+ }
1030
+ }
1031
+ try {
1032
+ await entityBatch.execute();
776
1033
  dataLoaders.clearAll({
777
1034
  model
778
1035
  });
779
1036
  return initialStorageEntry;
780
1037
  } catch (ex) {
781
- 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", {
782
1039
  entry,
783
1040
  latestStorageEntry: initialLatestStorageEntry,
784
1041
  publishedStorageEntry: initialPublishedStorageEntry
785
1042
  });
786
1043
  }
787
1044
  };
788
-
789
- const unpublish = async (model, params) => {
1045
+ const unpublish = async (initialModel, params) => {
790
1046
  const {
791
1047
  entry,
792
1048
  storageEntry: initialStorageEntry
793
1049
  } = params;
794
- const partitionKey = (0, _keys.createPartitionKey)({
1050
+ const model = getStorageOperationsModel(initialModel);
1051
+ const partitionKey = createPartitionKey({
795
1052
  id: entry.id,
796
- locale: model.locale,
797
1053
  tenant: model.tenant
798
1054
  });
799
1055
  const storageEntry = convertToStorageEntry({
@@ -804,58 +1060,122 @@ const createEntriesStorageOperations = params => {
804
1060
  * We need to:
805
1061
  * - delete currently published entry
806
1062
  * - update current entry revision with new data
807
- * - update latest entry status - if entry being unpublished is latest
1063
+ * - update the latest entry status - if entry being unpublished is latest
808
1064
  */
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
+ });
809
1077
 
810
- const items = [entity.deleteBatch({
811
- PK: partitionKey,
812
- SK: (0, _keys.createPublishedSortKey)()
813
- }), entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
814
- PK: partitionKey,
815
- SK: (0, _keys.createRevisionSortKey)(entry),
816
- TYPE: createType(),
817
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "A"),
818
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
819
- }))];
820
1078
  /**
821
- * We need the latest entry to see if something needs to be updated along side the unpublishing one.
1079
+ * We need the latest entry to see if something needs to be updated alongside the unpublishing one.
822
1080
  */
823
-
824
- const latestStorageEntry = await getLatestRevisionByEntryId(model, entry);
825
-
826
- if (latestStorageEntry && entry.id === latestStorageEntry.id) {
827
- items.push(entity.putBatch((0, _objectSpread2.default)((0, _objectSpread2.default)({}, storageEntry), {}, {
828
- PK: partitionKey,
829
- SK: (0, _keys.createLatestSortKey)(),
830
- TYPE: createLatestType(),
831
- GSI1_PK: (0, _keys.createGSIPartitionKey)(model, "L"),
832
- GSI1_SK: (0, _keys.createGSISortKey)(entry)
833
- })));
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
+ }
834
1119
  }
835
-
836
1120
  try {
837
- await (0, _batchWrite.batchWriteAll)({
838
- table: entity.table,
839
- items
840
- });
1121
+ await entityBatch.execute();
841
1122
  dataLoaders.clearAll({
842
1123
  model
843
1124
  });
844
1125
  return initialStorageEntry;
845
1126
  } catch (ex) {
846
- 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", {
847
1128
  entry,
848
1129
  storageEntry
849
1130
  });
850
1131
  }
851
1132
  };
852
-
1133
+ const getUniqueFieldValues = async (model, params) => {
1134
+ const {
1135
+ where,
1136
+ fieldId
1137
+ } = params;
1138
+ const field = model.fields.find(f => f.fieldId === fieldId);
1139
+ if (!field) {
1140
+ throw new WebinyError(`Could not find field with given "fieldId" value.`, "FIELD_NOT_FOUND", {
1141
+ fieldId
1142
+ });
1143
+ }
1144
+ const {
1145
+ items
1146
+ } = await list(model, {
1147
+ where,
1148
+ limit: MAX_LIST_LIMIT
1149
+ });
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);
1168
+ };
853
1169
  return {
854
1170
  create,
855
1171
  createRevisionFrom,
856
1172
  update,
1173
+ move,
857
1174
  delete: deleteEntry,
1175
+ moveToBin,
1176
+ restoreFromBin,
858
1177
  deleteRevision,
1178
+ deleteMultipleEntries,
859
1179
  getPreviousRevision,
860
1180
  getPublishedByIds,
861
1181
  getLatestByIds,
@@ -867,8 +1187,10 @@ const createEntriesStorageOperations = params => {
867
1187
  getRevisions,
868
1188
  publish,
869
1189
  list,
870
- unpublish
1190
+ unpublish,
1191
+ dataLoaders,
1192
+ getUniqueFieldValues
871
1193
  };
872
1194
  };
873
1195
 
874
- exports.createEntriesStorageOperations = createEntriesStorageOperations;
1196
+ //# sourceMappingURL=index.js.map