electrodb 2.8.0 → 2.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.8.0",
3
+ "version": "2.8.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/entity.js CHANGED
@@ -1886,12 +1886,20 @@ class Entity {
1886
1886
  // should only happen when an attribute is changed.
1887
1887
  const { indexKey, updatedKeys, deletedKeys = [] } = this._getUpdatedKeys(pk, sk, preparedUpdateValues, removed);
1888
1888
  const accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[TableIndex];
1889
-
1890
1889
  for (const path of Object.keys(preparedUpdateValues)) {
1891
1890
  if (modifiedAttributeNames[path] !== undefined && preparedUpdateValues[path] !== undefined) {
1892
1891
  update.updateValue(modifiedAttributeNames[path], preparedUpdateValues[path]);
1893
1892
  } else if (preparedUpdateValues[path] !== undefined) {
1894
- update.set(path, preparedUpdateValues[path]);
1893
+ const attr = this.model.schema.getAttribute(path);
1894
+ if (attr) {
1895
+ // attributes might enter into this flow because they were triggered via a `watch` event and were
1896
+ // not supplied directly by the user. In this case we should set the field name.
1897
+ // TODO: This will only work with root attributes and should be refactored for nested attributes.
1898
+ update.set(attr.field, preparedUpdateValues[path]);
1899
+ } else {
1900
+ // this could be fields added by electro that don't apeear in the schema
1901
+ update.set(path, preparedUpdateValues[path]);
1902
+ }
1895
1903
  }
1896
1904
  }
1897
1905
 
@@ -1901,7 +1909,6 @@ class Entity {
1901
1909
  const wasNotAlreadyModified = modifiedAttributeNames[indexKey] === undefined;
1902
1910
  if (isNotTablePK && isNotTableSK && wasNotAlreadyModified) {
1903
1911
  update.set(indexKey, updatedKeys[indexKey]);
1904
-
1905
1912
  }
1906
1913
  }
1907
1914
 
@@ -1948,7 +1955,6 @@ class Entity {
1948
1955
  _makePutParams({ data } = {}, pk, sk) {
1949
1956
  let { updatedKeys, setAttributes } = this._getPutKeys(pk, sk && sk.facets, data);
1950
1957
  let translatedFields = this.model.schema.translateToFields(setAttributes);
1951
-
1952
1958
  return {
1953
1959
  Item: {
1954
1960
  ...translatedFields,
@@ -2406,8 +2412,10 @@ class Entity {
2406
2412
  indexKey[sk] = keys.sk[0];
2407
2413
  }
2408
2414
  }
2409
- updatedKeys[pk] = keys.pk;
2410
- if (sk) {
2415
+ if (keys.pk !== undefined && keys.pk !== '') {
2416
+ updatedKeys[pk] = keys.pk;
2417
+ }
2418
+ if (sk && keys.sk[0] !== undefined && keys.sk[0] !== '') {
2411
2419
  updatedKeys[sk] = keys.sk[0];
2412
2420
  }
2413
2421
  }
@@ -3298,7 +3306,9 @@ class Entity {
3298
3306
  if (Array.isArray(sk.facets)) {
3299
3307
  let duplicates = pk.facets.filter(facet => sk.facets.includes(facet));
3300
3308
  if (duplicates.length !== 0) {
3301
- throw new e.ElectroError(e.ErrorCodes.DuplicateIndexCompositeAttributes, `The Access Pattern '${accessPattern}' contains duplicate references the composite attribute(s): ${u.commaSeparatedString(duplicates)}. Composite attributes may only be used once within an index. If this leaves the Sort Key (sk) without any composite attributes simply set this to be an empty array.`);
3309
+ if (sk.facets.length > 1) {
3310
+ throw new e.ElectroError(e.ErrorCodes.DuplicateIndexCompositeAttributes, `The Access Pattern '${accessPattern}' contains duplicate references the composite attribute(s): ${u.commaSeparatedString(duplicates)}. Composite attributes can only be used more than once in an index if your sort key is limitted to a single attribute. This is to prevent unexpected runtime errors related to the inability to generate keys.`);
3311
+ }
3302
3312
  }
3303
3313
  }
3304
3314
 
@@ -3595,7 +3605,6 @@ class Entity {
3595
3605
 
3596
3606
  _parseModel(model, config = {}) {
3597
3607
  /** start beta/v1 condition **/
3598
- const {client} = config;
3599
3608
  let modelVersion = u.getModelVersion(model);
3600
3609
  let service, entity, version, table, name;
3601
3610
  switch(modelVersion) {
@@ -3635,7 +3644,10 @@ class Entity {
3635
3644
  indexAccessPattern,
3636
3645
  indexHasSubCollections,
3637
3646
  } = this._normalizeIndexes(model.indexes);
3638
- let schema = new Schema(model.attributes, facets, {client, isRoot: true});
3647
+ let schema = new Schema(model.attributes, facets, {
3648
+ getClient: () => this.client,
3649
+ isRoot: true,
3650
+ });
3639
3651
  let filters = this._normalizeFilters(model.filters);
3640
3652
  // todo: consider a rename
3641
3653
  let prefixes = this._normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes, schema});
package/src/schema.js CHANGED
@@ -127,7 +127,7 @@ class Attribute {
127
127
  this.parent = { parentType: this.type, parentPath: this.path };
128
128
  this.get = this._makeGet(definition.get);
129
129
  this.set = this._makeSet(definition.set);
130
- this.client = definition.client;
130
+ this.getClient = definition.getClient;
131
131
  }
132
132
 
133
133
  static buildChildAttributes(type, definition, parent) {
@@ -145,25 +145,25 @@ class Attribute {
145
145
  }
146
146
 
147
147
  static buildChildListItems(definition, parent) {
148
- const {items, client} = definition;
148
+ const {items, getClient} = definition;
149
149
  const prop = {...items, ...parent};
150
150
  // The use of "*" is to ensure the child's name is "*" when added to the traverser and searching for the children of a list
151
- return Schema.normalizeAttributes({ '*': prop }, {}, {client, traverser: parent.traverser, parent}).attributes["*"];
151
+ return Schema.normalizeAttributes({ '*': prop }, {}, {getClient, traverser: parent.traverser, parent}).attributes["*"];
152
152
  }
153
153
 
154
154
  static buildChildSetItems(definition, parent) {
155
- const {items, client} = definition;
155
+ const {items, getClient} = definition;
156
156
 
157
157
  const allowedTypes = [AttributeTypes.string, AttributeTypes.boolean, AttributeTypes.number, AttributeTypes.enum];
158
158
  if (!Array.isArray(items) && !allowedTypes.includes(items)) {
159
159
  throw new e.ElectroError(e.ErrorCodes.InvalidAttributeDefinition, `Invalid "items" definition for Set attribute: "${definition.path}". Acceptable item types include ${u.commaSeparatedString(allowedTypes)}`);
160
160
  }
161
161
  const prop = {type: items, ...parent};
162
- return Schema.normalizeAttributes({ prop }, {}, {client, traverser: parent.traverser, parent}).attributes.prop;
162
+ return Schema.normalizeAttributes({ prop }, {}, {getClient, traverser: parent.traverser, parent}).attributes.prop;
163
163
  }
164
164
 
165
165
  static buildChildMapProperties(definition, parent) {
166
- const {properties, client} = definition;
166
+ const {properties, getClient} = definition;
167
167
  if (!properties || typeof properties !== "object") {
168
168
  throw new e.ElectroError(e.ErrorCodes.InvalidAttributeDefinition, `Invalid "properties" definition for Map attribute: "${definition.path}". The "properties" definition must describe the attributes that the Map will accept`);
169
169
  }
@@ -171,7 +171,7 @@ class Attribute {
171
171
  for (let name of Object.keys(properties)) {
172
172
  attributes[name] = {...properties[name], ...parent};
173
173
  }
174
- return Schema.normalizeAttributes(attributes, {}, {client, traverser: parent.traverser, parent});
174
+ return Schema.normalizeAttributes(attributes, {}, {getClient, traverser: parent.traverser, parent});
175
175
  }
176
176
 
177
177
  static buildPath(name, type, parentPath) {
@@ -886,11 +886,12 @@ class SetAttribute extends Attribute {
886
886
  }
887
887
 
888
888
  _createDDBSet(value) {
889
- if (this.client && typeof this.client.createSet === "function") {
889
+ const client = this.getClient();
890
+ if (client && typeof client.createSet === "function") {
890
891
  value = Array.isArray(value)
891
892
  ? Array.from(new Set(value))
892
893
  : value;
893
- return this.client.createSet(value, { validate: true });
894
+ return client.createSet(value, { validate: true });
894
895
  } else {
895
896
  return new DynamoDBSet(value, this.items.type);
896
897
  }
@@ -1005,10 +1006,10 @@ class SetAttribute extends Attribute {
1005
1006
  }
1006
1007
 
1007
1008
  class Schema {
1008
- constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), client, parent, isRoot} = {}) {
1009
+ constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), getClient, parent, isRoot} = {}) {
1009
1010
  this._validateProperties(properties, parent);
1010
- let schema = Schema.normalizeAttributes(properties, facets, {traverser, client, parent, isRoot});
1011
- this.client = client;
1011
+ let schema = Schema.normalizeAttributes(properties, facets, {traverser, getClient, parent, isRoot});
1012
+ this.getClient = getClient;
1012
1013
  this.attributes = schema.attributes;
1013
1014
  this.enums = schema.enums;
1014
1015
  this.translationForTable = schema.translationForTable;
@@ -1021,7 +1022,7 @@ class Schema {
1021
1022
  this.isRoot = !!isRoot;
1022
1023
  }
1023
1024
 
1024
- static normalizeAttributes(attributes = {}, facets = {}, {traverser, client, parent, isRoot} = {}) {
1025
+ static normalizeAttributes(attributes = {}, facets = {}, {traverser, getClient, parent, isRoot} = {}) {
1025
1026
  const attributeHasParent = !!parent;
1026
1027
  let invalidProperties = [];
1027
1028
  let normalized = {};
@@ -1114,7 +1115,7 @@ class Schema {
1114
1115
  let definition = {
1115
1116
  name,
1116
1117
  field,
1117
- client,
1118
+ getClient,
1118
1119
  casing,
1119
1120
  prefix,
1120
1121
  postfix,