Skip to content

Commit 5b3da02

Browse files
authored
Merge pull request #2972 from pcg-kk/issues/2819/linkName-in-the-link-decorator-doesnt-assign-to-the-value-on-the-correct-property
fix: linkName from decorator assign value to the correct property
2 parents d13d886 + 7ca4d8f commit 5b3da02

File tree

4 files changed

+87
-25
lines changed

4 files changed

+87
-25
lines changed

src/app/core/cache/builders/link.service.spec.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ class TestModel implements HALResource {
3131
self: HALLink;
3232
predecessor: HALLink;
3333
successor: HALLink;
34+
standardLinkName: HALLink;
3435
};
3536

3637
predecessor?: TestModel;
3738
successor?: TestModel;
39+
renamedProperty?: TestModel;
3840
}
3941

4042
const mockDataServiceMap: any = new Map([
@@ -66,6 +68,24 @@ describe('LinkService', () => {
6668
testDataService = new TestDataService();
6769
spyOn(testDataService, 'findListByHref').and.callThrough();
6870
spyOn(testDataService, 'findByHref').and.callThrough();
71+
72+
const linksDefinitions = new Map();
73+
linksDefinitions.set('predecessor', {
74+
resourceType: TEST_MODEL,
75+
linkName: 'predecessor',
76+
propertyName: 'predecessor',
77+
});
78+
linksDefinitions.set('successor', {
79+
resourceType: TEST_MODEL,
80+
linkName: 'successor',
81+
propertyName: 'successor',
82+
});
83+
linksDefinitions.set('standardLinkName', {
84+
resourceType: TEST_MODEL,
85+
linkName: 'standardLinkName',
86+
propertyName: 'renamedProperty',
87+
});
88+
6989
TestBed.configureTestingModule({
7090
providers: [
7191
LinkService,
@@ -87,18 +107,7 @@ describe('LinkService', () => {
87107
},
88108
{
89109
provide: LINK_DEFINITION_MAP_FACTORY,
90-
useValue: jasmine.createSpy('getLinkDefinitions').and.returnValue([
91-
{
92-
resourceType: TEST_MODEL,
93-
linkName: 'predecessor',
94-
propertyName: 'predecessor',
95-
},
96-
{
97-
resourceType: TEST_MODEL,
98-
linkName: 'successor',
99-
propertyName: 'successor',
100-
},
101-
]),
110+
useValue: jasmine.createSpy('getLinkDefinitions').and.returnValue(linksDefinitions),
102111
},
103112
],
104113
});
@@ -117,6 +126,15 @@ describe('LinkService', () => {
117126
});
118127
});
119128
});
129+
describe(`when the propertyName is different than linkName`, () => {
130+
beforeEach(() => {
131+
result = service.resolveLink(testModel, followLink('standardLinkName', {}));
132+
});
133+
it('link should be assign to custom property', () => {
134+
expect(result.renamedProperty).toBeDefined();
135+
expect(result.standardLinkName).toBeUndefined();
136+
});
137+
});
120138
describe(`when the linkdefinition concerns a list`, () => {
121139
beforeEach(() => {
122140
((service as any).getLinkDefinition as jasmine.Spy).and.returnValue({

src/app/core/cache/builders/link.service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,16 @@ export class LinkService {
112112
* @param linkToFollow the {@link FollowLinkConfig} to resolve
113113
*/
114114
public resolveLink<T extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): T {
115-
model[linkToFollow.name] = this.resolveLinkWithoutAttaching(model, linkToFollow);
115+
const linkDefinitions = this.getLinkDefinitions(model.constructor as GenericConstructor<T>);
116+
const linkDef = linkDefinitions.get(linkToFollow.name);
117+
118+
if (isNotEmpty(linkDef)) {
119+
// If link exist in definition we can resolve it and use a real property name
120+
model[linkDef.propertyName] = this.resolveLinkWithoutAttaching(model, linkToFollow);
121+
} else {
122+
// For some links we don't have a definition, so we use the link name as property name
123+
model[linkToFollow.name] = this.resolveLinkWithoutAttaching(model, linkToFollow);
124+
}
116125
return model;
117126
}
118127

src/app/core/data/base/base-data.service.spec.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*
66
* http://www.dspace.org/license/
77
*/
8+
// eslint-disable-next-line max-classes-per-file
89
import {
910
fakeAsync,
1011
tick,
@@ -26,12 +27,20 @@ import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-ser
2627
import { ObjectCacheServiceStub } from '../../../shared/testing/object-cache-service.stub';
2728
import { createPaginatedList } from '../../../shared/testing/utils.test';
2829
import { followLink } from '../../../shared/utils/follow-link-config.model';
30+
import {
31+
link,
32+
typedObject,
33+
} from '../../cache/builders/build-decorators';
2934
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
3035
import { ObjectCacheEntry } from '../../cache/object-cache.reducer';
3136
import { ObjectCacheService } from '../../cache/object-cache.service';
37+
import { BITSTREAM } from '../../shared/bitstream.resource-type';
38+
import { COLLECTION } from '../../shared/collection.resource-type';
3239
import { HALEndpointService } from '../../shared/hal-endpoint.service';
3340
import { HALLink } from '../../shared/hal-link.model';
41+
import { ResourceType } from '../../shared/resource-type';
3442
import { FindListOptions } from '../find-list-options.model';
43+
import { PaginatedList } from '../paginated-list.model';
3544
import { RemoteData } from '../remote-data';
3645
import { RequestService } from '../request.service';
3746
import { RequestEntryState } from '../request-entry-state.model';
@@ -56,6 +65,25 @@ class TestService extends BaseDataService<any> {
5665
}
5766
}
5867

68+
@typedObject
69+
class BaseData {
70+
static type = new ResourceType('test');
71+
72+
foo: string;
73+
74+
_links: {
75+
followLink1: HALLink;
76+
followLink2: HALLink[];
77+
self: HALLink;
78+
};
79+
80+
@link(COLLECTION)
81+
followLink1: Observable<any>;
82+
83+
@link(BITSTREAM, true, 'followLink2')
84+
followLink2CustomVariableName: Observable<PaginatedList<any>>;
85+
}
86+
5987
describe('BaseDataService', () => {
6088
let service: TestService;
6189
let requestService;
@@ -66,8 +94,8 @@ describe('BaseDataService', () => {
6694
let linksToFollow;
6795
let testScheduler;
6896
let remoteDataTimestamp: number;
69-
let remoteDataMocks: { [responseType: string]: RemoteData<any> };
70-
let remoteDataPageMocks: { [responseType: string]: RemoteData<any> };
97+
let remoteDataMocks: { [responseType: string]: RemoteData<BaseData> };
98+
let remoteDataPageMocks: { [responseType: string]: RemoteData<PaginatedList<BaseData>> };
7199

72100
function initTestService(): TestService {
73101
requestService = getMockRequestService();
@@ -90,10 +118,10 @@ describe('BaseDataService', () => {
90118
// as cached values.
91119
remoteDataTimestamp = new Date().getTime() + 60 * 1000;
92120
const msToLive = 15 * 60 * 1000;
93-
const payload = {
121+
const payload: BaseData = Object.assign(new BaseData(), {
94122
foo: 'bar',
95-
followLink1: {},
96-
followLink2: {},
123+
followLink1: observableOf({}),
124+
followLink2CustomVariableName: observableOf(createPaginatedList()),
97125
_links: {
98126
self: Object.assign(new HALLink(), {
99127
href: 'self-test-link',
@@ -110,7 +138,7 @@ describe('BaseDataService', () => {
110138
}),
111139
],
112140
},
113-
};
141+
});
114142
const statusCodeSuccess = 200;
115143
const statusCodeError = 404;
116144
const errorMessage = 'not found';

src/app/core/data/base/base-data.service.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ import {
2828
isNotEmptyOperator,
2929
} from '../../../shared/empty.util';
3030
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
31+
import {
32+
getLinkDefinition,
33+
LinkDefinition,
34+
} from '../../cache/builders/build-decorators';
3135
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
3236
import { CacheableObject } from '../../cache/cacheable-object.model';
3337
import { RequestParam } from '../../cache/models/request-param.model';
3438
import { ObjectCacheEntry } from '../../cache/object-cache.reducer';
3539
import { ObjectCacheService } from '../../cache/object-cache.service';
40+
import { GenericConstructor } from '../../shared/generic-constructor';
3641
import { HALEndpointService } from '../../shared/hal-endpoint.service';
3742
import { HALLink } from '../../shared/hal-link.model';
3843
import { getFirstCompletedRemoteData } from '../../shared/operators';
@@ -301,9 +306,10 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
301306
// Ensure all followLinks from the cached object are automatically invalidated when invalidating the cached object
302307
tap((remoteDataObject: RemoteData<T>) => {
303308
if (hasValue(remoteDataObject?.payload?._links)) {
304-
for (const followLinkName of Object.keys(remoteDataObject.payload._links)) {
305-
// only add the followLinks if they are embedded
306-
if (hasValue(remoteDataObject.payload[followLinkName]) && followLinkName !== 'self') {
309+
for (const followLinkName of Object.keys(remoteDataObject.payload._links) as (keyof typeof remoteDataObject.payload._links)[]) {
310+
// only add the followLinks if they are embedded, and we get only links from the linkMap with the correct name
311+
const linkDefinition: LinkDefinition<T> = getLinkDefinition(remoteDataObject.payload.constructor as GenericConstructor<T>, followLinkName);
312+
if (linkDefinition?.propertyName && hasValue(remoteDataObject.payload[linkDefinition.propertyName]) && followLinkName !== 'self') {
307313
// followLink can be either an individual HALLink or a HALLink[]
308314
const followLinksList: HALLink[] = [].concat(remoteDataObject.payload._links[followLinkName]);
309315
for (const individualFollowLink of followLinksList) {
@@ -357,9 +363,10 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
357363
if (hasValue(remoteDataObject?.payload?.page)) {
358364
for (const object of remoteDataObject.payload.page) {
359365
if (hasValue(object?._links)) {
360-
for (const followLinkName of Object.keys(object._links)) {
361-
// only add the followLinks if they are embedded
362-
if (hasValue(object[followLinkName]) && followLinkName !== 'self') {
366+
for (const followLinkName of Object.keys(object._links) as (keyof typeof object._links)[]) {
367+
// only add the followLinks if they are embedded, and we get only links from the linkMap with the correct name
368+
const linkDefinition: LinkDefinition<PaginatedList<T>> = getLinkDefinition(object.constructor as GenericConstructor<PaginatedList<T>>, followLinkName);
369+
if (linkDefinition?.propertyName && followLinkName !== 'self' && hasValue(object[linkDefinition.propertyName])) {
363370
// followLink can be either an individual HALLink or a HALLink[]
364371
const followLinksList: HALLink[] = [].concat(object._links[followLinkName]);
365372
for (const individualFollowLink of followLinksList) {

0 commit comments

Comments
 (0)