Skip to content

Commit

Permalink
Defect/fixes set creation when client is set after entity construction (
Browse files Browse the repository at this point in the history
#276)

* 2.8.1
Fixes set creation if client set after entity construction

* 2.8.1
Change log and package version bump
  • Loading branch information
tywalch authored Aug 6, 2023
1 parent 882c34a commit ecda560
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 19 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,4 +357,8 @@ All notable changes to this project will be documented in this file. Breaking ch
- Adds new `cast` option for indexes to allow users to cast index values to a different type than their composite attribute. This change comers from a user requested feature and addresses issue #237

### Fixed
- Fixed edge case when model defines an index without composites while using the template syntax that prevented `ignoreOwnership` from correctly gating return records
- Fixed edge case when model defines an index without composites while using the template syntax that prevented `ignoreOwnership` from correctly gating return records

## [2.8.1] - 2023-08-06
### Fixed
- Fixes bug with creating sets when client is provided or changed after initial Entity construction
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "electrodb",
"version": "2.8.0",
"version": "2.8.1",
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
"main": "index.js",
"scripts": {
Expand Down
6 changes: 4 additions & 2 deletions src/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -3595,7 +3595,6 @@ class Entity {

_parseModel(model, config = {}) {
/** start beta/v1 condition **/
const {client} = config;
let modelVersion = u.getModelVersion(model);
let service, entity, version, table, name;
switch(modelVersion) {
Expand Down Expand Up @@ -3635,7 +3634,10 @@ class Entity {
indexAccessPattern,
indexHasSubCollections,
} = this._normalizeIndexes(model.indexes);
let schema = new Schema(model.attributes, facets, {client, isRoot: true});
let schema = new Schema(model.attributes, facets, {
getClient: () => this.client,
isRoot: true,
});
let filters = this._normalizeFilters(model.filters);
// todo: consider a rename
let prefixes = this._normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes, schema});
Expand Down
29 changes: 15 additions & 14 deletions src/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class Attribute {
this.parent = { parentType: this.type, parentPath: this.path };
this.get = this._makeGet(definition.get);
this.set = this._makeSet(definition.set);
this.client = definition.client;
this.getClient = definition.getClient;
}

static buildChildAttributes(type, definition, parent) {
Expand All @@ -145,33 +145,33 @@ class Attribute {
}

static buildChildListItems(definition, parent) {
const {items, client} = definition;
const {items, getClient} = definition;
const prop = {...items, ...parent};
// The use of "*" is to ensure the child's name is "*" when added to the traverser and searching for the children of a list
return Schema.normalizeAttributes({ '*': prop }, {}, {client, traverser: parent.traverser, parent}).attributes["*"];
return Schema.normalizeAttributes({ '*': prop }, {}, {getClient, traverser: parent.traverser, parent}).attributes["*"];
}

static buildChildSetItems(definition, parent) {
const {items, client} = definition;
const {items, getClient} = definition;

const allowedTypes = [AttributeTypes.string, AttributeTypes.boolean, AttributeTypes.number, AttributeTypes.enum];
if (!Array.isArray(items) && !allowedTypes.includes(items)) {
throw new e.ElectroError(e.ErrorCodes.InvalidAttributeDefinition, `Invalid "items" definition for Set attribute: "${definition.path}". Acceptable item types include ${u.commaSeparatedString(allowedTypes)}`);
}
const prop = {type: items, ...parent};
return Schema.normalizeAttributes({ prop }, {}, {client, traverser: parent.traverser, parent}).attributes.prop;
return Schema.normalizeAttributes({ prop }, {}, {getClient, traverser: parent.traverser, parent}).attributes.prop;
}

static buildChildMapProperties(definition, parent) {
const {properties, client} = definition;
const {properties, getClient} = definition;
if (!properties || typeof properties !== "object") {
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`);
}
const attributes = {};
for (let name of Object.keys(properties)) {
attributes[name] = {...properties[name], ...parent};
}
return Schema.normalizeAttributes(attributes, {}, {client, traverser: parent.traverser, parent});
return Schema.normalizeAttributes(attributes, {}, {getClient, traverser: parent.traverser, parent});
}

static buildPath(name, type, parentPath) {
Expand Down Expand Up @@ -886,11 +886,12 @@ class SetAttribute extends Attribute {
}

_createDDBSet(value) {
if (this.client && typeof this.client.createSet === "function") {
const client = this.getClient();
if (client && typeof client.createSet === "function") {
value = Array.isArray(value)
? Array.from(new Set(value))
: value;
return this.client.createSet(value, { validate: true });
return client.createSet(value, { validate: true });
} else {
return new DynamoDBSet(value, this.items.type);
}
Expand Down Expand Up @@ -1005,10 +1006,10 @@ class SetAttribute extends Attribute {
}

class Schema {
constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), client, parent, isRoot} = {}) {
constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), getClient, parent, isRoot} = {}) {
this._validateProperties(properties, parent);
let schema = Schema.normalizeAttributes(properties, facets, {traverser, client, parent, isRoot});
this.client = client;
let schema = Schema.normalizeAttributes(properties, facets, {traverser, getClient, parent, isRoot});
this.getClient = getClient;
this.attributes = schema.attributes;
this.enums = schema.enums;
this.translationForTable = schema.translationForTable;
Expand All @@ -1021,7 +1022,7 @@ class Schema {
this.isRoot = !!isRoot;
}

static normalizeAttributes(attributes = {}, facets = {}, {traverser, client, parent, isRoot} = {}) {
static normalizeAttributes(attributes = {}, facets = {}, {traverser, getClient, parent, isRoot} = {}) {
const attributeHasParent = !!parent;
let invalidProperties = [];
let normalized = {};
Expand Down Expand Up @@ -1114,7 +1115,7 @@ class Schema {
let definition = {
name,
field,
client,
getClient,
casing,
prefix,
postfix,
Expand Down
26 changes: 25 additions & 1 deletion test/ts_connected.client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ const v3Client = new V3Client({
endpoint: process.env.LOCAL_DYNAMO_ENDPOINT
});



const clients = [
[c.DocumentClientVersions.v2, v2Client],
[c.DocumentClientVersions.v3, v3Client]
];

function createEntity(client: (typeof v2Client | typeof v3Client)) {
function createEntity(client?: (typeof v2Client | typeof v3Client)) {
return new Entity({
model: {
entity: uuid(),
Expand Down Expand Up @@ -265,6 +267,28 @@ describe('dynamodb sdk client compatibility', () => {
expect(updateRecord.data.prop3).to.be.an('array').with.length(1);
expect(updateRecord.data.prop3).to.deep.equal([prop3[1]]);
});

it('should work with sets even when client is given to Service and not the Entity directly', async () => {
const entity = createEntity();
const service = new Service({entity}, {client});
const prop1 = uuid();
const prop2 = uuid();
const prop3 = ['abc', 'def'];
const item = {
prop1,
prop2,
prop3,
};
await service.entities.entity.create(item).go();
const result1 = await service.entities.entity.get({prop1, prop2}).go();
expect(result1.data).to.deep.equal(item);
await service.entities.entity
.patch({prop1, prop2})
.add({prop3: ['hij']})
.go();
const result2 = await service.entities.entity.get({prop1, prop2}).go();
expect(result2.data?.prop3?.sort()).to.deep.equal([...prop3, 'hij'].sort());
});
});
}
})

0 comments on commit ecda560

Please sign in to comment.