electrodb 2.3.5 → 2.4.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/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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.3.5",
3
+ "version": "2.4.1",
4
4
  "description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
5
5
  "main": "index.js",
6
6
  "scripts": {
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 eq(options, attr, name, value) {
181
+ template: function ne(options, attr, name, value) {
164
182
  return `${name} <> ${value}`;
165
183
  },
166
184
  strict: false,
@@ -379,31 +397,61 @@ class AttributeOperationProxy {
379
397
 
380
398
  static buildOperations(builder, operations) {
381
399
  let ops = {};
382
- let seen = new Set();
400
+ let seen = new Map();
383
401
  for (let operation of Object.keys(operations)) {
384
- let {template, canNest} = operations[operation];
402
+ let {template, canNest, noAttribute} = operations[operation];
385
403
  Object.defineProperty(ops, operation, {
386
404
  get: () => {
387
405
  return (property, ...values) => {
388
406
  if (property === undefined) {
389
407
  throw new e.ElectroError(e.ErrorCodes.InvalidWhere, `Invalid/Unknown property passed in where clause passed to operation: '${operation}'`);
390
408
  }
391
- if (property.__is_clause__ === AttributeProxySymbol) {
392
- const {paths, root, target} = property();
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();
393
442
  const attributeValues = [];
394
443
  let hasNestedValue = false;
395
- for (let value of values) {
396
- value = target.applyFixings(value);
397
- // template.length is to see if function takes value argument
398
- if (template.length > 3) {
399
- if (seen.has(value)) {
400
- attributeValues.push(value);
401
- hasNestedValue = true;
402
- } else {
403
- let attributeValueName = builder.setValue(target.name, value);
404
- builder.setPath(paths.json, {value, name: attributeValueName});
405
- attributeValues.push(attributeValueName);
406
- }
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);
407
455
  }
408
456
  }
409
457
 
@@ -414,8 +462,8 @@ class AttributeOperationProxy {
414
462
  const formatted = template(options, target, paths.expression, ...attributeValues);
415
463
  builder.setImpacted(operation, paths.json, target);
416
464
  if (canNest) {
417
- seen.add(paths.expression);
418
- seen.add(formatted);
465
+ seen.set(paths.expression, attributeValues);
466
+ seen.set(formatted, attributeValues);
419
467
  }
420
468
 
421
469
  if (builder.type === BuilderTypes.update && formatted && typeof formatted.operation === "string" && typeof formatted.expression === "string") {
@@ -423,6 +471,17 @@ class AttributeOperationProxy {
423
471
  return formatted.expression;
424
472
  }
425
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]);
426
485
  return formatted;
427
486
  } else {
428
487
  throw new e.ElectroError(e.ErrorCodes.InvalidWhere, `Invalid Attribute in where clause passed to operation '${operation}'. Use injected attributes only.`);
@@ -437,15 +496,15 @@ class AttributeOperationProxy {
437
496
  static pathProxy(build) {
438
497
  return new Proxy(() => build(), {
439
498
  get: (_, prop, o) => {
440
- if (prop === "__is_clause__") {
441
- return AttributeProxySymbol;
499
+ if (prop === AttributeProxySymbol) {
500
+ return true;
442
501
  } else {
443
502
  return AttributeOperationProxy.pathProxy(() => {
444
- const { paths, root, target, builder } = build();
503
+ const { commit, root, target, builder } = build();
445
504
  const attribute = target.getChild(prop);
446
505
  let field;
447
506
  if (attribute === undefined) {
448
- throw new Error(`Invalid attribute "${prop}" at path "${paths.json}".`);
507
+ throw new Error(`Invalid attribute "${prop}" at path "${target.path}.${prop}"`);
449
508
  } else if (attribute === root && attribute.type === AttributeTypes.any) {
450
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
451
510
  field = prop;
@@ -457,7 +516,10 @@ class AttributeOperationProxy {
457
516
  root,
458
517
  builder,
459
518
  target: attribute,
460
- paths: builder.setName(paths, prop, field),
519
+ commit: () => {
520
+ const paths = commit();
521
+ return builder.setName(paths, prop, field);
522
+ },
461
523
  }
462
524
  });
463
525
  }
@@ -471,12 +533,11 @@ class AttributeOperationProxy {
471
533
  Object.defineProperty(attr, name, {
472
534
  get: () => {
473
535
  return AttributeOperationProxy.pathProxy(() => {
474
- const paths = builder.setName({}, attribute.name, attribute.field);
475
536
  return {
476
- paths,
477
537
  root: attribute,
478
538
  target: attribute,
479
539
  builder,
540
+ commit: () => builder.setName({}, attribute.name, attribute.field)
480
541
  }
481
542
  });
482
543
  }
package/src/schema.js CHANGED
@@ -238,7 +238,11 @@ class Attribute {
238
238
 
239
239
  _makeApplyFixings({ prefix = "", postfix = "", casing= KeyCasing.none } = {}) {
240
240
  return (value) => {
241
- if ([AttributeTypes.string, AttributeTypes.enum].includes(this.type) && value !== undefined) {
241
+ if (value === undefined) {
242
+ return;
243
+ }
244
+
245
+ if ([AttributeTypes.string, AttributeTypes.enum].includes(this.type)) {
242
246
  value = `${prefix}${value}${postfix}`;
243
247
  }
244
248
 
@@ -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;
@@ -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,17 @@ function CustomAttributeType(base) {
1474
1488
  return base;
1475
1489
  }
1476
1490
 
1491
+ function createSchema(schema) {
1492
+ v.model(schema);
1493
+ return schema;
1494
+ }
1495
+
1477
1496
  module.exports = {
1478
1497
  Schema,
1479
1498
  Attribute,
1480
- SetAttribute,
1481
1499
  CastTypes,
1500
+ SetAttribute,
1501
+ createSchema,
1482
1502
  CustomAttributeType,
1483
1503
  createCustomAttribute,
1484
1504
  };
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,