diff --git a/src/app/+bitstream-page/bitstream-page.resolver.ts b/src/app/+bitstream-page/bitstream-page.resolver.ts index a876b22d5e..fd9d5b351b 100644 --- a/src/app/+bitstream-page/bitstream-page.resolver.ts +++ b/src/app/+bitstream-page/bitstream-page.resolver.ts @@ -35,7 +35,7 @@ export class BitstreamPageResolver implements Resolve> { */ get followLinks(): FollowLinkConfig[] { return [ - followLink('bundle', undefined, true, true, true, followLink('item')), + followLink('bundle', {}, followLink('item')), followLink('format') ]; } diff --git a/src/app/+collection-page/collection-page.resolver.ts b/src/app/+collection-page/collection-page.resolver.ts index f6f87f117c..d476a180d3 100644 --- a/src/app/+collection-page/collection-page.resolver.ts +++ b/src/app/+collection-page/collection-page.resolver.ts @@ -14,7 +14,7 @@ import { ResolvedAction } from '../core/resolving/resolver.actions'; * Requesting them as embeds will limit the number of requests */ export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ - followLink('parentCommunity', undefined, true, true, true, + followLink('parentCommunity', {}, followLink('parentCommunity') ), followLink('logo') diff --git a/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index fb193b24d4..e4899abe3a 100644 --- a/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -79,7 +79,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { getFirstSucceededRemoteDataWithNotEmptyPayload(), map((item: Item) => this.linkService.resolveLink( item, - followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams')) + followLink('bundles', {}, followLink('bitstreams')) )) ) as Observable; diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index 3f9637cdc9..3f1d046a87 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -323,7 +323,7 @@ export class EditRelationshipListComponent implements OnInit { private getItemRelationships() { this.linkService.resolveLink(this.item, - followLink('relationships', undefined, true, true, true, + followLink('relationships', {}, followLink('relationshipType'), followLink('leftItem'), followLink('rightItem'), diff --git a/src/app/+item-page/item.resolver.ts b/src/app/+item-page/item.resolver.ts index 99b96511fe..217529d271 100644 --- a/src/app/+item-page/item.resolver.ts +++ b/src/app/+item-page/item.resolver.ts @@ -15,13 +15,13 @@ import { ResolvedAction } from '../core/resolving/resolver.actions'; * Requesting them as embeds will limit the number of requests */ export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ - followLink('owningCollection', undefined, true, true, true, - followLink('parentCommunity', undefined, true, true, true, + followLink('owningCollection', {}, + followLink('parentCommunity', {}, followLink('parentCommunity')) ), - followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams')), + followLink('bundles', {}, followLink('bitstreams')), followLink('relationships'), - followLink('version', undefined, true, true, true, followLink('versionhistory')), + followLink('version', {}, followLink('versionhistory')), ]; /** diff --git a/src/app/+search-page/search.component.ts b/src/app/+search-page/search.component.ts index b817c82a57..f6e9664911 100644 --- a/src/app/+search-page/search.component.ts +++ b/src/app/+search-page/search.component.ts @@ -16,9 +16,11 @@ import { SearchResult } from '../shared/search/search-result.model'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { SearchService } from '../core/shared/search/search.service'; import { currentPath } from '../shared/utils/route.utils'; -import { Router} from '@angular/router'; +import { Router } from '@angular/router'; import { Context } from '../core/shared/context.model'; import { SortOptions } from '../core/cache/models/sort-options.model'; +import { followLink } from '../shared/utils/follow-link-config.model'; +import { Item } from '../core/shared/item.model'; @Component({ selector: 'ds-search', diff --git a/src/app/community-list-page/community-list-service.ts b/src/app/community-list-page/community-list-service.ts index cbf70ca39a..e882ae5902 100644 --- a/src/app/community-list-page/community-list-service.ts +++ b/src/app/community-list-page/community-list-service.ts @@ -174,8 +174,8 @@ export class CommunityListService { direction: options.sort.direction } }, - followLink('subcommunities', this.configOnePage, true, true), - followLink('collections', this.configOnePage, true, true)) + followLink('subcommunities', { findListOptions: this.configOnePage }), + followLink('collections', { findListOptions: this.configOnePage })) .pipe( getFirstSucceededRemoteData(), map((results) => results.payload), @@ -242,8 +242,8 @@ export class CommunityListService { elementsPerPage: MAX_COMCOLS_PER_PAGE, currentPage: i }, - followLink('subcommunities', this.configOnePage, true, true), - followLink('collections', this.configOnePage, true, true)) + followLink('subcommunities', { findListOptions: this.configOnePage }), + followLink('collections', { findListOptions: this.configOnePage })) .pipe( getFirstCompletedRemoteData(), switchMap((rd: RemoteData>) => { diff --git a/src/app/core/cache/builders/link.service.spec.ts b/src/app/core/cache/builders/link.service.spec.ts index a6d9c59492..f567c39314 100644 --- a/src/app/core/cache/builders/link.service.spec.ts +++ b/src/app/core/cache/builders/link.service.spec.ts @@ -102,7 +102,7 @@ describe('LinkService', () => { describe('resolveLink', () => { describe(`when the linkdefinition concerns a single object`, () => { beforeEach(() => { - service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); + service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor'))); }); it('should call dataservice.findByHref with the correct href and nested links', () => { expect(testDataService.findByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, true, true, followLink('successor')); @@ -116,7 +116,7 @@ describe('LinkService', () => { propertyName: 'predecessor', isList: true }); - service.resolveLink(testModel, followLink('predecessor', { some: 'options ' } as any, true, true, true, followLink('successor'))); + service.resolveLink(testModel, followLink('predecessor', { findListOptions: { some: 'options ' } as any }, followLink('successor'))); }); it('should call dataservice.findAllByHref with the correct href, findListOptions, and nested links', () => { expect(testDataService.findAllByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, { some: 'options ' } as any, true, true, followLink('successor')); @@ -124,7 +124,7 @@ describe('LinkService', () => { }); describe('either way', () => { beforeEach(() => { - result = service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); + result = service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor'))); }); it('should call getLinkDefinition with the correct model and link', () => { @@ -149,7 +149,7 @@ describe('LinkService', () => { }); it('should throw an error', () => { expect(() => { - service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); + service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor'))); }).toThrow(); }); }); @@ -160,7 +160,7 @@ describe('LinkService', () => { }); it('should throw an error', () => { expect(() => { - service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); + service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor'))); }).toThrow(); }); }); diff --git a/src/app/core/cache/builders/link.service.ts b/src/app/core/cache/builders/link.service.ts index 29f8633da5..66f91dbbd6 100644 --- a/src/app/core/cache/builders/link.service.ts +++ b/src/app/core/cache/builders/link.service.ts @@ -39,7 +39,7 @@ export class LinkService { */ public resolveLinks(model: T, ...linksToFollow: FollowLinkConfig[]): T { linksToFollow.forEach((linkToFollow: FollowLinkConfig) => { - this.resolveLink(model, linkToFollow); + this.resolveLink(model, linkToFollow); }); return model; } @@ -55,9 +55,7 @@ export class LinkService { public resolveLinkWithoutAttaching(model, linkToFollow: FollowLinkConfig): Observable> { const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name); - if (hasNoValue(matchingLinkDef)) { - throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); - } else { + if (hasValue(matchingLinkDef)) { const provider = this.getDataServiceFor(matchingLinkDef.resourceType); if (hasNoValue(provider)) { @@ -84,7 +82,10 @@ export class LinkService { throw e; } } + } else if (!linkToFollow.isOptional) { + throw new Error(`followLink('${linkToFollow.name}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); } + return EMPTY; } diff --git a/src/app/core/cache/builders/remote-data-build.service.spec.ts b/src/app/core/cache/builders/remote-data-build.service.spec.ts index cb193724a7..0cb45733a6 100644 --- a/src/app/core/cache/builders/remote-data-build.service.spec.ts +++ b/src/app/core/cache/builders/remote-data-build.service.spec.ts @@ -523,7 +523,7 @@ describe('RemoteDataBuildService', () => { let paginatedLinksToFollow; beforeEach(() => { paginatedLinksToFollow = [ - followLink('page', undefined, true, true, true, ...linksToFollow), + followLink('page', {}, ...linksToFollow), ...linksToFollow ]; }); diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 11815c133b..6b67549f2d 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -271,7 +271,7 @@ export class RemoteDataBuildService { * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ buildList(href$: string | Observable, ...linksToFollow: FollowLinkConfig[]): Observable>> { - return this.buildFromHref>(href$, followLink('page', undefined, false, true, true, ...linksToFollow)); + return this.buildFromHref>(href$, followLink('page', { shouldEmbed: false }, ...linksToFollow)); } /** diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index 88b15754af..5bc7423824 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -233,7 +233,7 @@ describe('DataService', () => { const config: FindListOptions = Object.assign(new FindListOptions(), { elementsPerPage: 5 }); - (service as any).getFindAllHref({}, null, followLink('bundles', config, true, true, true)).subscribe((value) => { + (service as any).getFindAllHref({}, null, followLink('bundles', { findListOptions: config })).subscribe((value) => { expect(value).toBe(expected); }); }); @@ -253,7 +253,7 @@ describe('DataService', () => { elementsPerPage: 2 }); - (service as any).getFindAllHref({}, null, followLink('bundles'), followLink('owningCollection', config, true, true, true), followLink('templateItemOf')).subscribe((value) => { + (service as any).getFindAllHref({}, null, followLink('bundles'), followLink('owningCollection', { findListOptions: config }), followLink('templateItemOf')).subscribe((value) => { expect(value).toBe(expected); }); }); @@ -261,7 +261,13 @@ describe('DataService', () => { it('should not include linksToFollow with shouldEmbed = false', () => { const expected = `${endpoint}?embed=templateItemOf`; - (service as any).getFindAllHref({}, null, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf')).subscribe((value) => { + (service as any).getFindAllHref( + {}, + null, + followLink('bundles', { shouldEmbed: false }), + followLink('owningCollection', { shouldEmbed: false }), + followLink('templateItemOf') + ).subscribe((value) => { expect(value).toBe(expected); }); }); @@ -269,7 +275,7 @@ describe('DataService', () => { it('should include nested linksToFollow 3lvl', () => { const expected = `${endpoint}?embed=owningCollection/itemtemplate/relationships`; - (service as any).getFindAllHref({}, null, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', undefined, true, true, true, followLink('relationships')))).subscribe((value) => { + (service as any).getFindAllHref({}, null, followLink('owningCollection', {}, followLink('itemtemplate', {}, followLink('relationships')))).subscribe((value) => { expect(value).toBe(expected); }); }); @@ -279,7 +285,7 @@ describe('DataService', () => { const config: FindListOptions = Object.assign(new FindListOptions(), { elementsPerPage: 4 }); - (service as any).getFindAllHref({}, null, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', config, true, true, true))).subscribe((value) => { + (service as any).getFindAllHref({}, null, followLink('owningCollection', {}, followLink('itemtemplate', { findListOptions: config }))).subscribe((value) => { expect(value).toBe(expected); }); }); @@ -308,13 +314,19 @@ describe('DataService', () => { it('should not include linksToFollow with shouldEmbed = false', () => { const expected = `${endpointMock}/${resourceIdMock}?embed=templateItemOf`; - const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf')); + const result = (service as any).getIDHref( + endpointMock, + resourceIdMock, + followLink('bundles', { shouldEmbed: false }), + followLink('owningCollection', { shouldEmbed: false }), + followLink('templateItemOf') + ); expect(result).toEqual(expected); }); it('should include nested linksToFollow 3lvl', () => { const expected = `${endpointMock}/${resourceIdMock}?embed=owningCollection/itemtemplate/relationships`; - const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('owningCollection', undefined, true, true, true,followLink('itemtemplate', undefined, true, true, true, followLink('relationships')))); + const result = (service as any).getIDHref(endpointMock, resourceIdMock, followLink('owningCollection', {}, followLink('itemtemplate', {}, followLink('relationships')))); expect(result).toEqual(expected); }); }); diff --git a/src/app/core/data/dso-redirect-data.service.spec.ts b/src/app/core/data/dso-redirect-data.service.spec.ts index d64f37ad78..bcd25487c2 100644 --- a/src/app/core/data/dso-redirect-data.service.spec.ts +++ b/src/app/core/data/dso-redirect-data.service.spec.ts @@ -119,7 +119,7 @@ describe('DsoRedirectDataService', () => { }); it('should navigate to entities route with the corresponding entity type', () => { remoteData.payload.type = 'item'; - remoteData.payload.metadata = { + remoteData.payload.metadata = { 'dspace.entity.type': [ { language: 'en_US', @@ -174,13 +174,29 @@ describe('DsoRedirectDataService', () => { it('should not include linksToFollow with shouldEmbed = false', () => { const expected = `${requestUUIDURL}&embed=templateItemOf`; - const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('bundles', undefined, false), followLink('owningCollection', undefined, false), followLink('templateItemOf')); + const result = (service as any).getIDHref( + pidLink, + dsoUUID, + followLink('bundles', { shouldEmbed: false }), + followLink('owningCollection', { shouldEmbed: false }), + followLink('templateItemOf') + ); expect(result).toEqual(expected); }); it('should include nested linksToFollow 3lvl', () => { const expected = `${requestUUIDURL}&embed=owningCollection/itemtemplate/relationships`; - const result = (service as any).getIDHref(pidLink, dsoUUID, followLink('owningCollection', undefined, true, true, true, followLink('itemtemplate', undefined, true, true, true, followLink('relationships')))); + const result = (service as any).getIDHref( + pidLink, + dsoUUID, + followLink('owningCollection', + {}, + followLink('itemtemplate', + {}, + followLink('relationships') + ) + ) + ); expect(result).toEqual(expected); }); }); diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 9cb284db32..75723366bc 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -41,6 +41,43 @@ import { SearchConfig } from './search-filters/search-config.model'; import { PaginationService } from '../../pagination/pagination.service'; import { SearchConfigurationService } from './search-configuration.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +import { DataService } from '../../data/data.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../../core.reducers'; +import { ObjectCacheService } from '../../cache/object-cache.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DSOChangeAnalyzer } from '../../data/dso-change-analyzer.service'; + +/* tslint:disable:max-classes-per-file */ +/** + * A class that lets us delegate some methods to DataService + */ +class DataServiceImpl extends DataService { + protected linkPath = 'discover'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DSOChangeAnalyzer) { + super(); + } + + /** + * Adds the embed options to the link for the request + * @param href The href the params are to be added to + * @param args params for the query string + * @param linksToFollow links we want to embed in query string if shouldEmbed is true + */ + public addEmbedParams(href: string, args: string[], ...linksToFollow: FollowLinkConfig[]) { + return super.addEmbedParams(href, args, ...linksToFollow); + } +} /** * Service that performs all general actions that have to do with the search page @@ -78,6 +115,11 @@ export class SearchService implements OnDestroy { */ private sub; + /** + * Instance of DataServiceImpl that lets us delegate some methods to DataService + */ + private searchDataService: DataServiceImpl; + constructor(private router: Router, private routeService: RouteService, protected requestService: RequestService, @@ -89,6 +131,16 @@ export class SearchService implements OnDestroy { private paginationService: PaginationService, private searchConfigurationService: SearchConfigurationService ) { + this.searchDataService = new DataServiceImpl( + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + ); } /** @@ -131,7 +183,17 @@ export class SearchService implements OnDestroy { search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> { const href$ = this.getEndpoint(searchOptions); - href$.pipe(take(1)).subscribe((url: string) => { + href$.pipe( + take(1), + map((href: string) => { + const args = this.searchDataService.addEmbedParams(href, [], ...linksToFollow); + if (isNotEmpty(args)) { + return new URLCombiner(href, `?${args.join('&')}`).toString(); + } else { + return href; + } + }) + ).subscribe((url: string) => { const request = new this.request(this.requestService.generateRequestId(), url); const getResponseParserFn: () => GenericConstructor = () => { @@ -152,7 +214,7 @@ export class SearchService implements OnDestroy { ); return this.directlyAttachIndexableObjects(sqr$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); - } + } /** * Method to retrieve request entries for search results from the server @@ -399,9 +461,9 @@ export class SearchService implements OnDestroy { let pageParams = { page: 1 }; const queryParams = { view: viewMode }; if (viewMode === ViewMode.DetailedListElement) { - pageParams = Object.assign(pageParams, {pageSize: 1}); + pageParams = Object.assign(pageParams, { pageSize: 1 }); } else if (config.pageSize === 1) { - pageParams = Object.assign(pageParams, {pageSize: 10}); + pageParams = Object.assign(pageParams, { pageSize: 10 }); } this.paginationService.updateRouteWithUrl(this.searchConfigurationService.paginationID, hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], pageParams, queryParams); }); @@ -413,7 +475,7 @@ export class SearchService implements OnDestroy { * @param {string} configurationName the name of the configuration * @returns {Observable>} The found configuration */ - getSearchConfigurationFor(scope?: string, configurationName?: string ): Observable> { + getSearchConfigurationFor(scope?: string, configurationName?: string): Observable> { const href$ = this.halService.getEndpoint(this.configurationLinkPath).pipe( map((url: string) => this.getConfigUrl(url, scope, configurationName)), ); diff --git a/src/app/core/submission/vocabularies/vocabulary.service.ts b/src/app/core/submission/vocabularies/vocabulary.service.ts index d82ef01087..da58512441 100644 --- a/src/app/core/submission/vocabularies/vocabulary.service.ts +++ b/src/app/core/submission/vocabularies/vocabulary.service.ts @@ -173,7 +173,7 @@ export class VocabularyService { ); // TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved - return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe( + return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe( getFirstSucceededRemoteDataPayload(), switchMap((vocabulary: Vocabulary) => vocabulary.entries), ); @@ -200,7 +200,7 @@ export class VocabularyService { ); // TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved - return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe( + return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe( getFirstSucceededRemoteDataPayload(), switchMap((vocabulary: Vocabulary) => vocabulary.entries), ); @@ -249,7 +249,7 @@ export class VocabularyService { ); // TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved - return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', options, false)).pipe( + return this.findVocabularyById(vocabularyOptions.name, true, true, followLink('entries', { findListOptions: options, shouldEmbed: false })).pipe( getFirstSucceededRemoteDataPayload(), switchMap((vocabulary: Vocabulary) => vocabulary.entries), getFirstSucceededRemoteListPayload(), diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts index b729307443..74411d2341 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts @@ -49,8 +49,8 @@ export class ClaimedTaskSearchResultDetailElementComponent extends SearchResultD */ ngOnInit() { super.ngOnInit(); - this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true, - followLink('item', null, true, true, true, followLink('bundles')), + this.linkService.resolveLinks(this.dso, followLink('workflowitem', {}, + followLink('item', {}, followLink('bundles')), followLink('submitter') ), followLink('action')); this.workflowitemRD$ = this.dso.workflowitem as Observable>; diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts index a8b2514ffb..df27abd42e 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts @@ -48,8 +48,8 @@ export class PoolSearchResultDetailElementComponent extends SearchResultDetailEl */ ngOnInit() { super.ngOnInit(); - this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true, - followLink('item', null, true, true, true, followLink('bundles')), + this.linkService.resolveLinks(this.dso, followLink('workflowitem', {}, + followLink('item', {}, followLink('bundles')), followLink('submitter') ), followLink('action')); this.workflowitemRD$ = this.dso.workflowitem as Observable>; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts index 5571782ce2..eaf407d787 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts @@ -55,10 +55,7 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul super.ngOnInit(); this.linkService.resolveLinks(this.dso, followLink('workflowitem', - null, - true, - false, - true, + { useCachedVersionIfAvailable: false }, followLink('item'), followLink('submitter') ), diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts index 630aa699a7..0b9a925dbf 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts @@ -56,10 +56,7 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul super.ngOnInit(); this.linkService.resolveLinks(this.dso, followLink('workflowitem', - null, - true, - false, - true, + { useCachedVersionIfAvailable: false }, followLink('item'), followLink('submitter') ), diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts index dae3272889..2cf8f9a231 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts @@ -50,7 +50,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle */ ngOnInit() { super.ngOnInit(); - this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true, + this.linkService.resolveLinks(this.dso, followLink('workflowitem', {}, followLink('item'), followLink('submitter') ), followLink('action')); this.workflowitemRD$ = this.dso.workflowitem as Observable>; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts index fe4fa715ee..e9d64db572 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts @@ -60,7 +60,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen */ ngOnInit() { super.ngOnInit(); - this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true, + this.linkService.resolveLinks(this.dso, followLink('workflowitem', {}, followLink('item'), followLink('submitter') ), followLink('action')); this.workflowitemRD$ = this.dso.workflowitem as Observable>; diff --git a/src/app/shared/utils/follow-link-config.model.ts b/src/app/shared/utils/follow-link-config.model.ts index c823d53296..08153e1217 100644 --- a/src/app/shared/utils/follow-link-config.model.ts +++ b/src/app/shared/utils/follow-link-config.model.ts @@ -1,5 +1,6 @@ import { FindListOptions } from '../../core/data/request.models'; import { HALResource } from '../../core/shared/hal-resource.model'; +import { hasValue } from '../empty.util'; /** * A class to send the retrieval of a {@link HALLink} @@ -40,6 +41,12 @@ export class FollowLinkConfig { * Defaults to true */ reRequestOnStale? = true; + + /** + * If this is false an error will be thrown if the link doesn't exist on the model it is used on + * Defaults to false + */ + isOptional? = false; } /** @@ -57,23 +64,35 @@ export class FollowLinkConfig { * no valid cached version. Defaults * @param reRequestOnStale: Whether or not the link should automatically be re-requested after the * response becomes stale + * @param isOptional: Whether or not to fail if the link doesn't exist * @param linksToFollow: a list of {@link FollowLinkConfig}s to * use on the retrieved object. */ export const followLink = ( linkName: keyof R['_links'], - findListOptions?: FindListOptions, - shouldEmbed = true, - useCachedVersionIfAvailable = true, - reRequestOnStale = true, - ...linksToFollow: FollowLinkConfig[] -): FollowLinkConfig => { - return { - name: linkName, + { findListOptions, shouldEmbed, useCachedVersionIfAvailable, reRequestOnStale, + isOptional + }: { + findListOptions?: FindListOptions, + shouldEmbed?: boolean, + useCachedVersionIfAvailable?: boolean, + reRequestOnStale?: boolean, + isOptional?: boolean, + } = {}, + ...linksToFollow: FollowLinkConfig[] +): FollowLinkConfig => { + const followLinkConfig = { + name: linkName, + findListOptions: hasValue(findListOptions) ? findListOptions : new FindListOptions(), + shouldEmbed: hasValue(shouldEmbed) ? shouldEmbed : true, + useCachedVersionIfAvailable: hasValue(useCachedVersionIfAvailable) ? useCachedVersionIfAvailable : true, + reRequestOnStale: hasValue(reRequestOnStale) ? reRequestOnStale : true, + isOptional: hasValue(isOptional) ? isOptional : false, linksToFollow }; + return followLinkConfig; };