94207: Fix ResourcePolicyService.updateTarget

Follow "await invalidation" pattern ~ DataService.deleteByHref
This commit is contained in:
Yura Bondarenko
2022-09-02 12:16:30 +02:00
parent a8cf6df03f
commit 47b9b09139
2 changed files with 94 additions and 21 deletions

View File

@@ -15,12 +15,13 @@ import { ActionType } from './models/action-type.model';
import { RequestParam } from '../cache/models/request-param.model';
import { PageInfo } from '../shared/page-info.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 { RequestEntry } from '../data/request-entry.model';
import { FindListOptions } from '../data/find-list-options.model';
import { EPersonDataService } from '../eperson/eperson-data.service';
import { GroupDataService } from '../eperson/group-data.service';
import { RestRequestMethod } from '../data/rest-request-method';
describe('ResourcePolicyService', () => {
let scheduler: TestScheduler;
@@ -128,6 +129,14 @@ describe('ResourcePolicyService', () => {
getBrowseEndpoint: hot('a', {
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;
const notificationsService = {} as NotificationsService;
@@ -153,6 +162,7 @@ describe('ResourcePolicyService', () => {
spyOn((service as any).dataService, 'findByHref').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, 'invalidateByHref').and.returnValue(observableOf(requestURL));
});
describe('create', () => {
@@ -336,14 +346,56 @@ describe('ResourcePolicyService', () => {
});
describe('updateTarget', () => {
it('should create a new PUT request for eperson', () => {
const targetType = 'eperson';
const result = service.updateTarget(resourcePolicyId, requestURL, epersonUUID, targetType);
const expected = cold('a|', {
a: resourcePolicyRD
beforeEach(() => {
scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, epersonUUID));
});
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));
});
});

View File

@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { AsyncSubject, Observable } from 'rxjs';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
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 { RequestParam } from '../cache/models/request-param.model';
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 { getFirstCompletedRemoteData } from '../shared/operators';
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 { GroupDataService } from '../eperson/group-data.service';
/**
* 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
*/
updateTarget(resourcePolicyId: string, resourcePolicyHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
const targetService = targetType === 'eperson' ? this.ePersonService : this.groupService;
const targetEndpoint$ = targetService.getBrowseEndpoint().pipe(
take(1),
map((endpoint: string) =>`${endpoint}/${targetUUID}`),
);
const targetEndpoint$ = targetService.getIDHrefObs(targetUUID);
const options: HttpOptions = Object.create({});
let headers = new HttpHeaders();
@@ -256,9 +250,9 @@ export class ResourcePolicyService {
const requestId = this.requestService.generateRequestId();
this.requestService.setStaleByHrefSubstring(`${this.dataService.getLinkPath()}/${resourcePolicyId}/${targetType}`);
targetEndpoint$.subscribe((targetEndpoint) => {
targetEndpoint$.pipe(
first(),
).subscribe((targetEndpoint) => {
const resourceEndpoint = resourcePolicyHref + '/' + targetType;
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
Object.assign(request, {
@@ -269,8 +263,35 @@ export class ResourcePolicyService {
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];
}
})
);
}
}