1- import { Injectable } from '@angular/core' ;
1+ import {
2+ Inject ,
3+ Injectable ,
4+ } from '@angular/core' ;
25import isArray from 'lodash/isArray' ;
36import {
7+ combineLatest ,
48 Observable ,
59 of ,
610} from 'rxjs' ;
711import {
12+ filter ,
813 map ,
914 switchMap ,
15+ take ,
1016} from 'rxjs/operators' ;
17+ import { SearchOptions } from 'src/app/shared/search/models/search-options.model' ;
1118
19+ import {
20+ APP_CONFIG ,
21+ AppConfig ,
22+ } from '../../../config/app-config.interface' ;
1223import { FollowAuthorityMetadata } from '../../../config/search-follow-metadata.interface' ;
1324import { environment } from '../../../environments/environment' ;
25+ import { AuthorizationService } from '../../shared/authorizations/authorization.service' ;
1426import {
1527 hasValue ,
1628 isNotEmpty ,
1729} from '../../shared/empty.util' ;
1830import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model' ;
1931import { SearchObjects } from '../../shared/search/models/search-objects.model' ;
2032import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model' ;
33+ import { getRequestIdFromParams } from '../data/feature-authorization/authorization-utils' ;
34+ import { FeatureID } from '../data/feature-authorization/feature-id' ;
2135import { ItemDataService } from '../data/item-data.service' ;
2236import { PaginatedList } from '../data/paginated-list.model' ;
2337import { RemoteData } from '../data/remote-data' ;
@@ -43,6 +57,8 @@ export class SearchManager {
4357 protected itemService : ItemDataService ,
4458 protected browseService : BrowseService ,
4559 protected searchService : SearchService ,
60+ protected authorizationService : AuthorizationService ,
61+ @Inject ( APP_CONFIG ) protected appConfig : AppConfig ,
4662 ) {
4763 }
4864
@@ -79,7 +95,7 @@ export class SearchManager {
7995 ...linksToFollow : FollowLinkConfig < T > [ ] ) : Observable < RemoteData < SearchObjects < T > > > {
8096 const optionsWithDefaultProjection = Object . assign ( new PaginatedSearchOptions ( { } ) , searchOptions , { projection : searchOptions . projection ?? 'preventMetadataSecurity' } ) ;
8197 return this . searchService . search ( optionsWithDefaultProjection , responseMsToLive , useCachedVersionIfAvailable , reRequestOnStale , ...linksToFollow )
82- . pipe ( this . completeSearchObjectsWithExtraData ( ) ) ;
98+ . pipe ( this . completeSearchObjectsWithExtraData ( optionsWithDefaultProjection ) ) ;
8399 }
84100
85101
@@ -94,19 +110,111 @@ export class SearchManager {
94110 } ) ;
95111 }
96112
97- protected completeSearchObjectsWithExtraData < T extends DSpaceObject > ( ) {
113+ protected completeSearchObjectsWithExtraData < T extends DSpaceObject > ( searchOptions : SearchOptions ) {
98114 return switchMap ( ( searchObjectsRD : RemoteData < SearchObjects < T > > ) => {
99115 if ( searchObjectsRD . isSuccess ) {
100116 const items : Item [ ] = searchObjectsRD . payload . page
101117 . map ( ( searchResult ) => isNotEmpty ( searchResult ?. _embedded ?. indexableObject ) ? searchResult . _embedded . indexableObject : searchResult . indexableObject ) as any ;
102- return this . fetchExtraData ( items ) . pipe ( map ( ( ) => {
103- return searchObjectsRD ;
104- } ) ) ;
118+ return this . fetchExtraData ( items ) . pipe (
119+ switchMap ( ( ) => this . fetchConfiguredAuthorizations ( searchObjectsRD , searchOptions . configuration ?? 'default' ) ) ,
120+ map ( ( ) => {
121+ return searchObjectsRD ;
122+ } ) ,
123+ ) ;
105124 }
106125 return of ( searchObjectsRD ) ;
107126 } ) ;
108127 }
109128
129+ /**
130+ * Retrieve configured authorizations related to current discovery configuration
131+ *
132+ * @param searchObjects
133+ * @param configuration
134+ * @protected
135+ */
136+ protected fetchConfiguredAuthorizations < T extends DSpaceObject > ( searchObjects : RemoteData < SearchObjects < T > > , configuration : string ) : Observable < any > {
137+ const objects = searchObjects . payload . page . map ( ( searchResult ) => searchResult . indexableObject ) as any ;
138+ const mappedObjects = this . getConfiguredAuthorizationsMap ( objects , configuration ) ;
139+
140+ if ( [ ...mappedObjects . keys ( ) ] . length === 0 ) {
141+ return of ( searchObjects ) ;
142+ }
143+
144+ const requestsIds = [ ] ;
145+
146+ const uiidListsMappedToAuthorizations = this . groupItemsUuidsByAuthorizations ( objects , mappedObjects ) ;
147+ [ ...uiidListsMappedToAuthorizations . keys ( ) ] . forEach ( ( features ) => {
148+ const uuidList = uiidListsMappedToAuthorizations . get ( features ) ;
149+ const type = objects . find ( object => object . id === uuidList [ 0 ] ) . uniqueType ;
150+ const hrefs = objects . map ( dso => dso . self ) ;
151+ this . authorizationService . initStateForObjects ( uuidList , type , features , hrefs ) ;
152+ requestsIds . push ( getRequestIdFromParams ( type , uuidList , features ) ) ;
153+ } ) ;
154+
155+ return combineLatest ( requestsIds . map ( id => this . authorizationService . isRequestLoading ( id ) ) ) . pipe (
156+ filter ( loadingItems => loadingItems . every ( loading => ! loading ) ) ,
157+ take ( 1 ) ,
158+ map ( ( ) => {
159+ return searchObjects ;
160+ } ) ,
161+ ) ;
162+ }
163+
164+ /**
165+ * Group items by authorization ID in a map
166+ *
167+ * @param objects
168+ * @param mappedEntities
169+ * @private
170+ */
171+ private groupItemsUuidsByAuthorizations < T extends DSpaceObject > ( objects : T [ ] , mappedEntities : Map < string , FeatureID [ ] > ) : Map < FeatureID [ ] , string [ ] > {
172+ const mappedUuidListsToFeatures = new Map ( ) ;
173+
174+ objects . forEach ( object => {
175+ const objectType = object . uniqueType ;
176+ const features = mappedEntities . get ( objectType ) ;
177+
178+ if ( hasValue ( features ) && hasValue ( mappedUuidListsToFeatures . get ( features ) ) ) {
179+ mappedUuidListsToFeatures . set ( features , [ ...mappedUuidListsToFeatures . get ( features ) , object . id ] ) ;
180+ } else if ( hasValue ( features ) ) {
181+ mappedUuidListsToFeatures . set ( features , [ object . id ] ) ;
182+ }
183+ } ) ;
184+
185+ return mappedUuidListsToFeatures ;
186+ }
187+
188+ /**
189+ * Map entity types oe unique type to required authorizations so that we can group the items by feature
190+ *
191+ * @param objects
192+ * @param configuration
193+ * @private
194+ */
195+ private getConfiguredAuthorizationsMap < T extends DSpaceObject > ( objects : T [ ] , configuration : string ) : Map < string , FeatureID [ ] > {
196+ const configuredAuthorizationsForDiscovery =
197+ this . appConfig . discoveryAuthorizationFeaturesConfig [ configuration ] ?? this . appConfig . discoveryAuthorizationFeaturesConfig . default ;
198+ const configuredAuthorizationsToType = new Map ( ) ;
199+
200+ if ( ! hasValue ( configuredAuthorizationsForDiscovery ) ) {
201+ return configuredAuthorizationsToType ;
202+ }
203+
204+ const objectUniqueTypes = [ ...new Set ( objects . map ( dso => dso ?. uniqueType ) ) ] ;
205+
206+ objectUniqueTypes . forEach ( ( entity ) => {
207+ const config = configuredAuthorizationsForDiscovery [ entity ] ;
208+
209+ if ( hasValue ( config ) ) {
210+ configuredAuthorizationsToType . set ( entity , config ) ;
211+ }
212+ } ) ;
213+
214+ return configuredAuthorizationsToType ;
215+ }
216+
217+
110218 protected fetchExtraData < T extends DSpaceObject > ( objects : T [ ] ) : Observable < any > {
111219
112220 const items : Item [ ] = objects
0 commit comments