forked from hazza/dspace-angular
refactor followlinks to use a single object for all params and add an isOptional param. Also add support for embedding links to search service
This commit is contained in:
@@ -35,7 +35,7 @@ export class BitstreamPageResolver implements Resolve<RemoteData<Bitstream>> {
|
|||||||
*/
|
*/
|
||||||
get followLinks(): FollowLinkConfig<Bitstream>[] {
|
get followLinks(): FollowLinkConfig<Bitstream>[] {
|
||||||
return [
|
return [
|
||||||
followLink('bundle', undefined, true, true, true, followLink('item')),
|
followLink('bundle', {}, followLink('item')),
|
||||||
followLink('format')
|
followLink('format')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ import { ResolvedAction } from '../core/resolving/resolver.actions';
|
|||||||
* Requesting them as embeds will limit the number of requests
|
* Requesting them as embeds will limit the number of requests
|
||||||
*/
|
*/
|
||||||
export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Collection>[] = [
|
export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Collection>[] = [
|
||||||
followLink('parentCommunity', undefined, true, true, true,
|
followLink('parentCommunity', {},
|
||||||
followLink('parentCommunity')
|
followLink('parentCommunity')
|
||||||
),
|
),
|
||||||
followLink('logo')
|
followLink('logo')
|
||||||
|
@@ -79,7 +79,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
||||||
map((item: Item) => this.linkService.resolveLink(
|
map((item: Item) => this.linkService.resolveLink(
|
||||||
item,
|
item,
|
||||||
followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams'))
|
followLink('bundles', {}, followLink('bitstreams'))
|
||||||
))
|
))
|
||||||
) as Observable<Item>;
|
) as Observable<Item>;
|
||||||
|
|
||||||
|
@@ -323,7 +323,7 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
|
|
||||||
private getItemRelationships() {
|
private getItemRelationships() {
|
||||||
this.linkService.resolveLink(this.item,
|
this.linkService.resolveLink(this.item,
|
||||||
followLink('relationships', undefined, true, true, true,
|
followLink('relationships', {},
|
||||||
followLink('relationshipType'),
|
followLink('relationshipType'),
|
||||||
followLink('leftItem'),
|
followLink('leftItem'),
|
||||||
followLink('rightItem'),
|
followLink('rightItem'),
|
||||||
|
@@ -15,13 +15,13 @@ import { ResolvedAction } from '../core/resolving/resolver.actions';
|
|||||||
* Requesting them as embeds will limit the number of requests
|
* Requesting them as embeds will limit the number of requests
|
||||||
*/
|
*/
|
||||||
export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
|
export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
|
||||||
followLink('owningCollection', undefined, true, true, true,
|
followLink('owningCollection', {},
|
||||||
followLink('parentCommunity', undefined, true, true, true,
|
followLink('parentCommunity', {},
|
||||||
followLink('parentCommunity'))
|
followLink('parentCommunity'))
|
||||||
),
|
),
|
||||||
followLink('bundles', new FindListOptions(), true, true, true, followLink('bitstreams')),
|
followLink('bundles', {}, followLink('bitstreams')),
|
||||||
followLink('relationships'),
|
followLink('relationships'),
|
||||||
followLink('version', undefined, true, true, true, followLink('versionhistory')),
|
followLink('version', {}, followLink('versionhistory')),
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,9 +16,11 @@ import { SearchResult } from '../shared/search/search-result.model';
|
|||||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
import { SearchService } from '../core/shared/search/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { currentPath } from '../shared/utils/route.utils';
|
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 { Context } from '../core/shared/context.model';
|
||||||
import { SortOptions } from '../core/cache/models/sort-options.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({
|
@Component({
|
||||||
selector: 'ds-search',
|
selector: 'ds-search',
|
||||||
|
@@ -174,8 +174,8 @@ export class CommunityListService {
|
|||||||
direction: options.sort.direction
|
direction: options.sort.direction
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
followLink('subcommunities', this.configOnePage, true, true),
|
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
||||||
followLink('collections', this.configOnePage, true, true))
|
followLink('collections', { findListOptions: this.configOnePage }))
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstSucceededRemoteData(),
|
||||||
map((results) => results.payload),
|
map((results) => results.payload),
|
||||||
@@ -242,8 +242,8 @@ export class CommunityListService {
|
|||||||
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
||||||
currentPage: i
|
currentPage: i
|
||||||
},
|
},
|
||||||
followLink('subcommunities', this.configOnePage, true, true),
|
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
||||||
followLink('collections', this.configOnePage, true, true))
|
followLink('collections', { findListOptions: this.configOnePage }))
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
switchMap((rd: RemoteData<PaginatedList<Community>>) => {
|
switchMap((rd: RemoteData<PaginatedList<Community>>) => {
|
||||||
|
10
src/app/core/cache/builders/link.service.spec.ts
vendored
10
src/app/core/cache/builders/link.service.spec.ts
vendored
@@ -102,7 +102,7 @@ describe('LinkService', () => {
|
|||||||
describe('resolveLink', () => {
|
describe('resolveLink', () => {
|
||||||
describe(`when the linkdefinition concerns a single object`, () => {
|
describe(`when the linkdefinition concerns a single object`, () => {
|
||||||
beforeEach(() => {
|
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', () => {
|
it('should call dataservice.findByHref with the correct href and nested links', () => {
|
||||||
expect(testDataService.findByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, true, true, followLink('successor'));
|
expect(testDataService.findByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, true, true, followLink('successor'));
|
||||||
@@ -116,7 +116,7 @@ describe('LinkService', () => {
|
|||||||
propertyName: 'predecessor',
|
propertyName: 'predecessor',
|
||||||
isList: true
|
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', () => {
|
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'));
|
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', () => {
|
describe('either way', () => {
|
||||||
beforeEach(() => {
|
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', () => {
|
it('should call getLinkDefinition with the correct model and link', () => {
|
||||||
@@ -149,7 +149,7 @@ describe('LinkService', () => {
|
|||||||
});
|
});
|
||||||
it('should throw an error', () => {
|
it('should throw an error', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -160,7 +160,7 @@ describe('LinkService', () => {
|
|||||||
});
|
});
|
||||||
it('should throw an error', () => {
|
it('should throw an error', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor')));
|
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')));
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
7
src/app/core/cache/builders/link.service.ts
vendored
7
src/app/core/cache/builders/link.service.ts
vendored
@@ -55,9 +55,7 @@ export class LinkService {
|
|||||||
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U>> {
|
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U>> {
|
||||||
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
|
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
|
||||||
|
|
||||||
if (hasNoValue(matchingLinkDef)) {
|
if (hasValue(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 {
|
|
||||||
const provider = this.getDataServiceFor(matchingLinkDef.resourceType);
|
const provider = this.getDataServiceFor(matchingLinkDef.resourceType);
|
||||||
|
|
||||||
if (hasNoValue(provider)) {
|
if (hasNoValue(provider)) {
|
||||||
@@ -84,7 +82,10 @@ export class LinkService {
|
|||||||
throw e;
|
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;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -523,7 +523,7 @@ describe('RemoteDataBuildService', () => {
|
|||||||
let paginatedLinksToFollow;
|
let paginatedLinksToFollow;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
paginatedLinksToFollow = [
|
paginatedLinksToFollow = [
|
||||||
followLink('page', undefined, true, true, true, ...linksToFollow),
|
followLink('page', {}, ...linksToFollow),
|
||||||
...linksToFollow
|
...linksToFollow
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
@@ -271,7 +271,7 @@ export class RemoteDataBuildService {
|
|||||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
*/
|
*/
|
||||||
buildList<T extends HALResource>(href$: string | Observable<string>, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
buildList<T extends HALResource>(href$: string | Observable<string>, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
return this.buildFromHref<PaginatedList<T>>(href$, followLink('page', undefined, false, true, true, ...linksToFollow));
|
return this.buildFromHref<PaginatedList<T>>(href$, followLink('page', { shouldEmbed: false }, ...linksToFollow));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -233,7 +233,7 @@ describe('DataService', () => {
|
|||||||
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||||
elementsPerPage: 5
|
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);
|
expect(value).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -253,7 +253,7 @@ describe('DataService', () => {
|
|||||||
elementsPerPage: 2
|
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);
|
expect(value).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -261,7 +261,13 @@ describe('DataService', () => {
|
|||||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||||
const expected = `${endpoint}?embed=templateItemOf`;
|
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);
|
expect(value).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -269,7 +275,7 @@ describe('DataService', () => {
|
|||||||
it('should include nested linksToFollow 3lvl', () => {
|
it('should include nested linksToFollow 3lvl', () => {
|
||||||
const expected = `${endpoint}?embed=owningCollection/itemtemplate/relationships`;
|
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);
|
expect(value).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -279,7 +285,7 @@ describe('DataService', () => {
|
|||||||
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
const config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||||
elementsPerPage: 4
|
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);
|
expect(value).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -308,13 +314,19 @@ describe('DataService', () => {
|
|||||||
|
|
||||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||||
const expected = `${endpointMock}/${resourceIdMock}?embed=templateItemOf`;
|
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);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include nested linksToFollow 3lvl', () => {
|
it('should include nested linksToFollow 3lvl', () => {
|
||||||
const expected = `${endpointMock}/${resourceIdMock}?embed=owningCollection/itemtemplate/relationships`;
|
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);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -174,13 +174,29 @@ describe('DsoRedirectDataService', () => {
|
|||||||
|
|
||||||
it('should not include linksToFollow with shouldEmbed = false', () => {
|
it('should not include linksToFollow with shouldEmbed = false', () => {
|
||||||
const expected = `${requestUUIDURL}&embed=templateItemOf`;
|
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);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include nested linksToFollow 3lvl', () => {
|
it('should include nested linksToFollow 3lvl', () => {
|
||||||
const expected = `${requestUUIDURL}&embed=owningCollection/itemtemplate/relationships`;
|
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);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -41,6 +41,43 @@ import { SearchConfig } from './search-filters/search-config.model';
|
|||||||
import { PaginationService } from '../../pagination/pagination.service';
|
import { PaginationService } from '../../pagination/pagination.service';
|
||||||
import { SearchConfigurationService } from './search-configuration.service';
|
import { SearchConfigurationService } from './search-configuration.service';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
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<any> {
|
||||||
|
protected linkPath = 'discover';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DSOChangeAnalyzer<any>) {
|
||||||
|
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<any>[]) {
|
||||||
|
return super.addEmbedParams(href, args, ...linksToFollow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all general actions that have to do with the search page
|
* 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;
|
private sub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of DataServiceImpl that lets us delegate some methods to DataService
|
||||||
|
*/
|
||||||
|
private searchDataService: DataServiceImpl;
|
||||||
|
|
||||||
constructor(private router: Router,
|
constructor(private router: Router,
|
||||||
private routeService: RouteService,
|
private routeService: RouteService,
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
@@ -89,6 +131,16 @@ export class SearchService implements OnDestroy {
|
|||||||
private paginationService: PaginationService,
|
private paginationService: PaginationService,
|
||||||
private searchConfigurationService: SearchConfigurationService
|
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<T extends DSpaceObject>(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
|
search<T extends DSpaceObject>(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
|
||||||
const href$ = this.getEndpoint(searchOptions);
|
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 request = new this.request(this.requestService.generateRequestId(), url);
|
||||||
|
|
||||||
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
||||||
@@ -399,9 +461,9 @@ export class SearchService implements OnDestroy {
|
|||||||
let pageParams = { page: 1 };
|
let pageParams = { page: 1 };
|
||||||
const queryParams = { view: viewMode };
|
const queryParams = { view: viewMode };
|
||||||
if (viewMode === ViewMode.DetailedListElement) {
|
if (viewMode === ViewMode.DetailedListElement) {
|
||||||
pageParams = Object.assign(pageParams, {pageSize: 1});
|
pageParams = Object.assign(pageParams, { pageSize: 1 });
|
||||||
} else if (config.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);
|
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
|
* @param {string} configurationName the name of the configuration
|
||||||
* @returns {Observable<RemoteData<SearchConfig[]>>} The found configuration
|
* @returns {Observable<RemoteData<SearchConfig[]>>} The found configuration
|
||||||
*/
|
*/
|
||||||
getSearchConfigurationFor(scope?: string, configurationName?: string ): Observable<RemoteData<SearchConfig>> {
|
getSearchConfigurationFor(scope?: string, configurationName?: string): Observable<RemoteData<SearchConfig>> {
|
||||||
const href$ = this.halService.getEndpoint(this.configurationLinkPath).pipe(
|
const href$ = this.halService.getEndpoint(this.configurationLinkPath).pipe(
|
||||||
map((url: string) => this.getConfigUrl(url, scope, configurationName)),
|
map((url: string) => this.getConfigUrl(url, scope, configurationName)),
|
||||||
);
|
);
|
||||||
|
@@ -173,7 +173,7 @@ export class VocabularyService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// TODO remove false for the entries embed when https://github.com/DSpace/DSpace/issues/3096 is solved
|
// 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(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
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
|
// 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(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
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
|
// 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(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
switchMap((vocabulary: Vocabulary) => vocabulary.entries),
|
||||||
getFirstSucceededRemoteListPayload(),
|
getFirstSucceededRemoteListPayload(),
|
||||||
|
@@ -49,8 +49,8 @@ export class ClaimedTaskSearchResultDetailElementComponent extends SearchResultD
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||||
followLink('item', null, true, true, true, followLink('bundles')),
|
followLink('item', {}, followLink('bundles')),
|
||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
@@ -48,8 +48,8 @@ export class PoolSearchResultDetailElementComponent extends SearchResultDetailEl
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', null, true, true, true,
|
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||||
followLink('item', null, true, true, true, followLink('bundles')),
|
followLink('item', {}, followLink('bundles')),
|
||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
@@ -55,10 +55,7 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul
|
|||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.linkService.resolveLinks(this.dso,
|
this.linkService.resolveLinks(this.dso,
|
||||||
followLink('workflowitem',
|
followLink('workflowitem',
|
||||||
null,
|
{ useCachedVersionIfAvailable: false },
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
followLink('item'),
|
followLink('item'),
|
||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
),
|
),
|
||||||
|
@@ -56,10 +56,7 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul
|
|||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.linkService.resolveLinks(this.dso,
|
this.linkService.resolveLinks(this.dso,
|
||||||
followLink('workflowitem',
|
followLink('workflowitem',
|
||||||
null,
|
{ useCachedVersionIfAvailable: false },
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
followLink('item'),
|
followLink('item'),
|
||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
),
|
),
|
||||||
|
@@ -50,7 +50,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.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('item'), followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
@@ -60,7 +60,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.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('item'), followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
this.workflowitemRD$ = this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { FindListOptions } from '../../core/data/request.models';
|
import { FindListOptions } from '../../core/data/request.models';
|
||||||
import { HALResource } from '../../core/shared/hal-resource.model';
|
import { HALResource } from '../../core/shared/hal-resource.model';
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to send the retrieval of a {@link HALLink}
|
* A class to send the retrieval of a {@link HALLink}
|
||||||
@@ -40,6 +41,12 @@ export class FollowLinkConfig<R extends HALResource> {
|
|||||||
* Defaults to true
|
* Defaults to true
|
||||||
*/
|
*/
|
||||||
reRequestOnStale? = 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<R extends HALResource> {
|
|||||||
* no valid cached version. Defaults
|
* no valid cached version. Defaults
|
||||||
* @param reRequestOnStale: Whether or not the link should automatically be re-requested after the
|
* @param reRequestOnStale: Whether or not the link should automatically be re-requested after the
|
||||||
* response becomes stale
|
* 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
|
* @param linksToFollow: a list of {@link FollowLinkConfig}s to
|
||||||
* use on the retrieved object.
|
* use on the retrieved object.
|
||||||
*/
|
*/
|
||||||
export const followLink = <R extends HALResource>(
|
export const followLink = <R extends HALResource>(
|
||||||
linkName: keyof R['_links'],
|
linkName: keyof R['_links'],
|
||||||
findListOptions?: FindListOptions,
|
{
|
||||||
shouldEmbed = true,
|
|
||||||
useCachedVersionIfAvailable = true,
|
|
||||||
reRequestOnStale = true,
|
|
||||||
...linksToFollow: FollowLinkConfig<any>[]
|
|
||||||
): FollowLinkConfig<R> => {
|
|
||||||
return {
|
|
||||||
name: linkName,
|
|
||||||
findListOptions,
|
findListOptions,
|
||||||
shouldEmbed,
|
shouldEmbed,
|
||||||
useCachedVersionIfAvailable,
|
useCachedVersionIfAvailable,
|
||||||
reRequestOnStale,
|
reRequestOnStale,
|
||||||
|
isOptional
|
||||||
|
}: {
|
||||||
|
findListOptions?: FindListOptions,
|
||||||
|
shouldEmbed?: boolean,
|
||||||
|
useCachedVersionIfAvailable?: boolean,
|
||||||
|
reRequestOnStale?: boolean,
|
||||||
|
isOptional?: boolean,
|
||||||
|
} = {},
|
||||||
|
...linksToFollow: FollowLinkConfig<any>[]
|
||||||
|
): FollowLinkConfig<R> => {
|
||||||
|
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
|
linksToFollow
|
||||||
};
|
};
|
||||||
|
return followLinkConfig;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user