mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
94207: Fix ResourcePolicyService.updateTarget
Follow "await invalidation" pattern ~ DataService.deleteByHref
This commit is contained in:
@@ -15,12 +15,13 @@ import { ActionType } from './models/action-type.model';
|
|||||||
import { RequestParam } from '../cache/models/request-param.model';
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { buildPaginatedList } from '../data/paginated-list.model';
|
import { buildPaginatedList } from '../data/paginated-list.model';
|
||||||
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
import { createPendingRemoteDataObject, createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { RequestEntry } from '../data/request-entry.model';
|
import { RequestEntry } from '../data/request-entry.model';
|
||||||
import { FindListOptions } from '../data/find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
import { EPersonDataService } from '../eperson/eperson-data.service';
|
import { EPersonDataService } from '../eperson/eperson-data.service';
|
||||||
import { GroupDataService } from '../eperson/group-data.service';
|
import { GroupDataService } from '../eperson/group-data.service';
|
||||||
|
import { RestRequestMethod } from '../data/rest-request-method';
|
||||||
|
|
||||||
describe('ResourcePolicyService', () => {
|
describe('ResourcePolicyService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
@@ -128,6 +129,14 @@ describe('ResourcePolicyService', () => {
|
|||||||
getBrowseEndpoint: hot('a', {
|
getBrowseEndpoint: hot('a', {
|
||||||
a: ePersonEndpoint
|
a: ePersonEndpoint
|
||||||
}),
|
}),
|
||||||
|
getIDHrefObs: cold('a', {
|
||||||
|
a: 'https://rest.api/rest/api/eperson/epersons/' + epersonUUID
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
groupService = jasmine.createSpyObj('groupService', {
|
||||||
|
getIDHrefObs: cold('a', {
|
||||||
|
a: 'https://rest.api/rest/api/eperson/groups/' + groupUUID
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
objectCache = {} as ObjectCacheService;
|
objectCache = {} as ObjectCacheService;
|
||||||
const notificationsService = {} as NotificationsService;
|
const notificationsService = {} as NotificationsService;
|
||||||
@@ -153,6 +162,7 @@ describe('ResourcePolicyService', () => {
|
|||||||
spyOn((service as any).dataService, 'findByHref').and.callThrough();
|
spyOn((service as any).dataService, 'findByHref').and.callThrough();
|
||||||
spyOn((service as any).dataService, 'searchBy').and.callThrough();
|
spyOn((service as any).dataService, 'searchBy').and.callThrough();
|
||||||
spyOn((service as any).dataService, 'getSearchByHref').and.returnValue(observableOf(requestURL));
|
spyOn((service as any).dataService, 'getSearchByHref').and.returnValue(observableOf(requestURL));
|
||||||
|
spyOn((service as any).dataService, 'invalidateByHref').and.returnValue(observableOf(requestURL));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
@@ -336,14 +346,56 @@ describe('ResourcePolicyService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('updateTarget', () => {
|
describe('updateTarget', () => {
|
||||||
it('should create a new PUT request for eperson', () => {
|
beforeEach(() => {
|
||||||
const targetType = 'eperson';
|
scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, epersonUUID));
|
||||||
|
|
||||||
const result = service.updateTarget(resourcePolicyId, requestURL, epersonUUID, targetType);
|
|
||||||
const expected = cold('a|', {
|
|
||||||
a: resourcePolicyRD
|
|
||||||
});
|
});
|
||||||
expect(result).toBeObservable(expected);
|
|
||||||
|
it('should send a PUT request to update the EPerson', () => {
|
||||||
|
service.updateTarget(resourcePolicyId, requestURL, epersonUUID, 'eperson');
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||||
|
method: RestRequestMethod.PUT,
|
||||||
|
uuid: requestUUID,
|
||||||
|
href: `${resourcePolicy._links.self.href}/eperson`,
|
||||||
|
body: 'https://rest.api/rest/api/eperson/epersons/' + epersonUUID,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send a PUT request to update the Group', () => {
|
||||||
|
service.updateTarget(resourcePolicyId, requestURL, groupUUID, 'group');
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||||
|
method: RestRequestMethod.PUT,
|
||||||
|
uuid: requestUUID,
|
||||||
|
href: `${resourcePolicy._links.self.href}/group`,
|
||||||
|
body: 'https://rest.api/rest/api/eperson/groups/' + groupUUID,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate the ResourcePolicy if the PUT request succeeds', () => {
|
||||||
|
service.updateTarget(resourcePolicyId, requestURL, epersonUUID, 'eperson');
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect((service as any).dataService.invalidateByHref).toHaveBeenCalledWith(resourcePolicy._links.self.href);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only emit when invalidation is complete', () => {
|
||||||
|
const RD = {
|
||||||
|
p: createPendingRemoteDataObject(),
|
||||||
|
s: createSuccessfulRemoteDataObject({}),
|
||||||
|
};
|
||||||
|
const response$ = cold('(s|)', RD);
|
||||||
|
const invalidate$ = cold('--(d|)', { d: true });
|
||||||
|
|
||||||
|
(rdbService.buildFromRequestUUID as any).and.returnValue(response$);
|
||||||
|
((service as any).dataService.invalidateByHref).and.returnValue(invalidate$);
|
||||||
|
|
||||||
|
const out$ = service.updateTarget(resourcePolicyId, requestURL, groupUUID, 'group');
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(out$).toBeObservable(cold('--(s|)', RD));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs';
|
import { AsyncSubject, Observable } from 'rxjs';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { dataService } from '../cache/builders/build-decorators';
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import { PaginatedList } from '../data/paginated-list.model';
|
|||||||
import { ActionType } from './models/action-type.model';
|
import { ActionType } from './models/action-type.model';
|
||||||
import { RequestParam } from '../cache/models/request-param.model';
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { filter, first, map, switchMap } from 'rxjs/operators';
|
||||||
import { NoContent } from '../shared/NoContent.model';
|
import { NoContent } from '../shared/NoContent.model';
|
||||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
import { CoreState } from '../core-state.model';
|
import { CoreState } from '../core-state.model';
|
||||||
@@ -37,7 +37,6 @@ import { HALLink } from '../shared/hal-link.model';
|
|||||||
import { EPersonDataService } from '../eperson/eperson-data.service';
|
import { EPersonDataService } from '../eperson/eperson-data.service';
|
||||||
import { GroupDataService } from '../eperson/group-data.service';
|
import { GroupDataService } from '../eperson/group-data.service';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A private DataService implementation to delegate specific methods to.
|
* A private DataService implementation to delegate specific methods to.
|
||||||
*/
|
*/
|
||||||
@@ -241,13 +240,8 @@ export class ResourcePolicyService {
|
|||||||
* @param targetType the type of the target (eperson or group) to which the permission is being granted
|
* @param targetType the type of the target (eperson or group) to which the permission is being granted
|
||||||
*/
|
*/
|
||||||
updateTarget(resourcePolicyId: string, resourcePolicyHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
|
updateTarget(resourcePolicyId: string, resourcePolicyHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
|
||||||
|
|
||||||
const targetService = targetType === 'eperson' ? this.ePersonService : this.groupService;
|
const targetService = targetType === 'eperson' ? this.ePersonService : this.groupService;
|
||||||
|
const targetEndpoint$ = targetService.getIDHrefObs(targetUUID);
|
||||||
const targetEndpoint$ = targetService.getBrowseEndpoint().pipe(
|
|
||||||
take(1),
|
|
||||||
map((endpoint: string) =>`${endpoint}/${targetUUID}`),
|
|
||||||
);
|
|
||||||
|
|
||||||
const options: HttpOptions = Object.create({});
|
const options: HttpOptions = Object.create({});
|
||||||
let headers = new HttpHeaders();
|
let headers = new HttpHeaders();
|
||||||
@@ -256,9 +250,9 @@ export class ResourcePolicyService {
|
|||||||
|
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
this.requestService.setStaleByHrefSubstring(`${this.dataService.getLinkPath()}/${resourcePolicyId}/${targetType}`);
|
targetEndpoint$.pipe(
|
||||||
|
first(),
|
||||||
targetEndpoint$.subscribe((targetEndpoint) => {
|
).subscribe((targetEndpoint) => {
|
||||||
const resourceEndpoint = resourcePolicyHref + '/' + targetType;
|
const resourceEndpoint = resourcePolicyHref + '/' + targetType;
|
||||||
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
|
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
|
||||||
Object.assign(request, {
|
Object.assign(request, {
|
||||||
@@ -269,8 +263,35 @@ export class ResourcePolicyService {
|
|||||||
this.requestService.send(request);
|
this.requestService.send(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
const response$ = this.rdbService.buildFromRequestUUID(requestId);
|
||||||
|
|
||||||
|
const invalidated$ = new AsyncSubject<boolean>();
|
||||||
|
response$.pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
switchMap((rd: RemoteData<any>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
return this.dataService.invalidateByHref(resourcePolicyHref);
|
||||||
|
} else {
|
||||||
|
return [undefined];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
).subscribe(() => {
|
||||||
|
invalidated$.next(true);
|
||||||
|
invalidated$.complete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return response$.pipe(
|
||||||
|
switchMap((rd: RemoteData<NoContent>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
return invalidated$.pipe(
|
||||||
|
filter((invalidated: boolean) => invalidated),
|
||||||
|
map(() => rd)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [rd];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user