Skip to content

Commit d8a3873

Browse files
[DURACOM-426] config refactor, add typesdoc, fix popover issue, fix search result layout, add search manager for search enrichment
1 parent 4575977 commit d8a3873

27 files changed

Lines changed: 694 additions & 213 deletions

config/config.example.yml

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,12 @@ submission:
237237
- value: default
238238
style: text-muted
239239
icon: fa-circle-xmark
240-
241-
# Icons to be displayed next to an authority controlled value, to give indication of the source.
242-
sourceIcons:
243-
# Example of configuration for authority logo based on sources.
244-
# The configured icon will be displayed next to the authority value in submission and on item page or search results.
245-
- source: orcid
246-
- path: assets/images/orcid.logo.icon.svg
240+
# Icons to be displayed next to an authority controlled value, to give indication of the source.
241+
sourceIcons:
242+
# Example of configuration for authority logo based on sources.
243+
# The configured icon will be displayed next to the authority value in submission and on item page or search results.
244+
- source: orcid
245+
- path: assets/images/orcid.logo.icon.svg
247246
# Fallback language in which the UI will be rendered if the user's browser language is not an active language
248247
fallbackLanguage: en
249248

@@ -399,6 +398,30 @@ item:
399398
pageSize: 5
400399
# Show the bitstream access status label on the item page
401400
showAccessStatuses: false
401+
# Configuration of metadata to be displayed in the item metadata link view popover
402+
metadataLinkViewPopoverData:
403+
# Metdadata list to be displayed for entities without a specific configuration
404+
fallbackMetdataList:
405+
- dc.description.abstract
406+
- dc.description.note
407+
# Configuration for each entity type
408+
entityDataConfig:
409+
- entityType: Person
410+
# Descriptive metadata (popover body)
411+
metadataList:
412+
- person.affiliation.name
413+
- person.email
414+
# Title metadata (popover header)
415+
titleMetadataList:
416+
- person.givenName
417+
- person.familyName
418+
# Configuration for identifier subtypes, based on metadata like dc.identifier.ror where ror is the subtype.
419+
# This is used to map the layout of the identifier in the popover and the icon displayed next to the metadata value.
420+
identifierSubtypes:
421+
- name: ror
422+
icon: assets/images/ror.logo.icon.svg
423+
iconPosition: IdentifierSubtypesIconPositionEnum.LEFT
424+
link: https://ror.org
402425

403426
# Community Page Config
404427
community:
@@ -648,7 +671,8 @@ accessibility:
648671
# The duration in days after which the accessibility settings cookie expires
649672
cookieExpirationDuration: 7
650673

651-
# Configuration for custom layout
674+
# Configuration for layout customization of metadata rendering in Item page
675+
# Currently only the authority reference config is available, more will follow with the integration of the so called CRIS layout.
652676
layout:
653677
# Configuration of icons and styles to be used for each authority controlled link
654678
authorityRef:
@@ -676,22 +700,6 @@ layout:
676700
icon: fas fa-project-diagram
677701
style: text-success
678702

679-
# When the search results are retrieved, for each item type the metadata with a valid authority value are inspected.
680-
# Referenced items will be fetched with a find all by id strategy to avoid individual rest requests
681-
# to efficiently display the search results.
682-
followAuthorityMetadata:
683-
- type: Publication
684-
metadata: dc.contributor.author
685-
- type: Product
686-
metadata: dc.contributor.author
687-
688-
# The maximum number of item to process when following authority metadata values.
689-
followAuthorityMaxItemLimit: 100
690-
691-
# The maximum number of metadata values to process for each metadata key
692-
# when following authority metadata values.
693-
followAuthorityMetadataValuesLimit: 5
694-
695703
# Configuration for customization of search results
696704
searchResults:
697705
# Metadata fields to be displayed in the search results under the standard ones
@@ -704,29 +712,20 @@ searchResults:
704712
- dc.contributor.author
705713
- dc.creator
706714
- dc.contributor.*
715+
# When the search results are retrieved, for each item type the metadata with a valid authority value are inspected.
716+
# Referenced items will be fetched with a find all by id strategy to avoid individual rest requests
717+
# to efficiently display the search results.
718+
followAuthorityMetadata:
719+
- type: Publication
720+
metadata: dc.contributor.author
721+
- type: Product
722+
metadata: dc.contributor.author
723+
724+
# The maximum number of item to process when following authority metadata values.
725+
followAuthorityMaxItemLimit: 100
726+
727+
# The maximum number of metadata values to process for each metadata key
728+
# when following authority metadata values.
729+
followAuthorityMetadataValuesLimit: 5
707730

