@webiny/api-headless-cms-ddb 0.0.0-mt-2 → 0.0.0-unstable.06b2ede40f

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