From d8b3d3d517fd8ebb8cf7d55575b5763c806239e9 Mon Sep 17 00:00:00 2001 From: Lars Lauger Date: Mon, 30 Sep 2024 19:20:12 +0200 Subject: [PATCH 1/2] fix: Send recursive relationships during save() --- src/resource.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/resource.ts b/src/resource.ts index 9e81a08..ef846f2 100644 --- a/src/resource.ts +++ b/src/resource.ts @@ -79,8 +79,17 @@ export class Resource implements ICacheable { included_relationships && included_relationships.indexOf(relation_alias) !== -1 ) { - included_ids.push(temporal_id); - included.push(resource.toObject({}).data); + const { + data: data, + included: included_resources + }: { data: IDataResource; included?: Array } = resource.toObject(params); + + included_ids = [ + ...included_ids, + temporal_id, + ...(included_resources || []).map((result: IDataResource) => `${result.type}_${result.id}`) + ]; + included = [...included, data, ...(included_resources || [])]; } } } else { @@ -120,8 +129,17 @@ export class Resource implements ICacheable { included_relationships && included_relationships.indexOf(relation_alias) !== -1 ) { - included_ids.push(temporal_id); - included.push(relationship_data.toObject({}).data); + const { + data: data, + included: included_resources + }: { data: IDataResource; included?: Array } = relationship_data.toObject(params); + + included_ids = [ + ...included_ids, + temporal_id, + ...(included_resources || []).map((result: IDataResource) => `${result.type}_${result.id}`) + ]; + included = [...included, data, ...(included_resources || [])]; } } } From 71cad332e459fc8c996898dbbe80dc011af0e866 Mon Sep 17 00:00:00 2001 From: Lars Lauger Date: Tue, 1 Oct 2024 16:23:39 +0200 Subject: [PATCH 2/2] fix: Resolve recursive relationships during fill() --- src/resource.ts | 15 ++-- src/tests/get-resource.spec.ts | 130 +++++++++++++++++++++++++++------ 2 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/resource.ts b/src/resource.ts index ef846f2..26ee45a 100644 --- a/src/resource.ts +++ b/src/resource.ts @@ -213,12 +213,15 @@ export class Resource implements ICacheable { this.cache_last_update = data_object.data.cache_last_update; } - new ResourceRelationshipsConverter( - Converter.getService, - data_object.data.relationships || {}, - this.relationships, - Converter.buildIncluded(data_object) - ).buildRelationships(); + for (let i = 0; i < 2; i++) { + // do this twice so we already know all resources + new ResourceRelationshipsConverter( + Converter.getService, + data_object.data.relationships || {}, + this.relationships, + Converter.buildIncluded(data_object) + ).buildRelationships(); + } return true; } diff --git a/src/tests/get-resource.spec.ts b/src/tests/get-resource.spec.ts index 25f7536..c8e8b60 100644 --- a/src/tests/get-resource.spec.ts +++ b/src/tests/get-resource.spec.ts @@ -93,8 +93,77 @@ describe('core methods', () => { expect(http_request_spy.calls.mostRecent().args[0].data).toBeNull(); }); - it(`resource should have the correct hasOne and hasMany relationships corresponding to the back end response's included resources, - including nested relationships`, async () => { + it.only(`resource should have the correct hasOne and hasMany relationships corresponding to the back end response's included resources, including nested relationships`, async () => { + const response = { + data: { + type: 'test_resources', + id: '1', + attributes: { name: 'test_name' }, + relationships: { + test_resource: { + data: { id: '2', type: 'test_resources' } + }, + test_resources: { + data: [{ id: '3', type: 'test_resources' }, { id: '4', type: 'test_resources' }] + } + } + }, + included: [ + { + type: 'test_resources', + id: '2', + attributes: { name: 'test_name_2' }, + relationships: { + test_resource: { + data: { id: '4', type: 'test_resources' } + } + } + }, + { + type: 'test_resources', + id: '3', + attributes: { name: 'test_name_3' }, + relationships: { + test_resources: { + data: [ + { id: '4', type: 'test_resources' } + ] + } + } + }, + { + type: 'test_resources', + id: '4', + attributes: { name: 'test_name_4' }, + relationships: { + test_resource: { + data: { id: '5', type: 'test_resources' } + }, + test_resources: { + data: [ + { id: '5', type: 'test_resources' } + ] + } + } + }, + { + type: 'test_resources', + id: '5', + attributes: { name: 'test_name_5' }, + relationships: { + test_resource: { + data: { id: '6', type: 'test_resources' } + } + } + }, + { + type: 'test_resources', + id: '6', + attributes: { name: 'test_name_6' } + } + ] + }; + let test_resource = new TestResource(); test_resource.type = 'test_resources'; test_resource.id = '1'; @@ -128,30 +197,49 @@ describe('core methods', () => { await test_service.clearCache(); Core.me.injectedServices.JsonapiStoreService.clearCache(); mockedAxios.request.mockRestore(); - mockedAxios.request.mockResolvedValue({ data: { data: test_resource, included: included } }); + mockedAxios.request.mockResolvedValue({ data: response }); await test_service - .get('1', { include: ['test_resource.test_resource'] }) + .get('1', { include: ['test_resource.test_resource', 'test_resources.test_resource'] }) .toPromise() .then(resource => { - expect(test_resource.type).toBe('test_resources'); - expect(test_resource.id).toBe('1'); + expect(resource.type).toBe('test_resources'); + expect(resource.id).toBe('1'); expect(resource.attributes.name).toBe('test_name'); - expect(resource.relationships.test_resource instanceof DocumentResource).toBeTruthy(); - expect(resource.relationships.test_resources instanceof DocumentCollection).toBeTruthy(); - expect((resource.relationships.test_resource).data.id).toBe('2'); - expect((resource.relationships.test_resource).data.attributes.name).toBe('test_name_2'); - expect( - (resource.relationships.test_resources).data.find(related_resource => related_resource.id === '3') - ).toBeTruthy(); - expect( - (resource.relationships.test_resources).data.find(related_resource => related_resource.id === '3') - .attributes.name - ).toBe('test_name_3'); - let has_one_relationship = (resource.relationships.test_resource).data; - let has_many_relationship = (resource.relationships.test_resources).data; - expect((has_one_relationship.relationships.test_resource.data).id).toBe('4'); - expect((has_many_relationship[0].relationships.test_resources.data[0]).id).toBe('4'); + const has_one_relationship = resource.relationships.test_resource; + expect(has_one_relationship instanceof DocumentResource).toBeTruthy(); + expect(has_one_relationship.data instanceof TestResource).toBeTruthy(); + expect((has_one_relationship.data as TestResource).id).toBe('2'); + expect((has_one_relationship.data as TestResource).type).toBe('test_resources'); + expect((has_one_relationship.data as TestResource).attributes.name).toBe('test_name_2'); + + const has_many_relationship = resource.relationships.test_resources; + expect(has_many_relationship instanceof DocumentCollection).toBeTruthy(); + expect(has_many_relationship.data[0] instanceof TestResource).toBeTruthy(); + expect((has_many_relationship.data[0] as TestResource).id).toBe('3'); + expect((has_many_relationship.data[0] as TestResource).type).toBe('test_resources'); + expect((has_many_relationship.data[0] as TestResource).attributes.name).toBe('test_name_3'); + + const nested_has_many_relationship = has_many_relationship.data[0].relationships.test_resources; + expect(nested_has_many_relationship instanceof DocumentCollection).toBeTruthy(); + expect(nested_has_many_relationship.data[0] instanceof TestResource).toBeTruthy(); + expect((nested_has_many_relationship.data[0] as TestResource).id).toBe('4'); + expect((nested_has_many_relationship.data[0] as TestResource).type).toBe('test_resources'); + expect((nested_has_many_relationship.data[0] as TestResource).attributes.name).toBe('test_name_4'); + + const recursively_nested_has_many_relationship = nested_has_many_relationship.data[0].relationships.test_resources; + expect(recursively_nested_has_many_relationship instanceof DocumentCollection).toBeTruthy(); + expect(recursively_nested_has_many_relationship.data[0] instanceof TestResource).toBeTruthy(); + expect((recursively_nested_has_many_relationship.data[0] as TestResource).id).toBe('5'); + expect((recursively_nested_has_many_relationship.data[0] as TestResource).type).toBe('test_resources'); + expect((recursively_nested_has_many_relationship.data[0] as TestResource).attributes.name).toBe('test_name_5'); + + const recursively_nested_has_one_relationship = nested_has_many_relationship.data[0].relationships.test_resource; + expect(recursively_nested_has_one_relationship instanceof DocumentResource).toBeTruthy(); + expect(recursively_nested_has_one_relationship.data instanceof TestResource).toBeTruthy(); + expect((recursively_nested_has_one_relationship.data as TestResource).id).toBe('5'); + expect((recursively_nested_has_one_relationship.data as TestResource).type).toBe('test_resources'); + expect((recursively_nested_has_one_relationship.data as TestResource).attributes.name).toBe('test_name_5'); }); });