diff --git a/src/utils/EntityTransformer.ts b/src/utils/EntityTransformer.ts index bcaac5d..eac7008 100644 --- a/src/utils/EntityTransformer.ts +++ b/src/utils/EntityTransformer.ts @@ -41,6 +41,10 @@ export class EntityTransformer { props: EsPropsMetaDataInterface[], meta: EsMetaDataInterface, ) { + if (!entity) { + return undefined; + } + if (entity instanceof Array) { return entity.map((entityItem) => { return this.normalizeProps(entityItem, props, meta); diff --git a/test/fixtures/TestingClassOptionalNested.ts b/test/fixtures/TestingClassOptionalNested.ts new file mode 100644 index 0000000..6801689 --- /dev/null +++ b/test/fixtures/TestingClassOptionalNested.ts @@ -0,0 +1,23 @@ +import { EsEntity } from '../../src/decorators/EsEntity'; +import { EsId } from '../../src/decorators/EsId'; +import { EsProperty } from '../../src/decorators/EsProperty'; + +export class MyOptionalNestedEntity { + @EsProperty('keyword') + public name: string; +} + +@EsEntity('elastic_index') +export class MyRootEntityWithOptionalNested { + @EsId() + public id: string; + + @EsProperty('integer') + public foo: number; + + @EsProperty({ type: 'nested', entity: MyOptionalNestedEntity }) + public nestedItem?: MyOptionalNestedEntity; + + @EsProperty({ type: 'nested', entity: MyOptionalNestedEntity }) + public nestedItems?: MyOptionalNestedEntity[]; +} diff --git a/test/integration/Repository.integration.ts b/test/integration/Repository.integration.ts index 324132b..2b9dec4 100644 --- a/test/integration/Repository.integration.ts +++ b/test/integration/Repository.integration.ts @@ -264,10 +264,8 @@ describe('Repository.integration', () => { error = e; } expect(error).toBeInstanceOf(EsException); - expect((error.originalError as ElasticsearchClientError).message).toBe( - 'parse_exception\n' + - '\tRoot causes:\n' + - '\t\tparse_exception: request body is required', + expect((error.originalError as ElasticsearchClientError).message).toContain( + 'request body is required', ); }); diff --git a/test/integration/RepositoryAliases.integration.ts b/test/integration/RepositoryAliases.integration.ts index 46734d2..ea0fc9d 100644 --- a/test/integration/RepositoryAliases.integration.ts +++ b/test/integration/RepositoryAliases.integration.ts @@ -202,10 +202,8 @@ describe('RepositoryAliases', () => { error = e; } expect(error).toBeInstanceOf(EsException); - expect((error.originalError as ResponseError).message).toBe( - 'parse_exception\n' + - '\tRoot causes:\n' + - '\t\tparse_exception: request body is required', + expect((error.originalError as ResponseError).message).toContain( + 'request body is required', ); }); diff --git a/test/unit/src/utils/EntityTransformer.spec.ts b/test/unit/src/utils/EntityTransformer.spec.ts index 96c3767..c78eae9 100644 --- a/test/unit/src/utils/EntityTransformer.spec.ts +++ b/test/unit/src/utils/EntityTransformer.spec.ts @@ -3,6 +3,7 @@ import { TestingClass as TestingClass2 } from '../../../fixtures/TestingClass2'; import { EntityTransformer } from '../../../../src/utils/EntityTransformer'; import { FactoryProvider } from '../../../../src/factory/Factory.provider'; import { CustomIdGeneratorEntity } from '../../../fixtures/CustomIdGeneratorEntity'; +import { MyRootEntityWithOptionalNested } from '../../../fixtures/TestingClassOptionalNested'; describe('entity transformer', () => { let entityTransformer: EntityTransformer; @@ -68,4 +69,40 @@ describe('entity transformer', () => { expect(normalizedEntity.id).toBe('myCustomId'); }); + + it('should work for nested entities - optional fields', () => { + const testingClass1 = new MyRootEntityWithOptionalNested(); + testingClass1.id = '25u46fhno'; + testingClass1.foo = 1; + const normalizedEntity = entityTransformer.normalize(testingClass1); + expect(normalizedEntity.id).toBe('25u46fhno'); + expect(normalizedEntity.data.foo).toBe(1); + expect(normalizedEntity.data.nestedItem).toBeUndefined(); + expect(normalizedEntity.data.nestedItems).toBeUndefined(); + + const denormalizedEntity = entityTransformer.denormalize( + MyRootEntityWithOptionalNested, + normalizedEntity, + ); + + // remove in future, clean the expected object in-case undefined affects properties + for (const [k, v] of Object.entries(testingClass1)) { + if (v === undefined) { + delete testingClass1[k]; + } + } + + expect(denormalizedEntity).toMatchObject(testingClass1); + + // remove in future + for (const [k, v] of Object.entries(normalizedEntity.data)) { + if (v === undefined) { + delete normalizedEntity.data[k]; + } + } + + const normalizedEntityRetried = + entityTransformer.normalize(denormalizedEntity); + expect(normalizedEntityRetried).toMatchObject(normalizedEntity); + }); });