import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { createSelector, select, Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { filter, map, take } from 'rxjs/operators'; import { GroupRegistryCancelGroupAction, GroupRegistryEditGroupAction } from '../../+admin/admin-access-control/group-registry/group-registry.actions'; import { GroupRegistryState } from '../../+admin/admin-access-control/group-registry/group-registry.reducers'; import { AppState } from '../../app.reducer'; import { hasValue } from '../../shared/empty.util'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { SearchParam } from '../cache/models/search-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RestResponse } from '../cache/response.models'; import { DataService } from '../data/data.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { PaginatedList } from '../data/paginated-list'; import { RemoteData } from '../data/remote-data'; import { DeleteRequest, FindListOptions, FindListRequest, PostRequest, RestRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { getResponseFromEntry } from '../shared/operators'; import { EPerson } from './models/eperson.model'; import { Group } from './models/group.model'; import { dataService } from '../cache/builders/build-decorators'; import { GROUP } from './models/group.resource-type'; const groupRegistryStateSelector = (state: AppState) => state.groupRegistry; const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup); /** * Provides methods to retrieve eperson group resources from the REST API & Group related CRUD actions. */ @Injectable({ providedIn: 'root' }) @dataService(GROUP) export class GroupDataService extends DataService { protected linkPath = 'groups'; protected browseEndpoint = ''; protected ePersonsEndpoint = 'epersons'; protected subgroupsEndpoint = 'subgroups'; constructor( protected comparator: DSOChangeAnalyzer, protected http: HttpClient, protected notificationsService: NotificationsService, protected requestService: RequestService, protected rdbService: RemoteDataBuildService, protected store: Store, protected objectCache: ObjectCacheService, protected halService: HALEndpointService ) { super(); } /** * Retrieves all groups * @param pagination The pagination info used to retrieve the groups */ public getGroups(options: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { const hrefObs = this.getFindAllHref(options, this.linkPath, ...linksToFollow); hrefObs.pipe( filter((href: string) => hasValue(href)), take(1)) .subscribe((href: string) => { const request = new FindListRequest(this.requestService.generateRequestId(), href, options); this.requestService.configure(request); }); return this.rdbService.buildList(hrefObs) as Observable>>; } /** * Returns a search result list of groups, with certain query (searches in group name and by exact uuid) * Endpoint used: /eperson/groups/search/byMetadata?query=<:name> * @param query search query param * @param options * @param linksToFollow */ public searchGroups(query: string, options?: FindListOptions, ...linksToFollow: Array>): Observable>> { const searchParams = [new SearchParam('query', query)]; let findListOptions = new FindListOptions(); if (options) { findListOptions = Object.assign(new FindListOptions(), options); } if (findListOptions.searchParams) { findListOptions.searchParams = [...findListOptions.searchParams, ...searchParams]; } else { findListOptions.searchParams = searchParams; } return this.searchBy('byMetadata', findListOptions, ...linksToFollow); } /** * Check if the current user is member of to the indicated group * * @param groupName * the group name * @return boolean * true if user is member of the indicated group, false otherwise */ isMemberOf(groupName: string): Observable { const searchHref = 'isMemberOf'; const options = new FindListOptions(); options.searchParams = [new SearchParam('groupName', groupName)]; return this.searchBy(searchHref, options).pipe( filter((groups: RemoteData>) => !groups.isResponsePending), take(1), map((groups: RemoteData>) => groups.payload.totalElements > 0) ); } /** * Method to delete a group * @param id The group id to delete */ public deleteGroup(group: Group): Observable { return this.delete(group); } /** * Create or Update a group * If the group contains an id, it is assumed the eperson already exists and is updated instead * //TODO * @param group The group to create or update */ public createOrUpdateGroup(group: Group): Observable> { const isUpdate = hasValue(group.id); if (isUpdate) { return this.updateGroup(group); } else { return this.create(group, null); } } /** * // TODO * @param {DSpaceObject} ePerson The given object */ updateGroup(group: Group): Observable> { // TODO return null; } /** * Adds given subgroup as a subgroup to the given active group * @param activeGroup Group we want to add subgroup to * @param subgroup Group we want to add as subgroup to activeGroup */ addSubGroupToGroup(activeGroup: Group, subgroup: Group): Observable { const requestId = this.requestService.generateRequestId(); const options: HttpOptions = Object.create({}); let headers = new HttpHeaders(); headers = headers.append('Content-Type', 'text/uri-list'); options.headers = headers; const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint, subgroup.self, options); this.requestService.configure(postRequest); return this.fetchResponse(requestId); } /** * Deletes a given subgroup from the subgroups of the given active group * @param activeGroup Group we want to delete subgroup from * @param subgroup Subgroup we want to delete from activeGroup */ deleteSubGroupFromGroup(activeGroup: Group, subgroup: Group): Observable { const requestId = this.requestService.generateRequestId(); const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint + '/' + subgroup.id); this.requestService.configure(deleteRequest); return this.fetchResponse(requestId); } /** * Adds given ePerson as member to given group * @param activeGroup Group we want to add member to * @param ePerson EPerson we want to add as member to given activeGroup */ addMemberToGroup(activeGroup: Group, ePerson: EPerson): Observable { const requestId = this.requestService.generateRequestId(); const options: HttpOptions = Object.create({}); let headers = new HttpHeaders(); headers = headers.append('Content-Type', 'text/uri-list'); options.headers = headers; const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint, ePerson.self, options); this.requestService.configure(postRequest); return this.fetchResponse(requestId); } /** * Deletes a given ePerson from the members of the given active group * @param activeGroup Group we want to delete member from * @param ePerson EPerson we want to delete from members of given activeGroup */ deleteMemberFromGroup(activeGroup: Group, ePerson: EPerson): Observable { const requestId = this.requestService.generateRequestId(); const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint + '/' + ePerson.id); this.requestService.configure(deleteRequest); return this.fetchResponse(requestId); } /** * Gets the restResponse from the requestService * @param requestId */ protected fetchResponse(requestId: string): Observable { return this.requestService.getByUUID(requestId).pipe( getResponseFromEntry(), map((response: RestResponse) => { return response; }) ); } /** * Method to retrieve the group that is currently being edited */ public getActiveGroup(): Observable { return this.store.pipe(select(editGroupSelector)) } /** * Method to cancel editing a group, dispatches a cancel group action */ public cancelEditGroup() { this.store.dispatch(new GroupRegistryCancelGroupAction()); } /** * Method to set the group being edited, dispatches an edit group action * @param group The group to edit */ public editGroup(group: Group) { this.store.dispatch(new GroupRegistryEditGroupAction(group)); } /** * Method that clears a cached groups request and returns its REST url */ public clearGroupsRequests(): void { this.getBrowseEndpoint().pipe(take(1)).subscribe((link: string) => { this.requestService.removeByHrefSubstring(link); }); } public getGroupRegistryRouterLink(): string { return '/admin/access-control/groups'; } public getGroupEditPageRouterLink(groupId: string): string { return '/admin/access-control/groups/' + groupId; } }