708-
# Configuration of metadata to be displayed in the item metadata link view popover
709-
metadataLinkViewPopoverData:
710-
# Metdadata list to be displayed for entities without a specific configuration
711-
fallbackMetdataList:
712-
- dc.description.abstract
713-
- dc.description.note
714-
# Configuration for each entity type
715-
entityDataConfig:
716-
- entityType: Person
717-
# Descriptive metadata (popover body)
718-
metadataList:
719-
- person.affiliation.name
720-
- person.email
721-
# Title metadata (popover header)
722-
titleMetadataList:
723-
- person.givenName
724-
- person.familyName
725-
# Configuration for identifier subtypes, based on metadata like dc.identifier.ror where ror is the subtype.
726-
# This is used to map the layout of the identifier in the popover and the icon displayed next to the metadata value.
727-
identifierSubtypes:
728-
- name: ror
729-
icon: assets/images/ror.logo.icon.svg
730-
iconPosition: IdentifierSubtypesIconPositionEnum.LEFT
731-
link: https://ror.org
732731

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { Injectable } from '@angular/core';
2+
import { FollowAuthorityMetadata } from '@dspace/config/search-follow-metadata.interface';
3+
import {
4+
hasValue,
5+
isNotEmpty,
6+
} from '@dspace/shared/utils/empty.util';
7+
import isArray from 'lodash/isArray';
8+
import {
9+
Observable,
10+
of,
11+
} from 'rxjs';
12+
import {
13+
map,
14+
switchMap,
15+
} from 'rxjs/operators';
16+
import { SearchService } from 'src/app/shared/search/search.service';
17+
18+
import { environment } from '../../../environments/environment';
19+
import { ItemDataService } from '../data/item-data.service';
20+
import { PaginatedList } from '../data/paginated-list.model';
21+
import { RemoteData } from '../data/remote-data';
22+
import { WORKFLOWITEM } from '../eperson/models/workflowitem.resource-type';
23+
import { WORKSPACEITEM } from '../eperson/models/workspaceitem.resource-type';
24+
import { DSpaceObject } from '../shared/dspace-object.model';
25+
import { FollowLinkConfig } from '../shared/follow-link-config.model';
26+
import { Item } from '../shared/item.model';
27+
import { ITEM } from '../shared/item.resource-type';
28+
import { MetadataValue } from '../shared/metadata.models';
29+
import { Metadata } from '../shared/metadata.utils';
30+
import { getFirstCompletedRemoteData } from '../shared/operators';
31+
import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model';
32+
import { SearchObjects } from '../shared/search/models/search-objects.model';
33+
import { BrowseService } from './browse.service';
34+
35+
/**
36+
* The service aims to manage browse requests and subsequent extra fetch requests.
37+
*/
38+
@Injectable({ providedIn: 'root' })
39+
export class SearchManager {
40+
41+
constructor(
42+
protected itemService: ItemDataService,
43+
protected browseService: BrowseService,
44+
protected searchService: SearchService,
45+
) {
46+
}
47+
48+
/**
49+
* Method to retrieve a paginated list of search results from the server
50+
* @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search
51+
* @param responseMsToLive The amount of milliseconds for the response to live in cache
52+
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
53+
* no valid cached version. Defaults to true
54+
* @param reRequestOnStale Whether or not the request should automatically be re-requested after
55+
* the response becomes stale
56+
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
57+
* @returns {Observable<RemoteData<SearchObjects<T>>>} Emits a paginated list with all search results found
58+
*/
59+
search<T extends DSpaceObject>(
60+
searchOptions?: PaginatedSearchOptions,
61+
responseMsToLive?: number,
62+
useCachedVersionIfAvailable = true,
63+
reRequestOnStale = true,
64+
...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
65+
return this.searchService.search(searchOptions, responseMsToLive, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)
66+
.pipe(this.completeSearchObjectsWithExtraData());
67+
}
68+
69+
70+
protected completeWithExtraData() {
71+
return switchMap((itemsRD: RemoteData<PaginatedList<Item>>) => {
72+
if (itemsRD.isSuccess) {
73+
return this.fetchExtraData(itemsRD.payload.page).pipe(map(() => {
74+
return itemsRD;
75+
}));
76+
}
77+
return of(itemsRD);
78+
});
79+
}
80+
81+
protected completeSearchObjectsWithExtraData<T extends DSpaceObject>() {
82+
return switchMap((searchObjectsRD: RemoteData<SearchObjects<T>>) => {
83+
if (searchObjectsRD.isSuccess) {
84+
const items: Item[] = searchObjectsRD.payload.page
85+
.map((searchResult) => isNotEmpty(searchResult?._embedded?.indexableObject) ? searchResult._embedded.indexableObject : searchResult.indexableObject) as any;
86+
return this.fetchExtraData(items).pipe(map(() => {
87+
return searchObjectsRD;
88+
}));
89+
}
90+
return of(searchObjectsRD);
91+
});
92+
}
93+
94+
protected fetchExtraData<T extends DSpaceObject>(objects: T[]): Observable<any> {
95+
96+
const items: Item[] = objects
97+
.map((object: any) => {
98+
if (object.type === ITEM.value) {
99+
return object as Item;
100+
} else if (object.type === WORKSPACEITEM.value || object.type === WORKFLOWITEM.value) {
101+
return object?._embedded?.item as Item;
102+
} else {
103+
// Handle workflow task here, where the item is embedded in a workflowitem
104+
return object?._embedded?.workflowitem?._embedded?.item as Item;
105+
}
106+
107+
})
108+
.filter((item) => hasValue(item));
109+
110+
const uuidList = this.extractUUID(items, environment.searchResult.followAuthorityMetadata, environment.searchResult.followAuthorityMaxItemLimit);
111+
112+
return uuidList.length > 0 ? this.itemService.findAllById(uuidList).pipe(
113+
getFirstCompletedRemoteData(),
114+
map(data => {
115+
if (data.hasSucceeded) {
116+
return of(data);
117+
} else {
118+
of(null);
119+
}
120+
}),
121+
) : of(null);
122+
}
123+
124+
protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[], numberOfElementsToReturn?: number): string[] {
125+
const uuidMap = {};
126+
127+
items.forEach((item) => {
128+
metadataToFollow.forEach((followMetadata: FollowAuthorityMetadata) => {
129+
if (item.entityType === followMetadata.type) {
130+
if (isArray(followMetadata.metadata)) {
131+
followMetadata.metadata.forEach((metadata) => {
132+
Metadata.all(item.metadata, metadata, null, null, false, environment.searchResult.followAuthorityMetadataValuesLimit)
133+
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
134+
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
135+
});
136+
} else {
137+
Metadata.all(item.metadata, followMetadata.metadata, null, null, false, environment.searchResult.followAuthorityMetadataValuesLimit)
138+
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
139+
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
140+
}
141+
}
142+
});
143+
});
144+
145+
if (hasValue(numberOfElementsToReturn) && numberOfElementsToReturn > 0) {
146+
return Object.keys(uuidMap).slice(0, numberOfElementsToReturn);
147+
}
148+
149+
return Object.keys(uuidMap);
150+
}
151+
}

0 commit comments

Comments
 (0)