electrodb 2.1.0 → 2.1.2

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/README.md CHANGED
@@ -3401,7 +3401,7 @@ await StoreLocations
3401
3401
  o.add(a.tenant, newTenant); // electrodb "set" -> dynamodb "set"
3402
3402
  o.add(a.rent, 100); // electrodb "number" -> dynamodb "number"
3403
3403
  o.subtract(a.deposit, 200); // electrodb "number" -> dynamodb "number"
3404
- o.remove(a.leaseEndDate); // electrodb "string" -> dynamodb "string"
3404
+ o.remove(attr.discount); // electrodb "number" -> dynamodb "number"
3405
3405
  o.append(a.rentalAgreement, [{ // electrodb "list" -> dynamodb "list"
3406
3406
  type: "ammendment", // electrodb "map" -> dynamodb "map"
3407
3407
  detail: "no soup for you"
@@ -3425,40 +3425,49 @@ Response Format:
3425
3425
  Equivalent DocClient Parameters:
3426
3426
  ```json
3427
3427
  {
3428
- "UpdateExpression": "SET #category = :category_u0, #rent = #rent + :rent_u0, #deposit = #deposit - :deposit_u0, #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement_u0), #totalFees = #totalFees + #petFee REMOVE #leaseEndDate, #gsi2sk ADD #tenant :tenant_u0, #leaseHolders :tenant_u0 DELETE #tags :tags_u0, #contact :contact_u0",
3429
- "ExpressionAttributeNames": {
3430
- "#category": "category",
3431
- "#tenant": "tenant",
3432
- "#rent": "rent",
3433
- "#deposit": "deposit",
3434
- "#leaseEndDate": "leaseEndDate",
3435
- "#rentalAgreement": "rentalAgreement",
3436
- "#tags": "tags",
3437
- "#contact": "contact",
3438
- "#totalFees": "totalFees",
3439
- "#petFee": "petFee",
3440
- "#leaseHolders": "leaseHolders",
3441
- "#gsi2sk": "gsi2sk"
3442
- },
3443
- "ExpressionAttributeValues": {
3444
- ":category0": "food/coffee",
3445
- ":category_u0": "food/meal",
3446
- ":tenant_u0": ["larry"],
3447
- ":rent_u0": 100,
3448
- ":deposit_u0": 200,
3449
- ":rentalAgreement_u0": [{
3450
- "type": "amendment",
3451
- "detail": "no soup for you"
3452
- }],
3453
- ":tags_u0": ["coffee"], // <- DynamoDB Set
3454
- ":contact_u0": ["555-345-2222"], // <- DynamoDB Set
3428
+ "UpdateExpression": "SET #category = :category_u0, #deposit = #deposit - :deposit_u0, #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement_u0), #totalFees = #totalFees + #petFee, #cityId = :cityId_u0, #mallId = :mallId_u0, #buildingId = :buildingId_u0, #storeId = :storeId_u0, #__edb_e__ = :__edb_e___u0, #__edb_v__ = :__edb_v___u0 REMOVE #discount ADD #tenant :tenant_u0, #rent :rent_u0, #leaseHolders :tenant_u0 DELETE #tags :tags_u0, #contact :contact_u0",
3429
+ "ExpressionAttributeNames": {
3430
+ "#category": "category",
3431
+ "#tenant": "tenant",
3432
+ "#rent": "rent",
3433
+ "#deposit": "deposit",
3434
+ "#discount": "discount",
3435
+ "#rentalAgreement": "rentalAgreement",
3436
+ "#tags": "tags",
3437
+ "#contact": "contact",
3438
+ "#totalFees": "totalFees",
3439
+ "#petFee": "petFee",
3440
+ "#leaseHolders": "leaseHolders",
3441
+ "#buildingId": "buildingId",
3442
+ "#cityId": "cityId",
3443
+ "#mallId": "mallId",
3444
+ "#storeId": "storeId",
3445
+ "#__edb_e__": "__edb_e__", "#__edb_v__": "__edb_v__",
3455
3446
  },
3456
- "TableName": "electro",
3457
- "Key": {
3458
- "pk": `$mallstoredirectory#cityid_12345#mallid_eastpointe`,
3459
- "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
3460
- },
3461
- "ConditionExpression": "#category = :category0"
3447
+ "ExpressionAttributeValues": {
3448
+ ":buildingId_u0": "A34",
3449
+ ":cityId_u0": "portland",
3450
+ ":category0": "food/coffee",
3451
+ ":category_u0": "food/meal",
3452
+ ":tenant_u0": ["larry"],
3453
+ ":rent_u0": 100,
3454
+ ":deposit_u0": 200,
3455
+ ":rentalAgreement_u0": [{
3456
+ "type": "amendment",
3457
+ "detail": "no soup for you"
3458
+ }],
3459
+ ":tags_u0": ["coffee"],
3460
+ ":contact_u0": ["555-345-2222"],
3461
+ ":mallId_u0": "EastPointe",
3462
+ ":storeId_u0": "LatteLarrys",
3463
+ ":__edb_e___u0": "MallStore", ":__edb_v___u0": "1",
3464
+ },
3465
+ "TableName": "electro",
3466
+ "Key": {
3467
+ "pk": "$mallstoredirectory#cityid_portland#mallid_eastpointe",
3468
+ "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
3469
+ },
3470
+ "ConditionExpression": "#category = :category0"
3462
3471
  }
3463
3472
  ```
3464
3473
 
package/index.d.ts CHANGED
@@ -698,7 +698,7 @@ export type BatchWriteGo<ResponseType> = <O extends BulkOptions>(options?: O) =>
698
698
 
699
699
  export type ParamRecord<Options = ParamOptions> = <P>(options?: Options) => P;
700
700
 
701
- export interface ElectroError extends Error {
701
+ export class ElectroError extends Error {
702
702
  readonly name: 'ElectroError';
703
703
  readonly code: number;
704
704
  readonly date: number;
@@ -733,7 +733,7 @@ export interface ElectroValidationErrorFieldReference<T extends Error = Error> {
733
733
  readonly cause: T | undefined;
734
734
  }
735
735
 
736
- export interface ElectroValidationError<T extends Error = Error> extends ElectroError {
736
+ export class ElectroValidationError<T extends Error = Error> extends ElectroError {
737
737
  readonly fields: ReadonlyArray<ElectroValidationErrorFieldReference<T>>;
738
738
  }
739
739
 
package/index.js CHANGED
@@ -1,5 +1,12 @@
1
1
  const { Entity } = require("./src/entity");
2
2
  const { Service } = require("./src/service");
3
3
  const { createCustomAttribute } = require('./src/schema');
4
+ const { ElectroError, ElectroValidationError, ElectroUserValidationError, ElectroAttributeValidationError } = require('./src/errors');
4
5
 
5
- module.exports = { Entity, Service, createCustomAttribute };
6
+ module.exports = {
7
+ Entity,
8
+ Service,
9
+ createCustomAttribute,
10
+ ElectroError,
11
+ ElectroValidationError,
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
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/clauses.js CHANGED
@@ -277,6 +277,11 @@ let clauses = {
277
277
  }
278
278
  try {
279
279
  state.query.updateProxy.invokeCallback(cb);
280
+ for (const path of Object.keys(state.query.update.refs)) {
281
+ const operation = state.query.update.impacted[path];
282
+ const attribute = state.query.update.refs[path];
283
+ entity.model.schema.checkOperation(attribute, operation);
284
+ }
280
285
  return state;
281
286
  } catch(err) {
282
287
  state.setError(err);
package/src/entity.js CHANGED
@@ -624,7 +624,7 @@ class Entity {
624
624
 
625
625
  if (config._isPagination || response.LastEvaluatedKey) {
626
626
  const nextPage = this._formatReturnPager(config, response.LastEvaluatedKey);
627
- return { cursor: nextPage ?? null, data: results };
627
+ return { cursor: nextPage || null, data: results };
628
628
  }
629
629
 
630
630
  return { data: results };
@@ -652,19 +652,19 @@ class Entity {
652
652
  }
653
653
 
654
654
  _formatReturnPager(config, lastEvaluatedKey) {
655
- let page = lastEvaluatedKey ?? null;
655
+ let page = lastEvaluatedKey || null;
656
656
  if (config.raw || config.pager === Pager.raw) {
657
657
  return page;
658
658
  }
659
- return config.formatCursor.serialize(page) ?? null;
659
+ return config.formatCursor.serialize(page) || null;
660
660
  }
661
661
 
662
662
  _formatExclusiveStartKey(config) {
663
663
  let exclusiveStartKey = config.cursor;
664
664
  if (config.raw || config.pager === Pager.raw) {
665
- return exclusiveStartKey ?? null;
665
+ return exclusiveStartKey || null;
666
666
  }
667
- return config.formatCursor.deserialize(exclusiveStartKey) ?? null;
667
+ return config.formatCursor.deserialize(exclusiveStartKey) || null;
668
668
  }
669
669
 
670
670
  _getTableName() {
@@ -1972,7 +1972,7 @@ class Entity {
1972
1972
  _buildQueryFacets(facets, skFacets) {
1973
1973
  let queryFacets = this._findProperties(facets, skFacets).reduce(
1974
1974
  (result, [name, value]) => {
1975
- if (value) {
1975
+ if (value !== undefined) {
1976
1976
  result[name] = value;
1977
1977
  }
1978
1978
  return result;
package/src/operations.js CHANGED
@@ -256,6 +256,7 @@ class ExpressionState {
256
256
  this.impacted = {};
257
257
  this.expression = "";
258
258
  this.prefix = prefix || "";
259
+ this.refs = {};
259
260
  }
260
261
 
261
262
  incrementName(name) {
@@ -316,8 +317,9 @@ class ExpressionState {
316
317
  return this.expression;
317
318
  }
318
319
 
319
- setImpacted(operation, path) {
320
+ setImpacted(operation, path, ref) {
320
321
  this.impacted[path] = operation;
322
+ this.refs[path] = ref;
321
323
  }
322
324
  }
323
325
 
@@ -407,7 +409,7 @@ class AttributeOperationProxy {
407
409
  }
408
410
 
409
411
  const formatted = template(options, target, paths.expression, ...attributeValues);
410
- builder.setImpacted(operation, paths.json);
412
+ builder.setImpacted(operation, paths.json, target);
411
413
  if (canNest) {
412
414
  seen.add(paths.expression);
413
415
  seen.add(formatted);
package/src/schema.js CHANGED
@@ -1361,6 +1361,16 @@ class Schema {
1361
1361
  return record;
1362
1362
  }
1363
1363
 
1364
+ checkOperation(attribute, operation) {
1365
+ if (!attribute) {
1366
+ throw new e.ElectroAttributeValidationError(path, `Attribute "${path}" does not exist on model.`);
1367
+ } else if (attribute.required && operation === 'remove') {
1368
+ throw new e.ElectroAttributeValidationError(attribute.path, `Attribute "${attribute.path}" is Required and cannot be removed`);
1369
+ } else if (attribute.readOnly) {
1370
+ throw new e.ElectroAttributeValidationError(attribute.path, `Attribute "${attribute.path}" is Read-Only and cannot be updated`);
1371
+ }
1372
+ }
1373
+
1364
1374
  checkRemove(paths = []) {
1365
1375
  for (const path of paths) {
1366
1376
  const attribute = this.traverser.getPath(path);