electrodb 2.9.3 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/update.js CHANGED
@@ -1,107 +1,112 @@
1
- const {AttributeOperationProxy, ExpressionState} = require("./operations");
2
- const {ItemOperations, BuilderTypes} = require("./types");
1
+ const { UpdateOperations } = require("./updateOperations");
2
+ const { AttributeOperationProxy, ExpressionState } = require("./operations");
3
+ const { ItemOperations, BuilderTypes } = require("./types");
3
4
 
4
5
  class UpdateExpression extends ExpressionState {
5
- constructor(props = {}) {
6
- super({...props});
7
- this.operations = {
8
- set: new Set(),
9
- remove: new Set(),
10
- add: new Set(),
11
- subtract: new Set(),
12
- delete: new Set(),
13
- };
14
- this.composites = {};
15
- this.seen = new Map();
16
- this.type = BuilderTypes.update;
17
- }
18
- addComposite(attrName, value) {
19
- if (value !== undefined) {
20
- if (this.composites[attrName] === undefined || this.composites[attrName] === value) {
21
- this.composites[attrName] = value;
22
- return true;
23
- }
24
- }
25
- return false;
6
+ constructor(props = {}) {
7
+ super({ ...props });
8
+ this.operations = {
9
+ set: new Set(),
10
+ remove: new Set(),
11
+ add: new Set(),
12
+ subtract: new Set(),
13
+ delete: new Set(),
14
+ };
15
+ this.composites = {};
16
+ this.seen = new Map();
17
+ this.type = BuilderTypes.update;
18
+ }
19
+ addComposite(attrName, value) {
20
+ if (value !== undefined) {
21
+ if (
22
+ this.composites[attrName] === undefined ||
23
+ this.composites[attrName] === value
24
+ ) {
25
+ this.composites[attrName] = value;
26
+ return true;
27
+ }
26
28
  }
29
+ return false;
30
+ }
27
31
 
28
- add(type, expression) {
29
- this.operations[type].add(expression);
30
- }
32
+ add(type, expression) {
33
+ this.operations[type].add(expression);
34
+ }
31
35
 
32
- unadd(type, expression) {
33
- this.operations[type].delete(expression);
34
- }
36
+ unadd(type, expression) {
37
+ this.operations[type].delete(expression);
38
+ }
35
39
 
36
- set(name, value, operation = ItemOperations.set) {
37
- let operationToApply = operation;
38
- if (operation === ItemOperations.ifNotExists) {
39
- operationToApply = ItemOperations.set;
40
- }
41
- const seen = this.seen.get(name);
42
- let n;
43
- let v;
44
- if (seen) {
45
- n = seen.name;
46
- v = seen.value;
47
- this.unadd(operationToApply, seen.expression);
48
-
49
- } else {
50
- n = this.setName({}, name, name);
51
- v = this.setValue(name, value);
52
- }
53
- let expression = `${n.prop} = ${v}`;
54
- if (operation === ItemOperations.ifNotExists) {
55
- expression = `${n.prop} = if_not_exists(${n.prop}, ${v})`;
56
- }
57
- this.seen.set(name, {
58
- name: n,
59
- value: v,
60
- expression,
61
- });
62
- this.add(operationToApply, expression);
40
+ set(name, value, operation = ItemOperations.set, attribute) {
41
+ let operationToApply = operation;
42
+ if (operation === ItemOperations.ifNotExists) {
43
+ operationToApply = ItemOperations.set;
63
44
  }
64
-
65
- remove(name) {
66
- const n = this.setName({}, name, name);
67
- this.add(ItemOperations.remove, `${n.prop}`);
45
+ const seen = this.seen.get(name);
46
+ let n;
47
+ let v;
48
+ if (seen) {
49
+ n = seen.name;
50
+ v = seen.value;
51
+ this.unadd(operationToApply, seen.expression);
52
+ } else {
53
+ n = this.setName({}, name, name);
54
+ v = this.setValue(name, value);
68
55
  }
56
+ let expression = `${n.prop} = ${v}`;
57
+ if (operation === ItemOperations.ifNotExists) {
58
+ expression = `${n.prop} = if_not_exists(${n.prop}, ${v})`;
59
+ }
60
+ this.seen.set(name, {
61
+ name: n,
62
+ value: v,
63
+ expression,
64
+ });
65
+ this.add(operationToApply, expression);
66
+ }
67
+
68
+ remove(name) {
69
+ const n = this.setName({}, name, name);
70
+ this.add(ItemOperations.remove, `${n.prop}`);
71
+ }
69
72
 
70
- build() {
71
- let expressions = [];
72
- for (const type of Object.keys(this.operations)) {
73
- const operations = this.operations[type];
74
- if (operations.size > 0) {
75
- expressions.push(`${type.toUpperCase()} ${Array.from(operations).join(", ")}`);
76
- }
77
- }
78
- return expressions.join(" ");
73
+ build() {
74
+ let expressions = [];
75
+ for (const type of Object.keys(this.operations)) {
76
+ const operations = this.operations[type];
77
+ if (operations.size > 0) {
78
+ expressions.push(
79
+ `${type.toUpperCase()} ${Array.from(operations).join(", ")}`,
80
+ );
81
+ }
79
82
  }
83
+ return expressions.join(" ");
84
+ }
80
85
  }
81
86
 
82
87
  class UpdateEntity {
83
- constructor(attributes = {}, operations = {}) {
84
- this.attributes = {...attributes};
85
- this.operations = {...operations};
86
- }
88
+ constructor(attributes = {}, operations = {}) {
89
+ this.attributes = { ...attributes };
90
+ this.operations = { ...operations };
91
+ }
87
92
 
88
- buildCallbackHandler(entity, state) {
89
- const proxy = new AttributeOperationProxy({
90
- builder: state.query.updates,
91
- attributes: this.attributes,
92
- operations: this.operations,
93
- });
93
+ buildCallbackHandler(entity, state) {
94
+ const proxy = new AttributeOperationProxy({
95
+ builder: state.query.update,
96
+ attributes: this.attributes,
97
+ operations: this.operations,
98
+ });
94
99
 
95
- return (cb, ...params) => {
96
- if (typeof cb !== "function") {
97
- throw new Error('Update Callback must be of type "function"');
98
- }
99
- proxy.invokeCallback(cb, ...params);
100
- }
101
- }
100
+ return (cb, ...params) => {
101
+ if (typeof cb !== "function") {
102
+ throw new Error('Update Callback must be of type "function"');
103
+ }
104
+ proxy.invokeCallback(cb, ...params);
105
+ };
106
+ }
102
107
  }
103
108
 
104
109
  module.exports = {
105
- UpdateEntity,
106
- UpdateExpression
107
- }
110
+ UpdateEntity,
111
+ UpdateExpression,
112
+ };
@@ -0,0 +1,197 @@
1
+ const { AttributeTypes, ItemOperations } = require("./types");
2
+
3
+ const deleteOperations = {
4
+ canNest: false,
5
+ template: function del(options, attr, path, value) {
6
+ let operation = "";
7
+ let expression = "";
8
+ switch (attr.type) {
9
+ case AttributeTypes.any:
10
+ case AttributeTypes.set:
11
+ operation = ItemOperations.delete;
12
+ expression = `${path} ${value}`;
13
+ break;
14
+ default:
15
+ throw new Error(
16
+ `Invalid Update Attribute Operation: "DELETE" Operation can only be performed on attributes with type "set" or "any".`,
17
+ );
18
+ }
19
+ return { operation, expression };
20
+ },
21
+ };
22
+
23
+ const UpdateOperations = {
24
+ ifNotExists: {
25
+ template: function if_not_exists(options, attr, path, value) {
26
+ const operation = ItemOperations.set;
27
+ const expression = `${path} = if_not_exists(${path}, ${value})`;
28
+ return { operation, expression };
29
+ },
30
+ },
31
+ name: {
32
+ canNest: true,
33
+ template: function name(options, attr, path) {
34
+ return path;
35
+ },
36
+ },
37
+ value: {
38
+ canNest: true,
39
+ template: function value(options, attr, path, value) {
40
+ return value;
41
+ },
42
+ },
43
+ append: {
44
+ canNest: false,
45
+ template: function append(options, attr, path, value) {
46
+ let operation = "";
47
+ let expression = "";
48
+ switch (attr.type) {
49
+ case AttributeTypes.any:
50
+ case AttributeTypes.list:
51
+ const defaultValue = options.createValue("default_value", []);
52
+ expression = `${path} = list_append(if_not_exists(${path}, ${defaultValue}), ${value})`;
53
+ operation = ItemOperations.set;
54
+ break;
55
+ default:
56
+ throw new Error(
57
+ `Invalid Update Attribute Operation: "APPEND" Operation can only be performed on attributes with type "list" or "any".`,
58
+ );
59
+ }
60
+ return { operation, expression };
61
+ },
62
+ },
63
+ add: {
64
+ canNest: false,
65
+ template: function add(options, attr, path, value, defaultValue) {
66
+ let operation = "";
67
+ let expression = "";
68
+ let type = attr.type;
69
+ if (type === AttributeTypes.any) {
70
+ type =
71
+ typeof value === "number"
72
+ ? AttributeTypes.number
73
+ : AttributeTypes.any;
74
+ }
75
+ switch (type) {
76
+ case AttributeTypes.any:
77
+ case AttributeTypes.set: {
78
+ operation = ItemOperations.add;
79
+ expression = `${path} ${value}`;
80
+ break;
81
+ }
82
+ case AttributeTypes.number: {
83
+ if (options.nestedValue) {
84
+ operation = ItemOperations.set;
85
+ expression = `${path} = ${path} + ${value}`;
86
+ } else if (defaultValue !== undefined) {
87
+ // const defaultValueName = options.createValue(`default_value`, defaultValue)
88
+ operation = ItemOperations.set;
89
+ expression = `${path} = (if_not_exists(${path}, ${defaultValue}) + ${value})`;
90
+ } else {
91
+ operation = ItemOperations.add;
92
+ expression = `${path} ${value}`;
93
+ }
94
+ break;
95
+ }
96
+ default:
97
+ throw new Error(
98
+ `Invalid Update Attribute Operation: "ADD" Operation can only be performed on attributes with type "number", "set", or "any".`,
99
+ );
100
+ }
101
+ return { operation, expression };
102
+ },
103
+ },
104
+ subtract: {
105
+ canNest: false,
106
+ template: function subtract(options, attr, path, value, defaultValue = 0) {
107
+ let operation = "";
108
+ let expression = "";
109
+ switch (attr.type) {
110
+ case AttributeTypes.any:
111
+ case AttributeTypes.number: {
112
+ let resolvedDefaultValue;
113
+ if (
114
+ typeof defaultValue === "string" &&
115
+ defaultValue.startsWith(":")
116
+ ) {
117
+ resolvedDefaultValue = defaultValue;
118
+ } else if (defaultValue !== undefined) {
119
+ resolvedDefaultValue = options.createValue(
120
+ "default_value",
121
+ defaultValue,
122
+ );
123
+ } else {
124
+ resolvedDefaultValue = options.createValue("default_value", 0);
125
+ }
126
+ // const defaultValuePath = options.createValue('default_value', resolvedDefaultValue);
127
+ operation = ItemOperations.set;
128
+ expression = `${path} = (if_not_exists(${path}, ${resolvedDefaultValue}) - ${value})`;
129
+ break;
130
+ }
131
+ default:
132
+ throw new Error(
133
+ `Invalid Update Attribute Operation: "SUBTRACT" Operation can only be performed on attributes with type "number" or "any".`,
134
+ );
135
+ }
136
+
137
+ return { operation, expression };
138
+ },
139
+ },
140
+ set: {
141
+ canNest: false,
142
+ template: function set(options, attr, path, value) {
143
+ let operation = "";
144
+ let expression = "";
145
+ switch (attr.type) {
146
+ case AttributeTypes.set:
147
+ case AttributeTypes.list:
148
+ case AttributeTypes.map:
149
+ case AttributeTypes.enum:
150
+ case AttributeTypes.string:
151
+ case AttributeTypes.number:
152
+ case AttributeTypes.boolean:
153
+ case AttributeTypes.any:
154
+ operation = ItemOperations.set;
155
+ expression = `${path} = ${value}`;
156
+ break;
157
+ default:
158
+ throw new Error(
159
+ `Invalid Update Attribute Operation: "SET" Operation can only be performed on attributes with type "list", "map", "string", "number", "boolean", or "any".`,
160
+ );
161
+ }
162
+ return { operation, expression };
163
+ },
164
+ },
165
+ remove: {
166
+ canNest: false,
167
+ template: function remove(options, attr, ...paths) {
168
+ let operation = "";
169
+ let expression = "";
170
+ switch (attr.type) {
171
+ case AttributeTypes.set:
172
+ case AttributeTypes.any:
173
+ case AttributeTypes.list:
174
+ case AttributeTypes.map:
175
+ case AttributeTypes.string:
176
+ case AttributeTypes.number:
177
+ case AttributeTypes.boolean:
178
+ case AttributeTypes.enum:
179
+ operation = ItemOperations.remove;
180
+ expression = paths.join(", ");
181
+ break;
182
+ default: {
183
+ throw new Error(
184
+ `Invalid Update Attribute Operation: "REMOVE" Operation can only be performed on attributes with type "map", "list", "string", "number", "boolean", or "any".`,
185
+ );
186
+ }
187
+ }
188
+ return { operation, expression };
189
+ },
190
+ },
191
+ del: deleteOperations,
192
+ delete: deleteOperations,
193
+ };
194
+
195
+ module.exports = {
196
+ UpdateOperations,
197
+ };
package/src/util.js CHANGED
@@ -8,7 +8,7 @@ function parseJSONPath(path = "") {
8
8
  }
9
9
  path = path.replace(/\[/g, ".");
10
10
  path = path.replace(/\]/g, "");
11
- return path.split(".");
11
+ return path.split(".").filter((part) => part !== "");
12
12
  }
13
13
 
14
14
  function genericizeJSONPath(path = "") {
@@ -34,9 +34,10 @@ function getInstanceType(instance = {}) {
34
34
 
35
35
  function getModelVersion(model = {}) {
36
36
  let nameOnRoot = model && v.isStringHasLength(model.entity);
37
- let nameInModelNamespace = model && model.model && v.isStringHasLength(model.model.entity);
37
+ let nameInModelNamespace =
38
+ model && model.model && v.isStringHasLength(model.model.entity);
38
39
  if (nameInModelNamespace) {
39
- return t.ModelVersions.v1
40
+ return t.ModelVersions.v1;
40
41
  } else if (nameOnRoot) {
41
42
  return t.ModelVersions.beta;
42
43
  } else {
@@ -44,7 +45,10 @@ function getModelVersion(model = {}) {
44
45
  }
45
46
  }
46
47
 
47
- function applyBetaModelOverrides(model = {}, {service = "", version = "", table = ""} = {}) {
48
+ function applyBetaModelOverrides(
49
+ model = {},
50
+ { service = "", version = "", table = "" } = {},
51
+ ) {
48
52
  let type = getModelVersion(model);
49
53
  if (type !== t.ModelVersions.beta) {
50
54
  throw new Error("Invalid model");
@@ -76,7 +80,7 @@ function batchItems(arr = [], size) {
76
80
  }
77
81
 
78
82
  function commaSeparatedString(array = [], prefix = '"', postfix = '"') {
79
- return array.map(value => `${prefix}${value}${postfix}`).join(", ");
83
+ return array.map((value) => `${prefix}${value}${postfix}`).join(", ");
80
84
  }
81
85
 
82
86
  function formatStringCasing(str, casing, defaultCase) {
@@ -85,9 +89,8 @@ function formatStringCasing(str, casing, defaultCase) {
85
89
  }
86
90
  let strCase = defaultCase;
87
91
  if (v.isStringHasLength(casing) && typeof t.KeyCasing[casing] === "string") {
88
- strCase = t.KeyCasing.default === casing
89
- ? defaultCase
90
- : t.KeyCasing[casing];
92
+ strCase =
93
+ t.KeyCasing.default === casing ? defaultCase : t.KeyCasing[casing];
91
94
  }
92
95
  switch (strCase) {
93
96
  case t.KeyCasing.upper:
@@ -144,7 +147,12 @@ class BatchGetOrderMaintainer {
144
147
  if (this.enabled) {
145
148
  for (let i = 0; i < parameters.length; i++) {
146
149
  const batchParams = parameters[i];
147
- const recordKeys = (batchParams && batchParams.RequestItems && batchParams.RequestItems[this.table] && batchParams.RequestItems[this.table].Keys) || [];
150
+ const recordKeys =
151
+ (batchParams &&
152
+ batchParams.RequestItems &&
153
+ batchParams.RequestItems[this.table] &&
154
+ batchParams.RequestItems[this.table].Keys) ||
155
+ [];
148
156
  for (const recordKey of recordKeys) {
149
157
  const indexMapKey = this.keyFormatter(recordKey);
150
158
  this.batchIndexMap.set(indexMapKey, this.currentSlot++);
@@ -155,40 +163,45 @@ class BatchGetOrderMaintainer {
155
163
  }
156
164
 
157
165
  function getUnique(arr1, arr2) {
158
- return Array.from(new Set([
159
- ...arr1,
160
- ...arr2
161
- ]));
166
+ return Array.from(new Set([...arr1, ...arr2]));
162
167
  }
163
168
 
164
169
  const cursorFormatter = {
165
170
  serialize: (key) => {
166
171
  if (!key) {
167
172
  return null;
168
- } else if (typeof val !== 'string') {
173
+ } else if (typeof val !== "string") {
169
174
  key = JSON.stringify(key);
170
175
  }
171
- return Buffer.from(key).toString('base64url');
176
+ return Buffer.from(key).toString("base64url");
172
177
  },
173
178
  deserialize: (cursor) => {
174
179
  if (!cursor) {
175
180
  return undefined;
176
- } else if (typeof cursor !== 'string') {
177
- throw new Error(`Invalid cursor provided, expected type 'string' recieved: ${JSON.stringify(cursor)}`);
181
+ } else if (typeof cursor !== "string") {
182
+ throw new Error(
183
+ `Invalid cursor provided, expected type 'string' recieved: ${JSON.stringify(
184
+ cursor,
185
+ )}`,
186
+ );
178
187
  }
179
188
  try {
180
- return JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
181
- } catch(err) {
182
- throw new Error('Unable to parse cursor');
189
+ return JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
190
+ } catch (err) {
191
+ throw new Error("Unable to parse cursor");
183
192
  }
184
- }
185
- }
193
+ },
194
+ };
186
195
 
187
- function removeFixings({prefix = '', postfix = '', value = ''} = {}) {
188
- const start = value.toLowerCase().startsWith(prefix.toLowerCase()) ? prefix.length : 0;
189
- const end = value.length - (value.toLowerCase().endsWith(postfix.toLowerCase()) ? postfix.length : 0);
196
+ function removeFixings({ prefix = "", postfix = "", value = "" } = {}) {
197
+ const start = value.toLowerCase().startsWith(prefix.toLowerCase())
198
+ ? prefix.length
199
+ : 0;
200
+ const end =
201
+ value.length -
202
+ (value.toLowerCase().endsWith(postfix.toLowerCase()) ? postfix.length : 0);
190
203
 
191
- let formatted = '';
204
+ let formatted = "";
192
205
  for (let i = start; i < end; i++) {
193
206
  formatted += value[i];
194
207
  }
@@ -196,16 +209,16 @@ function removeFixings({prefix = '', postfix = '', value = ''} = {}) {
196
209
  return formatted;
197
210
  }
198
211
 
199
- function addPadding({padding = {}, value = ''} = {}) {
212
+ function addPadding({ padding = {}, value = "" } = {}) {
200
213
  return value.padStart(padding.length, padding.char);
201
214
  }
202
215
 
203
- function removePadding({padding = {}, value = ''} = {}) {
216
+ function removePadding({ padding = {}, value = "" } = {}) {
204
217
  if (!padding.length || value.length >= padding.length) {
205
218
  return value;
206
219
  }
207
220
 
208
- let formatted = '';
221
+ let formatted = "";
209
222
  let useRemaining = false;
210
223
  for (let i = 0; i < value.length; i++) {
211
224
  const char = value[i];
@@ -220,8 +233,8 @@ function removePadding({padding = {}, value = ''} = {}) {
220
233
  return formatted;
221
234
  }
222
235
 
223
- function shiftSortOrder(str = '', codePoint) {
224
- let newString = '';
236
+ function shiftSortOrder(str = "", codePoint) {
237
+ let newString = "";
225
238
  for (let i = 0; i < str.length; i++) {
226
239
  const isLast = i === str.length - 1;
227
240
  let char = str[i];
@@ -234,7 +247,7 @@ function shiftSortOrder(str = '', codePoint) {
234
247
  }
235
248
 
236
249
  function getFirstDefined(...params) {
237
- return params.find(val => val !== undefined);
250
+ return params.find((val) => val !== undefined);
238
251
  }
239
252
 
240
253
  function regexpEscape(str) {