diff --git a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts index 30c490a45c..07b5a00ea3 100644 --- a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts +++ b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts @@ -14,12 +14,14 @@ import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-d import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { RestResponse } from '../../../../core/cache/response.models'; import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; +import { DSpaceObjectDataService } from '../../../../core/data/dspace-object-data.service'; import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { Group } from '../../../../core/eperson/models/group.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { UUIDService } from '../../../../core/shared/uuid.service'; @@ -41,6 +43,7 @@ describe('GroupFormComponent', () => { let builderService: FormBuilderService; let ePersonDataServiceStub: any; let groupsDataServiceStub: any; + let dsoDataServiceStub: any; let authorizationService: AuthorizationDataService; let notificationService: NotificationsServiceStub; let router; @@ -97,6 +100,11 @@ describe('GroupFormComponent', () => { authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) }); + dsoDataServiceStub = { + findByHref(href: string): Observable> { + return null; + } + } builderService = getMockFormBuilderService(); translateService = getMockTranslateService(); router = new RouterMock(); @@ -114,6 +122,7 @@ describe('GroupFormComponent', () => { providers: [GroupFormComponent, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub }, + { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, { provide: NotificationsService, useValue: notificationService }, { provide: FormBuilderService, useValue: builderService }, { provide: DSOChangeAnalyzer, useValue: {} }, diff --git a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts index 720339c924..2306c675c8 100644 --- a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts +++ b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts @@ -9,11 +9,8 @@ import { DynamicTextAreaModel } from '@ng-dynamic-forms/core'; import { TranslateService } from '@ngx-translate/core'; -import { Observable } from 'rxjs/internal/Observable'; -import { combineLatest } from 'rxjs/internal/observable/combineLatest'; -import { Subscription } from 'rxjs/internal/Subscription'; -import { ObservedValueOf } from 'rxjs/internal/types'; -import { map, switchMap, take } from 'rxjs/operators'; +import { ObservedValueOf, combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; +import { catchError, map, switchMap, take } from 'rxjs/operators'; import { getCollectionEditRolesRoute } from '../../../../+collection-page/collection-page-routing-paths'; import { getCommunityEditRolesRoute } from '../../../../+community-page/community-page-routing-paths'; import { RestResponse } from '../../../../core/cache/response.models'; @@ -106,7 +103,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { groupBeingEdited: Group; /** - * Observable whether or not the logged in user is allowed to delete the Group + * Observable whether or not the logged in user is allowed to delete the Group & doesn't have a linked object (community / collection linked to workspace group */ canEdit$: Observable; @@ -139,7 +136,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { })); this.canEdit$ = this.groupDataService.getActiveGroup().pipe( switchMap((group: Group) => { - return combineLatest( + return observableCombineLatest( this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(group) ? group.self : undefined), this.hasLinkedDSO(group), (isAuthorized: ObservedValueOf>, hasLinkedDSO: ObservedValueOf>) => { @@ -147,7 +144,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { }) }) ); - combineLatest( + observableCombineLatest( this.translateService.get(`${this.messagePrefix}.groupName`), this.translateService.get(`${this.messagePrefix}.groupDescription`) ).subscribe(([groupName, groupDescription]) => { @@ -172,7 +169,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { ]; this.formGroup = this.formBuilderService.createFormGroup(this.formModel); this.subs.push( - combineLatest( + observableCombineLatest( this.groupDataService.getActiveGroup(), this.canEdit$ ).subscribe(([activeGroup, canEdit]) => { @@ -402,7 +399,8 @@ export class GroupFormComponent implements OnInit, OnDestroy { } else { return false } - }) + }), + catchError(() => observableOf(false)), ); } } @@ -413,7 +411,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { */ getLinkedDSO(group: Group): Observable> { if (hasValue(group) && hasValue(group._links.object.href)) { - if (group.object == undefined) { + if (group.object === undefined) { return this.dSpaceObjectDataService.findByHref(group._links.object.href); } return group.object; @@ -424,7 +422,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { * Get the route to the edit roles tab of the group's linked object (community or collection linked to a workflow group) if it has one * @param group */ - getLinkedEditRolesRoute(group: Group): Observable { + getLinkedEditRolesRoute(group: Group): Observable { if (hasValue(group) && hasValue(group._links.object.href)) { return this.getLinkedDSO(group).pipe( map((rd: RemoteData) => { diff --git a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.spec.ts b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.spec.ts index 9ed7cf473b..1d091e75d1 100644 --- a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.spec.ts +++ b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.spec.ts @@ -7,6 +7,7 @@ import { Router } from '@angular/router'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { Observable, of as observableOf } from 'rxjs'; +import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { PaginatedList } from '../../../core/data/paginated-list'; import { RemoteData } from '../../../core/data/remote-data'; @@ -16,6 +17,7 @@ import { GroupDataService } from '../../../core/eperson/group-data.service'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { Group } from '../../../core/eperson/models/group.model'; import { RouteService } from '../../../core/services/route.service'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { GroupMock, GroupMock2 } from '../../../shared/testing/group-mock'; @@ -32,6 +34,7 @@ describe('GroupRegistryComponent', () => { let fixture: ComponentFixture; let ePersonDataServiceStub: any; let groupsDataServiceStub: any; + let dsoDataServiceStub: any; let authorizationService: AuthorizationDataService; let mockGroups; @@ -80,6 +83,11 @@ describe('GroupRegistryComponent', () => { return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo({ elementsPerPage: [result].length, totalElements: [result].length, totalPages: 1, currentPage: 1 }), [result])); } }; + dsoDataServiceStub = { + findByHref(href: string): Observable> { + return createSuccessfulRemoteDataObject$(undefined); + } + } authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) }); @@ -96,6 +104,7 @@ describe('GroupRegistryComponent', () => { providers: [GroupsRegistryComponent, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub }, + { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: RouteService, useValue: routeServiceStub }, { provide: Router, useValue: new RouterMock() }, diff --git a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.ts b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.ts index 4148b3ed30..d4a1374cfb 100644 --- a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.ts +++ b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.ts @@ -2,11 +2,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription, Observable, of as observableOf } from 'rxjs'; import { filter } from 'rxjs/internal/operators/filter'; -import { Subscription } from 'rxjs/internal/Subscription'; import { ObservedValueOf } from 'rxjs/internal/types'; -import { map, switchMap, take } from 'rxjs/operators'; +import { catchError, map, switchMap, take } from 'rxjs/operators'; import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; @@ -125,8 +124,8 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { this.subs.push(this.groups$.pipe( getAllSucceededRemoteDataPayload(), switchMap((groups: PaginatedList) => { - return combineLatest(...groups.page.map((group: Group) => { - return combineLatest( + return observableCombineLatest(...groups.page.map((group: Group) => { + return observableCombineLatest( this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(group) ? group.self : undefined), this.hasLinkedDSO(group), (isAuthorized: ObservedValueOf>, hasLinkedDSO: ObservedValueOf>) => { @@ -199,17 +198,15 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { * @param group */ hasLinkedDSO(group: Group): Observable { - if (group.object == undefined) { - group.object = this.dSpaceObjectDataService.findByHref(group._links.object.href); - } - return group.object.pipe( + return this.dSpaceObjectDataService.findByHref(group._links.object.href).pipe( map((rd: RemoteData) => { if (hasValue(rd) && hasValue(rd.payload)) { return true; } else { return false } - }) + }), + catchError(() => observableOf(false)), ); } diff --git a/src/app/core/data/feature-authorization/authorization-data.service.ts b/src/app/core/data/feature-authorization/authorization-data.service.ts index 4dfa89cde6..ad9b724040 100644 --- a/src/app/core/data/feature-authorization/authorization-data.service.ts +++ b/src/app/core/data/feature-authorization/authorization-data.service.ts @@ -20,7 +20,7 @@ import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link- import { Observable } from 'rxjs/internal/Observable'; import { RemoteData } from '../remote-data'; import { PaginatedList } from '../paginated-list'; -import { find, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, find, map, switchMap, tap } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../../../shared/empty.util'; import { RequestParam } from '../../cache/models/request-param.model'; import { AuthorizationSearchParams } from './authorization-search-params'; @@ -71,6 +71,7 @@ export class AuthorizationDataService extends DataService { return []; } }), + catchError(() => observableOf(false)), oneAuthorizationMatchesFeature(featureId) ); } diff --git a/src/app/shared/testing/group-mock.ts b/src/app/shared/testing/group-mock.ts index d0ee135b98..24a78a58e5 100644 --- a/src/app/shared/testing/group-mock.ts +++ b/src/app/shared/testing/group-mock.ts @@ -12,6 +12,7 @@ export const GroupMock2: Group = Object.assign(new Group(), { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid2', }, subgroups: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid2/subgroups' }, + object: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid2/object' }, epersons: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid2/epersons' } }, _name: 'testgroupname2', @@ -31,6 +32,7 @@ export const GroupMock: Group = Object.assign(new Group(), { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid', }, subgroups: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/subgroups' }, + object: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid2/object' }, epersons: { href: 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/epersons' } }, _name: 'testgroupname',