electrodb 2.3.4 → 2.4.0
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/index.d.ts +28 -1
- package/index.js +2 -1
- package/package.json +1 -1
- package/src/entity.js +1 -1
- package/src/errors.js +1 -1
- package/src/operations.js +91 -27
- package/src/schema.js +22 -3
- package/src/service.js +1 -1
- package/src/types.js +18 -0
package/index.d.ts
CHANGED
|
@@ -116,6 +116,18 @@ export type IsolatedCollectionAttributes<E extends {[name: string]: Entity<any,
|
|
|
116
116
|
}[keyof E]
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
type DynamoDBAttributeType =
|
|
120
|
+
| 'S'
|
|
121
|
+
| 'SS'
|
|
122
|
+
| 'N'
|
|
123
|
+
| 'NS'
|
|
124
|
+
| 'B'
|
|
125
|
+
| 'BS'
|
|
126
|
+
| 'BOOL'
|
|
127
|
+
| 'NULL'
|
|
128
|
+
| 'L'
|
|
129
|
+
| 'M'
|
|
130
|
+
|
|
119
131
|
export interface CollectionWhereOperations {
|
|
120
132
|
eq: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: T) => string;
|
|
121
133
|
ne: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: T) => string;
|
|
@@ -131,6 +143,12 @@ export interface CollectionWhereOperations {
|
|
|
131
143
|
notContains: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: T) => string;
|
|
132
144
|
value: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: T) => string;
|
|
133
145
|
name: <T, A extends WhereAttributeSymbol<T>>(attr: A) => string;
|
|
146
|
+
size: <T, A extends WhereAttributeSymbol<T>>(attr: A) => string;
|
|
147
|
+
type: <T, A extends WhereAttributeSymbol<T>>(attr: A, type: DynamoDBAttributeType) => string;
|
|
148
|
+
escape: <T extends string | number | boolean>(value: T) => T extends string ? string
|
|
149
|
+
: T extends number ? number
|
|
150
|
+
: T extends boolean ? boolean
|
|
151
|
+
: never;
|
|
134
152
|
}
|
|
135
153
|
|
|
136
154
|
export type CollectionWhereCallback<E extends {[name: string]: Entity<any, any, any, any>}, I extends Partial<AllEntityAttributes<E>>> =
|
|
@@ -2199,6 +2217,13 @@ export interface WhereOperations<A extends string, F extends string, C extends s
|
|
|
2199
2217
|
notContains: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: T) => string;
|
|
2200
2218
|
value: <T, A extends WhereAttributeSymbol<T>>(attr: A, value: A extends WhereAttributeSymbol<infer V> ? V : never) => A extends WhereAttributeSymbol<infer V> ? V : never;
|
|
2201
2219
|
name: <A extends WhereAttributeSymbol<any>>(attr: A) => string;
|
|
2220
|
+
size: <T, A extends WhereAttributeSymbol<T>>(attr: A) => number;
|
|
2221
|
+
type: <T, A extends WhereAttributeSymbol<T>>(attr: A, type: DynamoDBAttributeType) => string;
|
|
2222
|
+
escape: <T extends string | number | boolean>(value: T) =>
|
|
2223
|
+
T extends string ? string
|
|
2224
|
+
: T extends number ? number
|
|
2225
|
+
: T extends boolean ? boolean
|
|
2226
|
+
: never;
|
|
2202
2227
|
}
|
|
2203
2228
|
|
|
2204
2229
|
export interface DataUpdateOperations<A extends string, F extends string, C extends string, S extends Schema<A,F,C>, I extends UpdateData<A,F,C,S>> {
|
|
@@ -2347,4 +2372,6 @@ declare function CustomAttributeType<T>(
|
|
|
2347
2372
|
: 'any'
|
|
2348
2373
|
): T extends string | number | boolean
|
|
2349
2374
|
? OpaquePrimitiveTypeName<T>
|
|
2350
|
-
: CustomAttributeTypeName<T>;
|
|
2375
|
+
: CustomAttributeTypeName<T>;
|
|
2376
|
+
|
|
2377
|
+
declare function createSchema<A extends string, F extends string, C extends string, S extends Schema<A,F,C>>(schema: S): S
|
package/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const { Entity } = require("./src/entity");
|
|
2
2
|
const { Service } = require("./src/service");
|
|
3
|
-
const { createCustomAttribute, CustomAttributeType } = require('./src/schema');
|
|
3
|
+
const { createCustomAttribute, CustomAttributeType, createSchema } = require('./src/schema');
|
|
4
4
|
const { ElectroError, ElectroValidationError, ElectroUserValidationError, ElectroAttributeValidationError } = require('./src/errors');
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
Entity,
|
|
8
8
|
Service,
|
|
9
9
|
ElectroError,
|
|
10
|
+
createSchema,
|
|
10
11
|
CustomAttributeType,
|
|
11
12
|
createCustomAttribute,
|
|
12
13
|
ElectroValidationError,
|
package/package.json
CHANGED
package/src/entity.js
CHANGED
|
@@ -307,7 +307,7 @@ class Entity {
|
|
|
307
307
|
return Promise.reject(err);
|
|
308
308
|
} else {
|
|
309
309
|
if (err.__isAWSError) {
|
|
310
|
-
stackTrace.message = new e.ElectroError(e.ErrorCodes.AWSError, err.message).message;
|
|
310
|
+
stackTrace.message = new e.ElectroError(e.ErrorCodes.AWSError, `Error thrown by DynamoDB client: "${err.message}"`).message;
|
|
311
311
|
return Promise.reject(stackTrace);
|
|
312
312
|
} else if (err.isElectroError) {
|
|
313
313
|
return Promise.reject(err);
|
package/src/errors.js
CHANGED
package/src/operations.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {AttributeTypes, ItemOperations, AttributeProxySymbol, BuilderTypes} = require("./types");
|
|
1
|
+
const {AttributeTypes, ItemOperations, AttributeProxySymbol, BuilderTypes, DynamoDBAttributeTypes} = require("./types");
|
|
2
2
|
const e = require("./errors");
|
|
3
3
|
const u = require("./util");
|
|
4
4
|
|
|
@@ -159,8 +159,26 @@ const UpdateOperations = {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
const FilterOperations = {
|
|
162
|
+
escape: {
|
|
163
|
+
template: function escape(options, attr) {
|
|
164
|
+
return `${attr}`;
|
|
165
|
+
},
|
|
166
|
+
noAttribute: true,
|
|
167
|
+
},
|
|
168
|
+
size: {
|
|
169
|
+
template: function size(options, attr, name) {
|
|
170
|
+
return `size(${name})`
|
|
171
|
+
},
|
|
172
|
+
strict: false,
|
|
173
|
+
},
|
|
174
|
+
type: {
|
|
175
|
+
template: function attributeType(options, attr, name, value) {
|
|
176
|
+
return `attribute_type(${name}, ${value})`;
|
|
177
|
+
},
|
|
178
|
+
strict: false
|
|
179
|
+
},
|
|
162
180
|
ne: {
|
|
163
|
-
template: function
|
|
181
|
+
template: function ne(options, attr, name, value) {
|
|
164
182
|
return `${name} <> ${value}`;
|
|
165
183
|
},
|
|
166
184
|
strict: false,
|
|
@@ -339,6 +357,9 @@ class AttributeOperationProxy {
|
|
|
339
357
|
|
|
340
358
|
fromObject(operation, record) {
|
|
341
359
|
for (let path of Object.keys(record)) {
|
|
360
|
+
if (record[path] === undefined) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
342
363
|
const value = record[path];
|
|
343
364
|
const parts = u.parseJSONPath(path);
|
|
344
365
|
let attribute = this.attributes;
|
|
@@ -376,31 +397,61 @@ class AttributeOperationProxy {
|
|
|
376
397
|
|
|
377
398
|
static buildOperations(builder, operations) {
|
|
378
399
|
let ops = {};
|
|
379
|
-
let seen = new
|
|
400
|
+
let seen = new Map();
|
|
380
401
|
for (let operation of Object.keys(operations)) {
|
|
381
|
-
let {template, canNest} = operations[operation];
|
|
402
|
+
let {template, canNest, noAttribute} = operations[operation];
|
|
382
403
|
Object.defineProperty(ops, operation, {
|
|
383
404
|
get: () => {
|
|
384
405
|
return (property, ...values) => {
|
|
385
406
|
if (property === undefined) {
|
|
386
407
|
throw new e.ElectroError(e.ErrorCodes.InvalidWhere, `Invalid/Unknown property passed in where clause passed to operation: '${operation}'`);
|
|
387
408
|
}
|
|
388
|
-
if (property
|
|
389
|
-
const {
|
|
409
|
+
if (property[AttributeProxySymbol]) {
|
|
410
|
+
const {commit, target} = property();
|
|
411
|
+
const fixedValues = values.map((value) => target.applyFixings(value))
|
|
412
|
+
.filter(value => value !== undefined);
|
|
413
|
+
const isFilterBuilder = builder.type === BuilderTypes.filter;
|
|
414
|
+
const takesValueArgument = template.length > 3;
|
|
415
|
+
const isAcceptableValue = fixedValues.every(value => {
|
|
416
|
+
const seenAttributes = seen.get(value);
|
|
417
|
+
if (seenAttributes) {
|
|
418
|
+
return seenAttributes.every(v => target.acceptable(v))
|
|
419
|
+
}
|
|
420
|
+
return target.acceptable(value);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const shouldCommit =
|
|
424
|
+
// if it is a filterBuilder than we don't care what they pass because the user needs more freedom here
|
|
425
|
+
isFilterBuilder ||
|
|
426
|
+
// if the operation does not take a value argument then not committing here could cause problems.
|
|
427
|
+
// this should be revisited to make more robust, we could hypothetically store the commit in the
|
|
428
|
+
// "seen" map for when the value is used, but that's a lot of new complexity
|
|
429
|
+
!takesValueArgument ||
|
|
430
|
+
// if the operation takes a value, we should determine if that value is acceptable. For
|
|
431
|
+
// example, in the cases of a "set" we check to see if it is empty, or if the value is
|
|
432
|
+
// undefined, we should not commit. The "fixedValues" length check is because the
|
|
433
|
+
// "fixedValues" array has been filtered for undefined, so no length there indicates an
|
|
434
|
+
// undefined value was passed.
|
|
435
|
+
(takesValueArgument && isAcceptableValue && fixedValues.length > 0);
|
|
436
|
+
|
|
437
|
+
if (!shouldCommit) {
|
|
438
|
+
return '';
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const paths = commit();
|
|
390
442
|
const attributeValues = [];
|
|
391
443
|
let hasNestedValue = false;
|
|
392
|
-
for (let
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
444
|
+
for (let fixedValue of fixedValues) {
|
|
445
|
+
if (seen.has(fixedValue)) {
|
|
446
|
+
attributeValues.push(fixedValue);
|
|
447
|
+
hasNestedValue = true;
|
|
448
|
+
} else {
|
|
449
|
+
let attributeValueName = builder.setValue(target.name, fixedValue);
|
|
450
|
+
builder.setPath(paths.json, {
|
|
451
|
+
value: fixedValue,
|
|
452
|
+
name: attributeValueName
|
|
453
|
+
});
|
|
454
|
+
attributeValues.push(attributeValueName);
|
|
404
455
|
}
|
|
405
456
|
}
|
|
406
457
|
|
|
@@ -411,8 +462,8 @@ class AttributeOperationProxy {
|
|
|
411
462
|
const formatted = template(options, target, paths.expression, ...attributeValues);
|
|
412
463
|
builder.setImpacted(operation, paths.json, target);
|
|
413
464
|
if (canNest) {
|
|
414
|
-
seen.
|
|
415
|
-
seen.
|
|
465
|
+
seen.set(paths.expression, attributeValues);
|
|
466
|
+
seen.set(formatted, attributeValues);
|
|
416
467
|
}
|
|
417
468
|
|
|
418
469
|
if (builder.type === BuilderTypes.update && formatted && typeof formatted.operation === "string" && typeof formatted.expression === "string") {
|
|
@@ -420,6 +471,17 @@ class AttributeOperationProxy {
|
|
|
420
471
|
return formatted.expression;
|
|
421
472
|
}
|
|
422
473
|
|
|
474
|
+
return formatted;
|
|
475
|
+
} else if (noAttribute) {
|
|
476
|
+
// const {json, expression} = builder.setName({}, property, property);
|
|
477
|
+
let attributeValueName = builder.setValue(property, property);
|
|
478
|
+
builder.setPath(property, {
|
|
479
|
+
value: property,
|
|
480
|
+
name: attributeValueName,
|
|
481
|
+
});
|
|
482
|
+
const formatted = template({}, attributeValueName);
|
|
483
|
+
seen.set(attributeValueName, [property]);
|
|
484
|
+
seen.set(formatted, [property]);
|
|
423
485
|
return formatted;
|
|
424
486
|
} else {
|
|
425
487
|
throw new e.ElectroError(e.ErrorCodes.InvalidWhere, `Invalid Attribute in where clause passed to operation '${operation}'. Use injected attributes only.`);
|
|
@@ -434,15 +496,15 @@ class AttributeOperationProxy {
|
|
|
434
496
|
static pathProxy(build) {
|
|
435
497
|
return new Proxy(() => build(), {
|
|
436
498
|
get: (_, prop, o) => {
|
|
437
|
-
if (prop ===
|
|
438
|
-
return
|
|
499
|
+
if (prop === AttributeProxySymbol) {
|
|
500
|
+
return true;
|
|
439
501
|
} else {
|
|
440
502
|
return AttributeOperationProxy.pathProxy(() => {
|
|
441
|
-
const {
|
|
503
|
+
const { commit, root, target, builder } = build();
|
|
442
504
|
const attribute = target.getChild(prop);
|
|
443
505
|
let field;
|
|
444
506
|
if (attribute === undefined) {
|
|
445
|
-
throw new Error(`Invalid attribute "${prop}" at path "${
|
|
507
|
+
throw new Error(`Invalid attribute "${prop}" at path "${target.path}.${prop}"`);
|
|
446
508
|
} else if (attribute === root && attribute.type === AttributeTypes.any) {
|
|
447
509
|
// This function is only called if a nested property is called. If this attribute is ultimately the root, don't use the root's field name
|
|
448
510
|
field = prop;
|
|
@@ -454,7 +516,10 @@ class AttributeOperationProxy {
|
|
|
454
516
|
root,
|
|
455
517
|
builder,
|
|
456
518
|
target: attribute,
|
|
457
|
-
|
|
519
|
+
commit: () => {
|
|
520
|
+
const paths = commit();
|
|
521
|
+
return builder.setName(paths, prop, field);
|
|
522
|
+
},
|
|
458
523
|
}
|
|
459
524
|
});
|
|
460
525
|
}
|
|
@@ -468,12 +533,11 @@ class AttributeOperationProxy {
|
|
|
468
533
|
Object.defineProperty(attr, name, {
|
|
469
534
|
get: () => {
|
|
470
535
|
return AttributeOperationProxy.pathProxy(() => {
|
|
471
|
-
const paths = builder.setName({}, attribute.name, attribute.field);
|
|
472
536
|
return {
|
|
473
|
-
paths,
|
|
474
537
|
root: attribute,
|
|
475
538
|
target: attribute,
|
|
476
539
|
builder,
|
|
540
|
+
commit: () => builder.setName({}, attribute.name, attribute.field)
|
|
477
541
|
}
|
|
478
542
|
});
|
|
479
543
|
}
|
package/src/schema.js
CHANGED
|
@@ -238,6 +238,10 @@ class Attribute {
|
|
|
238
238
|
|
|
239
239
|
_makeApplyFixings({ prefix = "", postfix = "", casing= KeyCasing.none } = {}) {
|
|
240
240
|
return (value) => {
|
|
241
|
+
if (value === undefined) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
241
245
|
if ([AttributeTypes.string, AttributeTypes.enum].includes(this.type)) {
|
|
242
246
|
value = `${prefix}${value}${postfix}`;
|
|
243
247
|
}
|
|
@@ -305,6 +309,10 @@ class Attribute {
|
|
|
305
309
|
};
|
|
306
310
|
}
|
|
307
311
|
|
|
312
|
+
acceptable(val) {
|
|
313
|
+
return val !== undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
308
316
|
getPathType(type, parentType) {
|
|
309
317
|
if (parentType === AttributeTypes.list || parentType === AttributeTypes.set) {
|
|
310
318
|
return PathTypes.item;
|
|
@@ -458,7 +466,7 @@ class Attribute {
|
|
|
458
466
|
let reason = [];
|
|
459
467
|
switch (this.type) {
|
|
460
468
|
case AttributeTypes.enum:
|
|
461
|
-
case AttributeTypes.enumSet:
|
|
469
|
+
// case AttributeTypes.enumSet:
|
|
462
470
|
// isTyped = this.enumArray.every(enumValue => {
|
|
463
471
|
// const val = Array.isArray(value) ? value : [value];
|
|
464
472
|
// return val.includes(enumValue);
|
|
@@ -876,12 +884,18 @@ class SetAttribute extends Attribute {
|
|
|
876
884
|
value = Array.isArray(value)
|
|
877
885
|
? Array.from(new Set(value))
|
|
878
886
|
: value;
|
|
879
|
-
return this.client.createSet(value, {validate: true});
|
|
887
|
+
return this.client.createSet(value, { validate: true });
|
|
880
888
|
} else {
|
|
881
889
|
return new DynamoDBSet(value, this.items.type);
|
|
882
890
|
}
|
|
883
891
|
}
|
|
884
892
|
|
|
893
|
+
acceptable(val) {
|
|
894
|
+
return Array.isArray(val)
|
|
895
|
+
? val.length > 0
|
|
896
|
+
: this.items.acceptable(val);
|
|
897
|
+
}
|
|
898
|
+
|
|
885
899
|
toDDBSet(value) {
|
|
886
900
|
const valueType = getValueType(value);
|
|
887
901
|
let array;
|
|
@@ -1474,11 +1488,16 @@ function CustomAttributeType(base) {
|
|
|
1474
1488
|
return base;
|
|
1475
1489
|
}
|
|
1476
1490
|
|
|
1491
|
+
function createSchema(schema) {
|
|
1492
|
+
return v.model(schema);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1477
1495
|
module.exports = {
|
|
1478
1496
|
Schema,
|
|
1479
1497
|
Attribute,
|
|
1480
|
-
SetAttribute,
|
|
1481
1498
|
CastTypes,
|
|
1499
|
+
SetAttribute,
|
|
1500
|
+
createSchema,
|
|
1482
1501
|
CustomAttributeType,
|
|
1483
1502
|
createCustomAttribute,
|
|
1484
1503
|
};
|
package/src/service.js
CHANGED
|
@@ -155,7 +155,7 @@ class Service {
|
|
|
155
155
|
default:
|
|
156
156
|
/** start beta/v1 condition **/
|
|
157
157
|
if (modelVersion !== this._modelVersion) {
|
|
158
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, "Invalid instance: Valid instances to join include Models and Entity instances.
|
|
158
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, "Invalid instance: Valid instances to join include Models and Entity instances.");
|
|
159
159
|
} else if (modelVersion === ModelVersions.beta) {
|
|
160
160
|
instance = applyBetaModelOverrides(instance, this._modelOverrides);
|
|
161
161
|
} else {
|
package/src/types.js
CHANGED
|
@@ -271,6 +271,23 @@ const ResultOrderOption = {
|
|
|
271
271
|
|
|
272
272
|
const ResultOrderParam = 'ScanIndexForward';
|
|
273
273
|
|
|
274
|
+
const DynamoDBAttributeTypes = Object.entries({
|
|
275
|
+
string: 'S',
|
|
276
|
+
stringSet: 'SS',
|
|
277
|
+
number: 'N',
|
|
278
|
+
numberSet: 'NS',
|
|
279
|
+
binary: 'B',
|
|
280
|
+
binarySet: 'BS',
|
|
281
|
+
boolean: 'BOOL',
|
|
282
|
+
null: 'NULL',
|
|
283
|
+
list: 'L',
|
|
284
|
+
map: 'M',
|
|
285
|
+
}).reduce((obj, [name, type]) => {
|
|
286
|
+
obj[name] = type;
|
|
287
|
+
obj[type] = type;
|
|
288
|
+
return obj;
|
|
289
|
+
}, {});
|
|
290
|
+
|
|
274
291
|
module.exports = {
|
|
275
292
|
Pager,
|
|
276
293
|
KeyTypes,
|
|
@@ -303,6 +320,7 @@ module.exports = {
|
|
|
303
320
|
ElectroInstanceTypes,
|
|
304
321
|
MethodTypeTranslation,
|
|
305
322
|
EventSubscriptionTypes,
|
|
323
|
+
DynamoDBAttributeTypes,
|
|
306
324
|
AttributeMutationMethods,
|
|
307
325
|
AllPages,
|
|
308
326
|
ResultOrderOption,
|