From d62cb04653e6e76a567df3eb100507654aab546e Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 21:29:47 -0300 Subject: [PATCH 01/27] feat(datamapper): enabling nested entity saving with improved null and undefined handling collectionFieldsWithValues method modified to work with nested entities, filter null and undefined extracted to a method. collectionFields method modified to not remove entities --- src/dataMapper.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index ba6828a..152ad88 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -58,15 +58,31 @@ class DataMapper { collectionFields() { return this.allFields - .filter((i) => !i.isEntity) .map((i) => i.nameDb) } + filterNullAndUndefined(i, instance) { + if (instance[i.name] === null || instance[i.name] === undefined) return null + if (i.isEntity) { + const entity = instance[i.name] + const newObject = Object.keys(entity).reduce((acc, key) => { + if (entity[key] === null || entity[key] === undefined) return acc + + acc[key] = entity[key] + + return acc + }, {}) + + return { [i.nameDb]: newObject } + } + return { [i.nameDb]: instance[i.name] } + } + collectionFieldsWithValue(instance) { let collectionFields = this.allFields - .filter((i) => !i.isEntity) - .map(i => ({ [i.nameDb]: instance[i.name] })) + .map(i => this.filterNullAndUndefined(i, instance)) + .filter(Boolean) .reduce((x, y) => ({ ...x, ...y })) if (instance.id === undefined) { From 5871b7b895a5e96424bcd245eb42f453aa1d2684 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 21:32:43 -0300 Subject: [PATCH 02/27] test(datamapper.test): adds tests to enforce rules regarding nested entities Tests for the modifications to the collectionFieldsWithValue and collectionFields methods, which now do not remove nested entities. --- test/dataMapper.test.js | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index fb66e6a..9563f55 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -98,6 +98,90 @@ describe('Data Mapper', () => { }) }) + describe('Simple Nested Entity', () => { + const givenAnNestedEntity = () => { + const ChildEntity = entity('Child entity', { + field1: field(String) + }) + + return entity('A nested entity', { + idField: field(Number), + field1: field(Boolean), + childEntity: field(ChildEntity), + arrayChildEntity: field([ChildEntity]) + }) + } + + it('should retrieve collection fields an nested entity', () => { + //given + const Entity = givenAnNestedEntity() + const entityInstance = new Entity() + entityInstance.idField = 1 + entityInstance.field1 = true + entityInstance.childEntity = { + field1: 'String' + } + const entityIDs = ['idField'] + const dataMapper = new DataMapper(Entity, entityIDs) + + //when + const toEntity = dataMapper.collectionFields() + + //then + assert.deepStrictEqual(toEntity, ['id_field', 'field1', 'child_entity', 'array_child_entity']) + }) + + it('should retrieve collection fields with values of an nested entity', () => { + //given + const Entity = givenAnNestedEntity() + const entityInstance = new Entity() + entityInstance.idField = 1 + entityInstance.field1 = true + entityInstance.childEntity = { + field1: 'String' + } + entityInstance.arrayChildEntity = [ + { + field1: 'String' + } + ] + const entityIDs = ['idField'] + const dataMapper = new DataMapper(Entity, entityIDs) + + //when + const toEntity = dataMapper.collectionFieldsWithValue(entityInstance) + + //then + assert.deepStrictEqual(toEntity, { + id_field: 1, + field1: true, + child_entity: { + field1: 'String' + }, + array_child_entity: { + 0: { field1: 'String' } + } + }) + }) + + it('should retrieve collection fields with values of an nested entity with child entity as empty object', () => { + //given + const Entity = givenAnNestedEntity() + const entityInstance = new Entity() + entityInstance.idField = 1 + entityInstance.field1 = true + entityInstance.childEntity = {} + const entityIDs = ['idField'] + const dataMapper = new DataMapper(Entity, entityIDs) + + //when + const toEntity = dataMapper.collectionFieldsWithValue(entityInstance) + + //then + assert.deepStrictEqual(toEntity, { id_field: 1, field1: true, child_entity: {} }) + }) + }) + describe('Complex Entity - Multiple Types', () => { const givenAnComplexEntity = () => { From 68dfcf031812f65989f71a8b8aad46f2778abd3b Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 22:13:40 -0300 Subject: [PATCH 03/27] feat(datamapper): enable find of nested entities Adjust buildProxy to not return undefined when isEntity is true --- src/dataMapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 152ad88..c720eb0 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -142,7 +142,7 @@ class DataMapper { Object.defineProperty(proxy, field.name, { enumerable: true, get: function () { - if (field.isEntity) return undefined + if (field.isEntity) return parser(this._payload[nameDb]) return parser(this._payload[nameDb]) } }) From 8d4b6b2f45f417c7d4b37e77f71e9209b7f95d13 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 22:24:08 -0300 Subject: [PATCH 04/27] test(datamapper.test): validate toEntity with nested entities Validates that fields are being converted to entities when they are nested. --- test/dataMapper.test.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index 9563f55..e671a68 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -38,7 +38,6 @@ describe('Data Mapper', () => { assert.deepStrictEqual(toEntity.idField, 1) assert.deepStrictEqual(toEntity.field1, true) assert.deepStrictEqual(toEntity.fieldName, false) - }) it('should convert an entity field to the collection string convetion', () => { @@ -99,10 +98,11 @@ describe('Data Mapper', () => { }) describe('Simple Nested Entity', () => { + const ChildEntity = entity('Child entity', { + field1: field(String) + }) + const givenAnNestedEntity = () => { - const ChildEntity = entity('Child entity', { - field1: field(String) - }) return entity('A nested entity', { idField: field(Number), @@ -112,6 +112,33 @@ describe('Data Mapper', () => { }) } + it('should convert data from collection to nested entity', () => { + //given + const Entity = givenAnNestedEntity() + const entityIDs = ['idField'] + const dataMapper = new DataMapper(Entity, entityIDs) + const childEntity = new ChildEntity() + childEntity.field1 = 'String' + + //when + const toEntity = dataMapper.toEntity({ + id_field: 1, + field1: true, + child_entity: { + field1: 'String' + }, + array_child_entity: [ + { field1: 'String' } + ] + }) + + //then + assert.deepStrictEqual(toEntity.idField, 1) + assert.deepStrictEqual(toEntity.field1, true) + assert.deepStrictEqual(toEntity.childEntity, childEntity) + assert.deepStrictEqual(toEntity.arrayChildEntity, [childEntity]) + }) + it('should retrieve collection fields an nested entity', () => { //given const Entity = givenAnNestedEntity() From 2453cb72df917923f8ca668f1fe40890e8c0c2dc Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 22:26:01 -0300 Subject: [PATCH 05/27] fix(datamapper): improved empty object checking in arrayDataParse An object passed to arrayDataParse can be null or undefined, so checking has been improved using the checker. --- src/dataMapper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index c720eb0..6414ae1 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -1,5 +1,5 @@ const Convention = require('./convention') -const { entity } = require('@herbsjs/herbs') +const { entity, checker } = require('@herbsjs/herbs') const dependency = { convention: Convention } class DataMapper { @@ -101,7 +101,7 @@ class DataMapper { function getDataParser(type, isArray) { function arrayDataParser(value, parser) { - if (value === null) return null + if (checker.isEmpty(value)) return null return value.map((i) => parser(i)) } From e34049e124aee6b660b28574d530cb3bdf29eed4 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 22:31:23 -0300 Subject: [PATCH 06/27] feat(herbs2mongo): enable export of DataMapper class --- src/herbs2mongo.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/herbs2mongo.js b/src/herbs2mongo.js index 2696932..4f84aea 100644 --- a/src/herbs2mongo.js +++ b/src/herbs2mongo.js @@ -1,3 +1,4 @@ const Repository = require('./repository') +const DataMapper = require('./dataMapper') -module.exports = { Repository } \ No newline at end of file +module.exports = { Repository, DataMapper } \ No newline at end of file From e9c006eccb191aea341e9e79fcaf103d129965b4 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 9 Jul 2023 22:54:26 -0300 Subject: [PATCH 07/27] test: adjust array type return rule when value is empty Adjust unit tests referring to entity array when value is empty. --- test/queries/find.js | 2 +- test/queries/findByID.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/queries/find.js b/test/queries/find.js index 759d80d..70ecd7c 100644 --- a/test/queries/find.js +++ b/test/queries/find.js @@ -134,7 +134,7 @@ describe('Query Find', () => { const ret = await itemRepo.find({ filter: { stringTest: ["aString"] } }) //then - assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true , entityTest: undefined, entitiesTest: undefined }) + assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true , entityTest: undefined, entitiesTest: null }) assert.deepStrictEqual(ret[0].isValid(),true ) }) diff --git a/test/queries/findByID.js b/test/queries/findByID.js index 558495d..8cb43df 100644 --- a/test/queries/findByID.js +++ b/test/queries/findByID.js @@ -64,7 +64,7 @@ describe('Query Find by ID', () => { const ret = await itemRepo.findByID(anEntity.id) //then - assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true , entityTest: undefined, entitiesTest: undefined }) + assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true , entityTest: undefined, entitiesTest: null }) assert.deepStrictEqual(ret[0].isValid(),true ) }) From 4f460aed4295659494dc8bcf97106600b63165b4 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Tue, 11 Jul 2023 21:15:47 -0300 Subject: [PATCH 08/27] feat(datamapper): enable find of nested entities Maps the entity's internal fields and returns an object with these fields filled in. --- src/dataMapper.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 6414ae1..782f4c5 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -142,7 +142,21 @@ class DataMapper { Object.defineProperty(proxy, field.name, { enumerable: true, get: function () { - if (field.isEntity) return parser(this._payload[nameDb]) + if (field.isEntity && !field.isArray) { + const entity = this._payload[field.nameDb] + + if (checker.isEmpty(entity)) return undefined + + const object = field.type.schema.fields.reduce((obj, entityField) => { + const fieldNameDb = convention.toCollectionFieldName(entityField.name) + const fieldParser = getDataParser(entityField.type, Array.isArray(entityField.type)) + + obj[entityField.name] = fieldParser(this._payload[field.nameDb][fieldNameDb]) + return obj + }, {}) + + return object + } return parser(this._payload[nameDb]) } }) From 9818b6b94cfd89c55a87d3b9bbaf400a7561ba51 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Tue, 11 Jul 2023 21:51:24 -0300 Subject: [PATCH 09/27] test(findbyid): add test to validate nested entity Adds and Modifies existing tests to validate nested entities. --- testdb/findByID.js | 69 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/testdb/findByID.js b/testdb/findByID.js index 76309cc..fc15e56 100644 --- a/testdb/findByID.js +++ b/testdb/findByID.js @@ -19,9 +19,18 @@ describe('Query Find by ID', () => { await client.createCollection(collection) - await client.collection(collection).insertOne( { _id: new ObjectId("60edc25fc39277307ca9a7ff"), number_test: 100, boolean_test: true, string_test: 'aString' }) - await client.collection(collection).insertOne( { _id: new ObjectId("70edc25fc39277307ca9a700"), number_test: 200, boolean_test: false }) - await client.collection(collection).insertOne( { _id: new ObjectId("80edd25fc39272307ca9a712"), number_test: 300, boolean_test: false }) + await client.collection(collection).insertOne({ _id: new ObjectId("60edc25fc39277307ca9a7ff"), number_test: 100, boolean_test: true, string_test: 'aString' }) + await client.collection(collection).insertOne({ _id: new ObjectId("70edc25fc39277307ca9a700"), number_test: 200, boolean_test: false }) + await client.collection(collection).insertOne({ _id: new ObjectId("80edd25fc39272307ca9a712"), number_test: 300, boolean_test: false }) + await client.collection(collection).insertOne({ + _id: new ObjectId("64acbc1ba6a28fbd4501c25c"), + number_test: 400, + boolean_test: true, + string_test: "aString", + child_entity: { + number_test: 100, boolean_test: true, string_test: 'aString' + } + }) }) after(async () => { @@ -38,16 +47,24 @@ describe('Query Find by ID', () => { } } + const ChildEntity = entity('Child entity', { + numberTest: field(Number), + stringTest: field(String), + booleanTest: field(Boolean), + arrayTest: field([String]) + }) + const givenAnEntity = () => { return entity('A entity', { id: field(String), numberTest: field(Number), stringTest: field(String), - booleanTest: field(Boolean) + booleanTest: field(Boolean), + childEntity: field(ChildEntity) }) } - it('should return entities', async () => { + it('should return entity', async () => { //given const anEntity = givenAnEntity() const ItemRepository = givenAnRepositoryClass({ @@ -65,8 +82,38 @@ describe('Query Find by ID', () => { const ret = await itemRepo.findByID(anEntity.id) //then - assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true }) - assert.deepStrictEqual(ret[0].isValid(),true ) + assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true, childEntity: undefined }) + assert.deepStrictEqual(ret[0].isValid(), true) + }) + + it('should return nested entitiy', async () => { + //given + const anEntity = givenAnEntity() + const ItemRepository = givenAnRepositoryClass({ + entity: anEntity, + collection, + database, + ids: ['id'], + mongodb: await connection + }) + const injection = {} + const itemRepo = new ItemRepository(injection) + + anEntity.id = '64acbc1ba6a28fbd4501c25c' + //when + const ret = await itemRepo.findByID(anEntity.id) + + //then + assert.deepStrictEqual(ret[0].toJSON(), { + id: '64acbc1ba6a28fbd4501c25c', + numberTest: 400, + booleanTest: true, + stringTest: "aString", + childEntity: { + numberTest: 100, booleanTest: true, stringTest: 'aString', arrayTest: null, + } + }) + assert.deepStrictEqual(ret[0].isValid(), true) }) it('should return multiple entities', async () => { @@ -81,17 +128,17 @@ describe('Query Find by ID', () => { }) const injection = {} const itemRepo = new ItemRepository(injection) - + const ids = [ '60edc25fc39277307ca9a7ff', '80edd25fc39272307ca9a712', - ] + ] //when const ret = await itemRepo.findByID(ids) //then - assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true }) - assert.deepStrictEqual(ret[0].isValid(),true ) + assert.deepStrictEqual(ret[0].toJSON(), { id: '60edc25fc39277307ca9a7ff', stringTest: "aString", numberTest: 100, booleanTest: true, childEntity: undefined }) + assert.deepStrictEqual(ret[0].isValid(), true) }) }) From 16d2e54c7e9fb6b870fd4bbe75c76fed1326475a Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 18:08:02 -0300 Subject: [PATCH 10/27] refactor(datamapper): field transformation and filter of null and undefined Extract filter of null and undefined to `isNotNullOrUndefined` Refactor and change name of `filterNullAndUndefined` function Update `collectionFieldsWithValue` to use new function structure --- src/dataMapper.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 782f4c5..188d7d5 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -61,29 +61,33 @@ class DataMapper { .map((i) => i.nameDb) } - filterNullAndUndefined(i, instance) { - if (instance[i.name] === null || instance[i.name] === undefined) return null - if (i.isEntity) { - const entity = instance[i.name] - const newObject = Object.keys(entity).reduce((acc, key) => { - if (entity[key] === null || entity[key] === undefined) return acc + isNotNullOrUndefined(field, instance) { + if (instance[field.name] === null || instance[field.name] === undefined) return false + return true + } + + transformField(field, instance) { + if (field.isEntity) { + const entityToFilter = instance[field.name] + const parsedEntity = Object.keys(entityToFilter).reduce((acc, key) => { + if (entityToFilter[key] === null || entityToFilter[key] === undefined) return acc - acc[key] = entity[key] + acc[key] = entityToFilter[key] return acc }, {}) - return { [i.nameDb]: newObject } + return { [field.nameDb]: parsedEntity } } - return { [i.nameDb]: instance[i.name] } + return { [field.nameDb]: instance[field.name] } } collectionFieldsWithValue(instance) { let collectionFields = this.allFields - .map(i => this.filterNullAndUndefined(i, instance)) - .filter(Boolean) - .reduce((x, y) => ({ ...x, ...y })) + .filter((field) => this.isNotNullOrUndefined(field, instance)) + .map((field) => this.transformField(field, instance)) + .reduce((acc, current) => ({ ...acc, ...current }), {}) if (instance.id === undefined) { delete instance.id From 8a4e05d6f6cc399eb0cedf3944378b420820134b Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 18:32:28 -0300 Subject: [PATCH 11/27] refactor(datamapper): refine parsing for entity processing Segregate entity parsing logic into `parseEntity` function --- src/dataMapper.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 188d7d5..4616967 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -68,18 +68,21 @@ class DataMapper { transformField(field, instance) { if (field.isEntity) { - const entityToFilter = instance[field.name] - const parsedEntity = Object.keys(entityToFilter).reduce((acc, key) => { - if (entityToFilter[key] === null || entityToFilter[key] === undefined) return acc + return { [field.nameDb]: this.parseEntity(instance[field.name]) } + } + return { [field.nameDb]: instance[field.name] } + } - acc[key] = entityToFilter[key] + parseEntity(value) { + const parsedEntity = Object.keys(value).reduce((acc, key) => { + if (value[key] === null || value[key] === undefined) return acc - return acc - }, {}) + acc[key] = value[key] - return { [field.nameDb]: parsedEntity } - } - return { [field.nameDb]: instance[field.name] } + return acc + }, {}) + + return parsedEntity } collectionFieldsWithValue(instance) { From 6190f0bc86ebc3c5293b5c5d8311c2f295726a8a Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 21:54:31 -0300 Subject: [PATCH 12/27] feat(datamapper): adds recursion and childrenm key in field mapping Must contain a children key containing an array of files when the field is entity --- src/dataMapper.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 4616967..896aa04 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -34,14 +34,30 @@ class DataMapper { const fields = Object.keys(schema) .map((field) => { - if (typeof schema[field] === 'function') return { type: Function } + if (typeof schema[field] === 'function') return null + const isArray = Array.isArray(schema[field].type) const type = fieldType(schema[field].type) const isEntity = entity.isEntity(type) const nameDb = convention.toCollectionFieldName(field) + const isID = entityIDs.includes(field) - return { name: field, type, isEntity, nameDb, isArray, isID } + + const object = { name: field, type, isEntity, nameDb, isArray, isID } + + if(isEntity && !isArray) { + const entitySchema = schema[field].type.prototype.meta.schema + object.children = this.buildAllFields(entitySchema, [], convention) + } + + if (isEntity && isArray) { + const entitySchema = schema[field].type[0].prototype.meta.schema + object.children = this.buildAllFields(entitySchema, [], convention) + } + + return object }) + .filter(Boolean) const allFields = fields.filter((f) => f.type !== Function) From 6418b2b10a393e6c04c08e195a5fe85526eb43a4 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 21:56:58 -0300 Subject: [PATCH 13/27] feat(datamapper): adds recursion to work with nested entities Recursion makes it possible to work with entities nested at multiple levels --- src/dataMapper.js | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 896aa04..12f46be 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -84,21 +84,42 @@ class DataMapper { transformField(field, instance) { if (field.isEntity) { - return { [field.nameDb]: this.parseEntity(instance[field.name]) } + return { [field.nameDb]: this.parseEntity(field, instance[field.name]) } } + return { [field.nameDb]: instance[field.name] } } - parseEntity(value) { - const parsedEntity = Object.keys(value).reduce((acc, key) => { - if (value[key] === null || value[key] === undefined) return acc + parseEntity(field, value) { + if (field.isArray && checker.isArray(value)) { + const parsedArray = value.map(item => this.parseEntity(field, item)) + return parsedArray.reduce((acc, curr, index) => { + acc[index] = curr + return acc + }, {}) + } + + if(field.isEntity) { + const parsedEntity = Object.keys(value).reduce((acc, key) => { + if (value[key] === null || value[key] === undefined) return acc + + const childField = field?.children.find((i) => i.name === key) + + if(childField?.isEntity) { + acc[childField.nameDb] = this.parseEntity(childField, value[key]) - acc[key] = value[key] + return acc + } + + acc[childField.nameDb] = value[key] + + return acc + }, {}) - return acc - }, {}) + return parsedEntity + } - return parsedEntity + return { [field.nameDb]: value } } collectionFieldsWithValue(instance) { From 00f598e588805c254056df824b9a56225b35ff83 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 21:57:59 -0300 Subject: [PATCH 14/27] test(datamapper.test): adds tests with multi-level nested entities --- test/dataMapper.test.js | 54 +++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index e671a68..5d93d42 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -98,8 +98,16 @@ describe('Data Mapper', () => { }) describe('Simple Nested Entity', () => { + const GreatGrandChildEntity = entity('Great-Grand Child entity', { + simpleString: field(String) + }) + + const GrandChildEntity = entity('Grand Child entity', { + greatGrandChild: field(GreatGrandChildEntity) + }) + const ChildEntity = entity('Child entity', { - field1: field(String) + grandChild: field(GrandChildEntity) }) const givenAnNestedEntity = () => { @@ -118,17 +126,29 @@ describe('Data Mapper', () => { const entityIDs = ['idField'] const dataMapper = new DataMapper(Entity, entityIDs) const childEntity = new ChildEntity() - childEntity.field1 = 'String' + childEntity.grandChild = new GrandChildEntity() + childEntity.grandChild.greatGrandChild = new GreatGrandChildEntity() + childEntity.grandChild.greatGrandChild.simpleString = 'String' //when const toEntity = dataMapper.toEntity({ id_field: 1, field1: true, child_entity: { - field1: 'String' + grand_child: { + great_grand_child: { + simple_string: 'String' + } + } }, array_child_entity: [ - { field1: 'String' } + { + grand_child: { + great_grand_child: { + simple_string: 'String' + } + } + } ] }) @@ -165,11 +185,19 @@ describe('Data Mapper', () => { entityInstance.idField = 1 entityInstance.field1 = true entityInstance.childEntity = { - field1: 'String' + grandChild: { + greatGrandChild: { + simpleString: 'String' + } + } } entityInstance.arrayChildEntity = [ { - field1: 'String' + grandChild: { + greatGrandChild: { + simpleString: 'String' + } + } } ] const entityIDs = ['idField'] @@ -183,10 +211,20 @@ describe('Data Mapper', () => { id_field: 1, field1: true, child_entity: { - field1: 'String' + grand_child: { + great_grand_child: { + simple_string: 'String' + } + } }, array_child_entity: { - 0: { field1: 'String' } + 0: { + grand_child: { + great_grand_child: { + simple_string: 'String' + } + } + } } }) }) From 636c9abf1d91559c1c90446b8391946c8c47a6c7 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 22:33:27 -0300 Subject: [PATCH 15/27] feat(datamapper): recursive parse of entities from collection to entity Recursive parsing of nested entities allows multi-level nested objects to be parsed from the collection --- src/dataMapper.js | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 12f46be..1284087 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -103,7 +103,7 @@ class DataMapper { const parsedEntity = Object.keys(value).reduce((acc, key) => { if (value[key] === null || value[key] === undefined) return acc - const childField = field?.children.find((i) => i.name === key) + const childField = field.children.find((i) => i.name === key) if(childField?.isEntity) { acc[childField.nameDb] = this.parseEntity(childField, value[key]) @@ -168,6 +168,32 @@ class DataMapper { const convention = this.convention const proxy = {} + function processEntity (field, payload) { + const entityValue = payload[field.nameDb] + + if (checker.isEmpty(entityValue)) return undefined + + const object = field.type.schema.fields.reduce((obj, entityField) => { + const fieldNameDb = convention.toCollectionFieldName(entityField.name) + + const isEntity = entity.isEntity(entityField.type) + + if(isEntity) { + const childField = field?.children.find((i) => i.name === entityField.name) + obj[entityField.name] = processEntity(childField, payload[field.nameDb]) + + return obj + } + + const fieldParser = getDataParser(entityField.type, Array.isArray(entityField.type)) + + obj[entityField.name] = fieldParser(payload[field.nameDb][fieldNameDb]) + return obj + }, {}) + + return object + } + Object.defineProperty(proxy, '_payload', { enumerable: false, wricollection: true, @@ -187,19 +213,7 @@ class DataMapper { enumerable: true, get: function () { if (field.isEntity && !field.isArray) { - const entity = this._payload[field.nameDb] - - if (checker.isEmpty(entity)) return undefined - - const object = field.type.schema.fields.reduce((obj, entityField) => { - const fieldNameDb = convention.toCollectionFieldName(entityField.name) - const fieldParser = getDataParser(entityField.type, Array.isArray(entityField.type)) - - obj[entityField.name] = fieldParser(this._payload[field.nameDb][fieldNameDb]) - return obj - }, {}) - - return object + return processEntity(field, this._payload) } return parser(this._payload[nameDb]) } From b56dd30b468bc9c8fc378129ca086745a8389106 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sat, 19 Aug 2023 23:37:59 -0300 Subject: [PATCH 16/27] feat(datamapper): add recursive entity array parser --- src/dataMapper.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 1284087..b6bc5c4 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -143,7 +143,7 @@ class DataMapper { buildProxy() { - function getDataParser(type, isArray) { + function getDataParser(type, isArray, isArrayOfEntities, field) { function arrayDataParser(value, parser) { if (checker.isEmpty(value)) return null return value.map((i) => parser(i)) @@ -154,11 +154,21 @@ class DataMapper { return parser(value) } - if (isArray) { + if (isArray && !isArrayOfEntities) { const parser = getDataParser(type, false) return (value) => arrayDataParser(value, parser) } + if(isArrayOfEntities) { + return (value) => { + if (checker.isEmpty(value)) return null + return value?.map((i) => { return { + [field.children[0].name]: processEntity(field.children[0], i) + } + }) + } + } + if ((type === Date) || (!convention.isScalarType(type))) return (x) => x @@ -215,6 +225,12 @@ class DataMapper { if (field.isEntity && !field.isArray) { return processEntity(field, this._payload) } + + if(field.isEntity && field.isArray) { + const arrayOfEntityParser = getDataParser(field.type, field.isArray, field.isEntity, field) + return arrayOfEntityParser(this._payload[nameDb]) + } + return parser(this._payload[nameDb]) } }) From 9830cdcd63de73dd4986f1261a4ce7b02c7d4f26 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 00:38:30 -0300 Subject: [PATCH 17/27] feat: adjust nested entity array compatibility Must be able to return an array of nested entities with multiple fields --- src/dataMapper.js | 27 ++++++++++++++++++++++++--- test/dataMapper.test.js | 22 +++++++++++++++------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index b6bc5c4..acf2c9a 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -162,9 +162,22 @@ class DataMapper { if(isArrayOfEntities) { return (value) => { if (checker.isEmpty(value)) return null - return value?.map((i) => { return { - [field.children[0].name]: processEntity(field.children[0], i) - } + return value?.map((item) => { + const object = Object.keys(item).reduce((obj, key) => { + const childField = field?.children.find((i) => i.nameDb === key) + + if(childField.isEntity) { + obj[childField.name] = processEntity(childField, item) + + return obj + } + + const parser = getDataParser(field.type, false) + obj[childField.name] = parser(item[childField.nameDb]) + + return obj + }, {}) + return object }) } } @@ -190,6 +203,14 @@ class DataMapper { if(isEntity) { const childField = field?.children.find((i) => i.name === entityField.name) + + if(childField.isArray) { + const arrayOfEntityParser = getDataParser(childField.type, childField.isArray, childField.isEntity, childField) + obj[entityField.name] = arrayOfEntityParser(payload[field.nameDb][fieldNameDb]) + + return obj + } + obj[entityField.name] = processEntity(childField, payload[field.nameDb]) return obj diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index 5d93d42..cf72a3c 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -107,7 +107,8 @@ describe('Data Mapper', () => { }) const ChildEntity = entity('Child entity', { - grandChild: field(GrandChildEntity) + grandChild: field(GrandChildEntity), + simpleString: field(String) }) const givenAnNestedEntity = () => { @@ -127,6 +128,7 @@ describe('Data Mapper', () => { const dataMapper = new DataMapper(Entity, entityIDs) const childEntity = new ChildEntity() childEntity.grandChild = new GrandChildEntity() + childEntity.simpleString = 'String' childEntity.grandChild.greatGrandChild = new GreatGrandChildEntity() childEntity.grandChild.greatGrandChild.simpleString = 'String' @@ -139,7 +141,8 @@ describe('Data Mapper', () => { great_grand_child: { simple_string: 'String' } - } + }, + simple_string: 'String' }, array_child_entity: [ { @@ -147,7 +150,8 @@ describe('Data Mapper', () => { great_grand_child: { simple_string: 'String' } - } + }, + simple_string: 'String' } ] }) @@ -189,7 +193,8 @@ describe('Data Mapper', () => { greatGrandChild: { simpleString: 'String' } - } + }, + simpleString: 'String' } entityInstance.arrayChildEntity = [ { @@ -197,7 +202,8 @@ describe('Data Mapper', () => { greatGrandChild: { simpleString: 'String' } - } + }, + simpleString: 'String' } ] const entityIDs = ['idField'] @@ -215,7 +221,8 @@ describe('Data Mapper', () => { great_grand_child: { simple_string: 'String' } - } + }, + simple_string: 'String' }, array_child_entity: { 0: { @@ -223,7 +230,8 @@ describe('Data Mapper', () => { great_grand_child: { simple_string: 'String' } - } + }, + simple_string: 'String' } } }) From 2acfe310d6a22708e25ce257db9c981b2530e150 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 00:40:36 -0300 Subject: [PATCH 18/27] test(findbyid): coverage of more nested entity scenarios --- testdb/findByID.js | 87 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/testdb/findByID.js b/testdb/findByID.js index fc15e56..f7c9972 100644 --- a/testdb/findByID.js +++ b/testdb/findByID.js @@ -8,34 +8,48 @@ let client = {} describe('Query Find by ID', () => { - const collection = 'test_repository' - const database = 'herbs2mongo_testdb' + const collection = 'test_repository' + const database = 'herbs2mongo_testdb' before(async () => { - client = await connection() - - await client.dropDatabase() - - await client.createCollection(collection) - - await client.collection(collection).insertOne({ _id: new ObjectId("60edc25fc39277307ca9a7ff"), number_test: 100, boolean_test: true, string_test: 'aString' }) - await client.collection(collection).insertOne({ _id: new ObjectId("70edc25fc39277307ca9a700"), number_test: 200, boolean_test: false }) - await client.collection(collection).insertOne({ _id: new ObjectId("80edd25fc39272307ca9a712"), number_test: 300, boolean_test: false }) - await client.collection(collection).insertOne({ - _id: new ObjectId("64acbc1ba6a28fbd4501c25c"), - number_test: 400, - boolean_test: true, - string_test: "aString", - child_entity: { - number_test: 100, boolean_test: true, string_test: 'aString' - } - }) + client = await connection() + + await client.dropDatabase() + + await client.createCollection(collection) + + await client.collection(collection).insertOne({ _id: new ObjectId("60edc25fc39277307ca9a7ff"), number_test: 100, boolean_test: true, string_test: 'aString' }) + await client.collection(collection).insertOne({ _id: new ObjectId("70edc25fc39277307ca9a700"), number_test: 200, boolean_test: false }) + await client.collection(collection).insertOne({ _id: new ObjectId("80edd25fc39272307ca9a712"), number_test: 300, boolean_test: false }) + await client.collection(collection).insertOne({ + _id: new ObjectId("64acbc1ba6a28fbd4501c25c"), + number_test: 400, + boolean_test: true, + string_test: "aString", + child_entity: { + number_test: 100, + boolean_test: true, + string_test: 'aString', + grand_child_test: { + number_test: 100, + boolean_test: true, + string_test: 'aString', + array_entities_test: [ + { + number_test: 100, + boolean_test: true, + string_test: 'aString', + } + ] + } + } + }) }) after(async () => { - await client.dropDatabase() + await client.dropDatabase() }) @@ -47,11 +61,26 @@ describe('Query Find by ID', () => { } } + const GreatGrandChildEntity = entity('Great-Grand child entity', { + numberTest: field(Number), + stringTest: field(String), + booleanTest: field(Boolean), + }) + + const GrandChildEntity = entity('Grand child entity', { + numberTest: field(Number), + stringTest: field(String), + booleanTest: field(Boolean), + arrayTest: field([String]), + arrayEntitiesTest: field([GreatGrandChildEntity]) + }) + const ChildEntity = entity('Child entity', { numberTest: field(Number), stringTest: field(String), booleanTest: field(Boolean), - arrayTest: field([String]) + arrayTest: field([String]), + grandChildTest: field(GrandChildEntity) }) const givenAnEntity = () => { @@ -110,7 +139,19 @@ describe('Query Find by ID', () => { booleanTest: true, stringTest: "aString", childEntity: { - numberTest: 100, booleanTest: true, stringTest: 'aString', arrayTest: null, + numberTest: 100, booleanTest: true, stringTest: 'aString', arrayTest: null, grandChildTest: { + numberTest: 100, + booleanTest: true, + stringTest: 'aString', + arrayTest: null, + arrayEntitiesTest: [ + { + numberTest: 100, + booleanTest: true, + stringTest: 'aString' + } + ] + } } }) assert.deepStrictEqual(ret[0].isValid(), true) From 135587d6b7df9b1db2dac4cc94d0dd2184753d70 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 00:43:08 -0300 Subject: [PATCH 19/27] refactor(datamapper): refactor ifs to use ternary if --- src/dataMapper.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index acf2c9a..522029c 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -44,14 +44,11 @@ class DataMapper { const isID = entityIDs.includes(field) const object = { name: field, type, isEntity, nameDb, isArray, isID } - - if(isEntity && !isArray) { - const entitySchema = schema[field].type.prototype.meta.schema - object.children = this.buildAllFields(entitySchema, [], convention) - } - if (isEntity && isArray) { - const entitySchema = schema[field].type[0].prototype.meta.schema + if(isEntity) { + const entitySchema = isArray + ? schema[field].type[0].prototype.meta.schema + : schema[field].type.prototype.meta.schema object.children = this.buildAllFields(entitySchema, [], convention) } From b87c6b1af87b38c9c1f785087fc9aa38e0475b91 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:18:04 -0300 Subject: [PATCH 20/27] test(datamapper.test): add more test cases --- test/dataMapper.test.js | 51 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index cf72a3c..d80a931 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -98,8 +98,16 @@ describe('Data Mapper', () => { }) describe('Simple Nested Entity', () => { + const GreatGreatGrandChildEntity = entity('Great-Grand Child entity', { + simpleString: field(String), + simpleBoolean: field(Boolean) + }) + const GreatGrandChildEntity = entity('Great-Grand Child entity', { - simpleString: field(String) + simpleString: field(String), + simpleNumber: field(Number), + simpleStringArray: field([String]), + greatGreatGrandChild: field([GreatGreatGrandChildEntity]) }) const GrandChildEntity = entity('Grand Child entity', { @@ -131,6 +139,11 @@ describe('Data Mapper', () => { childEntity.simpleString = 'String' childEntity.grandChild.greatGrandChild = new GreatGrandChildEntity() childEntity.grandChild.greatGrandChild.simpleString = 'String' + childEntity.grandChild.greatGrandChild.simpleStringArray = ['String'] + childEntity.grandChild.greatGrandChild.simpleNumber = 1 + childEntity.grandChild.greatGrandChild.greatGreatGrandChild = [GreatGreatGrandChildEntity.fromJSON({ + simpleString: 'String' + })] //when const toEntity = dataMapper.toEntity({ @@ -139,7 +152,16 @@ describe('Data Mapper', () => { child_entity: { grand_child: { great_grand_child: { - simple_string: 'String' + simple_string: 'String', + simple_string_array: [ + 'String' + ], + simple_number: 1, + great_great_grand_child: [ + { + simple_string: 'String' + } + ] } }, simple_string: 'String' @@ -148,7 +170,16 @@ describe('Data Mapper', () => { { grand_child: { great_grand_child: { - simple_string: 'String' + simple_string: 'String', + simple_string_array: [ + 'String' + ], + simple_number: 1, + great_great_grand_child: [ + { + simple_string: 'String' + } + ] } }, simple_string: 'String' @@ -191,7 +222,12 @@ describe('Data Mapper', () => { entityInstance.childEntity = { grandChild: { greatGrandChild: { - simpleString: 'String' + simpleString: 'String', + greatGreatGrandChild: [ + { + simpleString: 'String' + } + ] } }, simpleString: 'String' @@ -219,7 +255,12 @@ describe('Data Mapper', () => { child_entity: { grand_child: { great_grand_child: { - simple_string: 'String' + simple_string: 'String', + great_great_grand_child: { + '0': { + simple_string: 'String' + } + } } }, simple_string: 'String' From f94144e78c6604fb4b90ff3039daee41aaf23044 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:18:44 -0300 Subject: [PATCH 21/27] refactor(datamapper): remove unused code --- src/dataMapper.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 522029c..951be64 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -115,8 +115,6 @@ class DataMapper { return parsedEntity } - - return { [field.nameDb]: value } } collectionFieldsWithValue(instance) { From 66b91affd799ab6c2af173ac48f5e7fc74386e52 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:31:15 -0300 Subject: [PATCH 22/27] test(datamapper.test): add null nested entity test case --- test/dataMapper.test.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index d80a931..fe0a13e 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -110,14 +110,20 @@ describe('Data Mapper', () => { greatGreatGrandChild: field([GreatGreatGrandChildEntity]) }) + const CousinEntity = entity('Cousin entity', { + greatGrandChildEntity: field(GreatGrandChildEntity) + }) + const GrandChildEntity = entity('Grand Child entity', { - greatGrandChild: field(GreatGrandChildEntity) + greatGrandChild: field(GreatGrandChildEntity), + cousin: field(CousinEntity) }) const ChildEntity = entity('Child entity', { grandChild: field(GrandChildEntity), simpleString: field(String) }) + const givenAnNestedEntity = () => { @@ -161,8 +167,9 @@ describe('Data Mapper', () => { { simple_string: 'String' } - ] - } + ], + cousin: null + }, }, simple_string: 'String' }, @@ -227,7 +234,8 @@ describe('Data Mapper', () => { { simpleString: 'String' } - ] + ], + cousin: null } }, simpleString: 'String' From ac909240ec0df512feb1490088f32478a2ecb8fc Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:38:41 -0300 Subject: [PATCH 23/27] refactor(datamapper): remove if useless --- src/dataMapper.js | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 951be64..53b6bd2 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -94,27 +94,25 @@ class DataMapper { acc[index] = curr return acc }, {}) - } - - if(field.isEntity) { - const parsedEntity = Object.keys(value).reduce((acc, key) => { - if (value[key] === null || value[key] === undefined) return acc - - const childField = field.children.find((i) => i.name === key) - - if(childField?.isEntity) { - acc[childField.nameDb] = this.parseEntity(childField, value[key]) - - return acc - } - - acc[childField.nameDb] = value[key] - + } + + const parsedEntity = Object.keys(value).reduce((acc, key) => { + if (value[key] === null || value[key] === undefined) return acc + + const childField = field.children.find((i) => i.name === key) + + if (childField?.isEntity) { + acc[childField.nameDb] = this.parseEntity(childField, value[key]) + return acc - }, {}) + } - return parsedEntity - } + acc[childField.nameDb] = value[key] + + return acc + }, {}) + + return parsedEntity } collectionFieldsWithValue(instance) { From 75d91d0bd53b8e52af11c5255d5d2600702805ba Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:39:14 -0300 Subject: [PATCH 24/27] style(datamapper): code formatting and styling --- src/dataMapper.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index 53b6bd2..dc4e087 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -45,13 +45,13 @@ class DataMapper { const object = { name: field, type, isEntity, nameDb, isArray, isID } - if(isEntity) { + if (isEntity) { const entitySchema = isArray ? schema[field].type[0].prototype.meta.schema : schema[field].type.prototype.meta.schema object.children = this.buildAllFields(entitySchema, [], convention) } - + return object }) .filter(Boolean) @@ -152,14 +152,14 @@ class DataMapper { return (value) => arrayDataParser(value, parser) } - if(isArrayOfEntities) { + if (isArrayOfEntities) { return (value) => { if (checker.isEmpty(value)) return null return value?.map((item) => { const object = Object.keys(item).reduce((obj, key) => { const childField = field?.children.find((i) => i.nameDb === key) - if(childField.isEntity) { + if (childField.isEntity) { obj[childField.name] = processEntity(childField, item) return obj @@ -184,7 +184,7 @@ class DataMapper { const convention = this.convention const proxy = {} - function processEntity (field, payload) { + function processEntity(field, payload) { const entityValue = payload[field.nameDb] if (checker.isEmpty(entityValue)) return undefined @@ -194,10 +194,10 @@ class DataMapper { const isEntity = entity.isEntity(entityField.type) - if(isEntity) { + if (isEntity) { const childField = field?.children.find((i) => i.name === entityField.name) - if(childField.isArray) { + if (childField.isArray) { const arrayOfEntityParser = getDataParser(childField.type, childField.isArray, childField.isEntity, childField) obj[entityField.name] = arrayOfEntityParser(payload[field.nameDb][fieldNameDb]) @@ -240,7 +240,7 @@ class DataMapper { return processEntity(field, this._payload) } - if(field.isEntity && field.isArray) { + if (field.isEntity && field.isArray) { const arrayOfEntityParser = getDataParser(field.type, field.isArray, field.isEntity, field) return arrayOfEntityParser(this._payload[nameDb]) } From 09a3740193a71268a5ae7440c4ab857a39b55b55 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:40:13 -0300 Subject: [PATCH 25/27] test(datamapper.test): use new to create GreatGreatGrandChildEntity --- test/dataMapper.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index fe0a13e..1c01821 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -147,9 +147,9 @@ describe('Data Mapper', () => { childEntity.grandChild.greatGrandChild.simpleString = 'String' childEntity.grandChild.greatGrandChild.simpleStringArray = ['String'] childEntity.grandChild.greatGrandChild.simpleNumber = 1 - childEntity.grandChild.greatGrandChild.greatGreatGrandChild = [GreatGreatGrandChildEntity.fromJSON({ - simpleString: 'String' - })] + const greatGreatGrandChild = new GreatGreatGrandChildEntity() + greatGreatGrandChild.simpleString = 'String' + childEntity.grandChild.greatGrandChild.greatGreatGrandChild = [greatGreatGrandChild] //when const toEntity = dataMapper.toEntity({ From 3896da7717663e39d21fb9c3e76668e0304c28ed Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 01:44:27 -0300 Subject: [PATCH 26/27] test(datamapper): fix unit test --- test/dataMapper.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index 1c01821..8318920 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -148,7 +148,7 @@ describe('Data Mapper', () => { childEntity.grandChild.greatGrandChild.simpleStringArray = ['String'] childEntity.grandChild.greatGrandChild.simpleNumber = 1 const greatGreatGrandChild = new GreatGreatGrandChildEntity() - greatGreatGrandChild.simpleString = 'String' + greatGreatGrandChild.simpleString = 'greatGreatGrandChildEntity' childEntity.grandChild.greatGrandChild.greatGreatGrandChild = [greatGreatGrandChild] //when @@ -165,7 +165,7 @@ describe('Data Mapper', () => { simple_number: 1, great_great_grand_child: [ { - simple_string: 'String' + simple_string: 'greatGreatGrandChildEntity' } ], cousin: null @@ -184,7 +184,7 @@ describe('Data Mapper', () => { simple_number: 1, great_great_grand_child: [ { - simple_string: 'String' + simple_string: 'greatGreatGrandChildEntity' } ] } From 1122dd1623d94d53a1607b76ef914d36cb783ff2 Mon Sep 17 00:00:00 2001 From: Vitor Araujo Date: Sun, 20 Aug 2023 13:22:31 -0300 Subject: [PATCH 27/27] feat: adjust entity checking when entityField.type is an array When entity.type is an array, it must check if the item inside the array is an entity (extends BaseEntity) --- src/dataMapper.js | 6 +++++- test/dataMapper.test.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dataMapper.js b/src/dataMapper.js index dc4e087..40ffd28 100644 --- a/src/dataMapper.js +++ b/src/dataMapper.js @@ -192,7 +192,11 @@ class DataMapper { const object = field.type.schema.fields.reduce((obj, entityField) => { const fieldNameDb = convention.toCollectionFieldName(entityField.name) - const isEntity = entity.isEntity(entityField.type) + const typeIsArray = checker.isArray(entityField.type) + + const isEntity = typeIsArray + ? entity.isEntity(entityField.type[0]) + : entity.isEntity(entityField.type) if (isEntity) { const childField = field?.children.find((i) => i.name === entityField.name) diff --git a/test/dataMapper.test.js b/test/dataMapper.test.js index 8318920..fa0224f 100644 --- a/test/dataMapper.test.js +++ b/test/dataMapper.test.js @@ -98,7 +98,7 @@ describe('Data Mapper', () => { }) describe('Simple Nested Entity', () => { - const GreatGreatGrandChildEntity = entity('Great-Grand Child entity', { + const GreatGreatGrandChildEntity = entity('Great Great-Grand Child entity', { simpleString: field(String), simpleBoolean: field(Boolean) })