Fixes after merge with master

This commit is contained in:
Giuseppe Digilio
2019-02-19 17:46:34 +01:00
parent 880d9ed069
commit f6fe2c3c3e
34 changed files with 164 additions and 238 deletions

View File

@@ -1,4 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { GroupEpersonService } from '../core/eperson/group-eperson.service';
@Component({ @Component({
selector: 'ds-home-page', selector: 'ds-home-page',
@@ -6,4 +7,12 @@ import { Component } from '@angular/core';
templateUrl: './home-page.component.html' templateUrl: './home-page.component.html'
}) })
export class HomePageComponent { export class HomePageComponent {
constructor(private s: GroupEpersonService) {}
ngOnInit() {
this.s.findById('11cc35e5-a11d-4b64-b5b9-0052a5d15509')
.subscribe((r) => {
console.log(r);
})
}
} }

View File

@@ -1,20 +1,36 @@
import { AuthStatusResponse } from '../cache/response.models'; import { async, TestBed } from '@angular/core/testing';
import { Store, StoreModule } from '@ngrx/store';
import { GlobalConfig } from '../../../config/global-config.interface';
import { AuthStatusResponse } from '../cache/response.models';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { AuthStatus } from './models/auth-status.model'; import { AuthStatus } from './models/auth-status.model';
import { AuthResponseParsingService } from './auth-response-parsing.service'; import { AuthResponseParsingService } from './auth-response-parsing.service';
import { AuthGetRequest, AuthPostRequest } from '../data/request.models'; import { AuthGetRequest, AuthPostRequest } from '../data/request.models';
import { MockStore } from '../../shared/testing/mock-store'; import { MockStore } from '../../shared/testing/mock-store';
import { ObjectCacheState } from '../cache/object-cache.reducer';
describe('AuthResponseParsingService', () => { describe('AuthResponseParsingService', () => {
let service: AuthResponseParsingService; let service: AuthResponseParsingService;
const EnvConfig = { cache: { msToLive: 1000 } } as any; const EnvConfig: GlobalConfig = { cache: { msToLive: 1000 } } as any;
const store = new MockStore<ObjectCacheState>({}); let store: any;
const objectCacheService = new ObjectCacheService(store as any); let objectCacheService: ObjectCacheService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
],
providers: [
{ provide: Store, useClass: MockStore }
]
}).compileComponents();
}));
beforeEach(() => { beforeEach(() => {
store = TestBed.get(Store);
objectCacheService = new ObjectCacheService(store as any);
service = new AuthResponseParsingService(EnvConfig, objectCacheService); service = new AuthResponseParsingService(EnvConfig, objectCacheService);
}); });

View File

@@ -1,20 +1,17 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { provideMockActions } from '@ngrx/effects/testing'; import { provideMockActions } from '@ngrx/effects/testing';
import { cold, hot } from 'jasmine-marbles'; import { cold, hot } from 'jasmine-marbles';
import { ServerSyncBufferEffects } from './server-sync-buffer.effects'; import { ServerSyncBufferEffects } from './server-sync-buffer.effects';
import { GLOBAL_CONFIG } from '../../../config'; import { GLOBAL_CONFIG } from '../../../config';
import { import { CommitSSBAction, EmptySSBAction, ServerSyncBufferActionTypes } from './server-sync-buffer.actions';
CommitSSBAction,
EmptySSBAction,
ServerSyncBufferActionTypes
} from './server-sync-buffer.actions';
import { RestRequestMethod } from '../data/rest-request-method'; import { RestRequestMethod } from '../data/rest-request-method';
import { Store } from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { ObjectCacheService } from './object-cache.service'; import { ObjectCacheService } from './object-cache.service';
import { MockStore } from '../../shared/testing/mock-store'; import { MockStore } from '../../shared/testing/mock-store';
import { ObjectCacheState } from './object-cache.reducer';
import * as operators from 'rxjs/operators'; import * as operators from 'rxjs/operators';
import { spyOnOperator } from '../../shared/testing/utils'; import { spyOnOperator } from '../../shared/testing/utils';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
@@ -38,8 +35,10 @@ describe('ServerSyncBufferEffects', () => {
let store; let store;
beforeEach(() => { beforeEach(() => {
store = new MockStore<ObjectCacheState>({});
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
],
providers: [ providers: [
ServerSyncBufferEffects, ServerSyncBufferEffects,
provideMockActions(() => actions), provideMockActions(() => actions),
@@ -54,11 +53,12 @@ describe('ServerSyncBufferEffects', () => {
} }
} }
}, },
{ provide: Store, useValue: store } { provide: Store, useClass: MockStore }
// other providers // other providers
], ],
}); });
store = TestBed.get(Store);
ssbEffects = TestBed.get(ServerSyncBufferEffects); ssbEffects = TestBed.get(ServerSyncBufferEffects);
}); });

View File

@@ -6,7 +6,6 @@ import { ConfigRequest, FindAllOptions, RestRequest } from '../data/request.mode
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ConfigData } from './config-data'; import { ConfigData } from './config-data';
import { RequestEntry } from '../data/request.reducer';
import { getResponseFromEntry } from '../shared/operators'; import { getResponseFromEntry } from '../shared/operators';
export abstract class ConfigService { export abstract class ConfigService {

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ConfigService } from './config.service'; import { ConfigService } from './config.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ObjectCacheService } from '../cache/object-cache.service';
/** /**
* Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process.
@@ -13,7 +13,7 @@ export class SubmissionUploadsConfigService extends ConfigService {
protected browseEndpoint = ''; protected browseEndpoint = '';
constructor( constructor(
protected responseCache: ResponseCacheService, protected objectCache: ObjectCacheService,
protected requestService: RequestService, protected requestService: RequestService,
protected halService: HALEndpointService) { protected halService: HALEndpointService) {
super(); super();

View File

@@ -1,4 +1,3 @@
import { distinctUntilChanged, filter, first, map } from 'rxjs/operators';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { import {
distinctUntilChanged, distinctUntilChanged,
@@ -10,10 +9,8 @@ import {
switchMap, switchMap,
take take
} from 'rxjs/operators'; } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';

View File

@@ -9,7 +9,7 @@ import {
import { GetRequest } from './request.models'; import { GetRequest } from './request.models';
import { RestResponse } from '../cache/response.models'; import { RestResponse } from '../cache/response.models';
const response = new RestResponse(true, 'OK'); const response = new RestResponse(true, 200, 'OK');
class NullAction extends RequestCompleteAction { class NullAction extends RequestCompleteAction {
type = null; type = null;
payload = null; payload = null;
@@ -89,8 +89,8 @@ describe('requestReducer', () => {
expect(newState[id1].requestPending).toEqual(state[id1].requestPending); expect(newState[id1].requestPending).toEqual(state[id1].requestPending);
expect(newState[id1].responsePending).toEqual(false); expect(newState[id1].responsePending).toEqual(false);
expect(newState[id1].completed).toEqual(true); expect(newState[id1].completed).toEqual(true);
expect(newState[id1].response.isSuccessful).toEqual(response.isSuccessful) expect(newState[id1].response.isSuccessful).toEqual(response.isSuccessful);
expect(newState[id1].response.statusCode).toEqual(response.statusCode) expect(newState[id1].response.statusCode).toEqual(response.statusCode);
expect(newState[id1].response.timeAdded).toBeTruthy() expect(newState[id1].response.timeAdded).toBeTruthy()
}); });

View File

@@ -289,14 +289,6 @@ describe('RequestService', () => {
}); });
}); });
describe('when forceBypassCache is true', () => {
it('should call clearRequestsOnTheirWayToTheStore method', () => {
spyOn(serviceAsAny, 'clearRequestsOnTheirWayToTheStore');
service.configure(testPostRequest, true);
expect(serviceAsAny.clearRequestsOnTheirWayToTheStore).toHaveBeenCalledWith(testPostRequest.href);
});
});
}); });
describe('isCachedOrPending', () => { describe('isCachedOrPending', () => {
@@ -467,37 +459,6 @@ describe('RequestService', () => {
}); });
}); });
describe('clearRequestsOnTheirWayToTheStore', () => {
let request: GetRequest;
beforeEach(() => {
request = testPatchRequest;
});
describe('when there is no request entry', () => {
it('should remove response from cache', () => {
spyOn(service, 'getByHref').and.returnValue(observableOf(undefined));
serviceAsAny.clearRequestsOnTheirWayToTheStore(request.href);
expect(responseCache.remove).toHaveBeenCalledWith(request.href);
});
});
describe('when there is a request entry and is not pending', () => {
it('should remove response from cache and stop tracking the request', () => {
spyOn(service, 'getByHref').and.returnValue(observableOf({responsePending: false}));
serviceAsAny.clearRequestsOnTheirWayToTheStore(request.href);
expect(responseCache.remove).toHaveBeenCalledWith(request.href);
expect(serviceAsAny.requestsOnTheirWayToTheStore.includes(request.href)).toBeFalsy();
});
});
});
describe('isReusable', () => { describe('isReusable', () => {
describe('when the given UUID is has no value', () => { describe('when the given UUID is has no value', () => {
let reusable; let reusable;

View File

@@ -1,8 +1,7 @@
import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs';
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { remove } from 'lodash'; import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs';
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
import { MemoizedSelector, select, Store } from '@ngrx/store'; import { MemoizedSelector, select, Store } from '@ngrx/store';
import { hasNoValue, hasValue } from '../../shared/empty.util'; import { hasNoValue, hasValue } from '../../shared/empty.util';
@@ -15,7 +14,6 @@ import { pathSelector } from '../shared/selectors';
import { UUIDService } from '../shared/uuid.service'; import { UUIDService } from '../shared/uuid.service';
import { RequestConfigureAction, RequestExecuteAction } from './request.actions'; import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
import { GetRequest, RestRequest } from './request.models'; import { GetRequest, RestRequest } from './request.models';
import { RequestEntry } from './request.reducer'; import { RequestEntry } from './request.reducer';
import { CommitSSBAction } from '../cache/server-sync-buffer.actions'; import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
import { RestRequestMethod } from './rest-request-method'; import { RestRequestMethod } from './rest-request-method';
@@ -70,6 +68,7 @@ export class RequestService {
this.store.pipe( this.store.pipe(
select(this.originalUUIDFromUUIDSelector(uuid)), select(this.originalUUIDFromUUIDSelector(uuid)),
mergeMap((originalUUID) => { mergeMap((originalUUID) => {
console.log(originalUUID);
return this.store.pipe(select(this.entryFromUUIDSelector(originalUUID))) return this.store.pipe(select(this.entryFromUUIDSelector(originalUUID)))
}, },
)) ))
@@ -83,19 +82,6 @@ export class RequestService {
); );
} }
private clearRequestsOnTheirWayToTheStore(href) {
this.getByHref(href).pipe(
take(1)
).subscribe((re: RequestEntry) => {
if (!hasValue(re)) {
this.responseCache.remove(href);
} else if (!re.responsePending) {
this.responseCache.remove(href);
remove(this.requestsOnTheirWayToTheStore, (item) => item === href);
}
});
}
/** /**
* Configure a certain request * Configure a certain request
* Used to make sure a request is in the cache * Used to make sure a request is in the cache
@@ -105,12 +91,9 @@ export class RequestService {
// TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed // TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void { configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
const isGetRequest = request.method === RestRequestMethod.GET; const isGetRequest = request.method === RestRequestMethod.GET;
if (forceBypassCache) { if (!isGetRequest || !this.isCachedOrPending(request) || forceBypassCache) {
this.clearRequestsOnTheirWayToTheStore(request.href);
}
if (!isGetRequest || !this.isCachedOrPending(request) || (forceBypassCache && !this.isPending(request))) {
this.dispatchRequest(request); this.dispatchRequest(request);
if (isGetRequest) { if (isGetRequest && !forceBypassCache) {
this.trackRequestsOnTheirWayToTheStore(request); this.trackRequestsOnTheirWayToTheStore(request);
} }
} else { } else {

View File

@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
import { RestRequest } from '../data/request.models'; import { RestRequest } from '../data/request.models';
import { ResponseParsingService } from '../data/parsing.service'; import { ResponseParsingService } from '../data/parsing.service';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { EpersonSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models'; import { EpersonSuccessResponse, ErrorResponse, RestResponse } from '../cache/response.models';
import { isNotEmpty } from '../../shared/empty.util'; import { isNotEmpty } from '../../shared/empty.util';
import { BaseResponseParsingService } from '../data/base-response-parsing.service'; import { BaseResponseParsingService } from '../data/base-response-parsing.service';
import { GLOBAL_CONFIG } from '../../../config'; import { GLOBAL_CONFIG } from '../../../config';

View File

@@ -1,21 +1,13 @@
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RequestService } from '../data/request.service'; import { FindAllOptions } from '../data/request.models';
import { ResponseCacheService } from '../cache/response-cache.service';
import { EpersonRequest, FindAllOptions } from '../data/request.models';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NormalizedObject } from '../cache/models/normalized-object.model'; import { NormalizedObject } from '../cache/models/normalized-object.model';
import { DataService } from '../data/data.service'; import { DataService } from '../data/data.service';
import { CacheableObject } from '../cache/object-cache.reducer';
/** /**
* An abstract class that provides methods to make HTTP request to eperson endpoint. * An abstract class that provides methods to make HTTP request to eperson endpoint.
*/ */
export abstract class EpersonService<TNormalized extends NormalizedObject, TDomain> extends DataService<TNormalized, TDomain> { export abstract class EpersonService<TNormalized extends NormalizedObject, TDomain extends CacheableObject> extends DataService<TNormalized, TDomain> {
protected request: EpersonRequest;
protected abstract responseCache: ResponseCacheService;
protected abstract requestService: RequestService;
protected abstract linkPath: string;
protected abstract browseEndpoint: string;
protected abstract halService: HALEndpointService;
public getBrowseEndpoint(options: FindAllOptions): Observable<string> { public getBrowseEndpoint(options: FindAllOptions): Observable<string> {
return this.halService.getEndpoint(this.linkPath); return this.halService.getEndpoint(this.linkPath);

View File

@@ -1,11 +1,11 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators'; import { filter, map, take } from 'rxjs/operators';
import { EpersonService } from './eperson.service'; import { EpersonService } from './eperson.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { FindAllOptions } from '../data/request.models'; import { FindAllOptions } from '../data/request.models';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -17,6 +17,9 @@ import { ObjectCacheService } from '../cache/object-cache.service';
import { SearchParam } from '../cache/models/search-param.model'; import { SearchParam } from '../cache/models/search-param.model';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list'; import { PaginatedList } from '../data/paginated-list';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
/** /**
* Provides methods to retrieve eperson group resources. * Provides methods to retrieve eperson group resources.
@@ -28,7 +31,10 @@ export class GroupEpersonService extends EpersonService<NormalizedGroup, Group>
protected forceBypassCache = false; protected forceBypassCache = false;
constructor( constructor(
protected responseCache: ResponseCacheService, protected comparator: DSOChangeAnalyzer,
protected dataBuildService: NormalizedObjectBuildService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected store: Store<CoreState>,
@@ -57,4 +63,5 @@ export class GroupEpersonService extends EpersonService<NormalizedGroup, Group>
map((groups: RemoteData<PaginatedList<Group>>) => groups.payload.totalElements > 0) map((groups: RemoteData<PaginatedList<Group>>) => groups.payload.totalElements > 0)
); );
} }
} }

View File

@@ -12,6 +12,9 @@ export class NormalizedGroup extends NormalizedDSpaceObject implements Cacheable
@autoserialize @autoserialize
public handle: string; public handle: string;
@autoserialize
public name: string;
@autoserialize @autoserialize
public permanent: boolean; public permanent: boolean;
} }

View File

@@ -35,35 +35,35 @@ describe('IntegrationResponseParsingService', () => {
function initVars() { function initVars() {
pageInfo = Object.assign(new PageInfo(), { elementsPerPage: 5, totalElements: 5, totalPages: 1, currentPage: 1, self: 'https://rest.api/integration/authorities/type/entries'}); pageInfo = Object.assign(new PageInfo(), { elementsPerPage: 5, totalElements: 5, totalPages: 1, currentPage: 1, self: 'https://rest.api/integration/authorities/type/entries'});
definitions = new PaginatedList(pageInfo,[ definitions = new PaginatedList(pageInfo,[
Object.assign({}, new AuthorityValue(), { Object.assign(new AuthorityValue(), {
type: 'authority', type: 'authority',
display: 'One', display: 'One',
id: 'One', id: 'One',
otherInformation: undefined, otherInformation: undefined,
value: 'One' value: 'One'
}), }),
Object.assign({}, new AuthorityValue(), { Object.assign(new AuthorityValue(), {
type: 'authority', type: 'authority',
display: 'Two', display: 'Two',
id: 'Two', id: 'Two',
otherInformation: undefined, otherInformation: undefined,
value: 'Two' value: 'Two'
}), }),
Object.assign({}, new AuthorityValue(), { Object.assign(new AuthorityValue(), {
type: 'authority', type: 'authority',
display: 'Three', display: 'Three',
id: 'Three', id: 'Three',
otherInformation: undefined, otherInformation: undefined,
value: 'Three' value: 'Three'
}), }),
Object.assign({}, new AuthorityValue(), { Object.assign(new AuthorityValue(), {
type: 'authority', type: 'authority',
display: 'Four', display: 'Four',
id: 'Four', id: 'Four',
otherInformation: undefined, otherInformation: undefined,
value: 'Four' value: 'Four'
}), }),
Object.assign({}, new AuthorityValue(), { Object.assign(new AuthorityValue(), {
type: 'authority', type: 'authority',
display: 'Five', display: 'Five',
id: 'Five', id: 'Five',

View File

@@ -2,16 +2,12 @@ import { Observable, of as observableOf, throwError as observableThrowError } fr
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators'; import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { IntegrationSuccessResponse } from '../cache/response.models'; import { IntegrationSuccessResponse } from '../cache/response.models';
import { ResponseCacheService } from '../cache/response-cache.service';
import { IntegrationSuccessResponse, RestResponse } from '../cache/response-cache.models';
import { GetRequest, IntegrationRequest } from '../data/request.models'; import { GetRequest, IntegrationRequest } from '../data/request.models';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { IntegrationData } from './integration-data'; import { IntegrationData } from './integration-data';
import { IntegrationSearchOptions } from './models/integration-options.model'; import { IntegrationSearchOptions } from './models/integration-options.model';
import { RequestEntry } from '../data/request.reducer';
import { getResponseFromEntry } from '../shared/operators'; import { getResponseFromEntry } from '../shared/operators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
export abstract class IntegrationService { export abstract class IntegrationService {
protected request: IntegrationRequest; protected request: IntegrationRequest;

View File

@@ -1,12 +1,11 @@
import { async, TestBed } from '@angular/core/testing'; import { async, TestBed } from '@angular/core/testing';
import { cold, getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { Store, StoreModule } from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store';
import { getMockRequestService } from '../../shared/mocks/mock-request.service'; import { getMockRequestService } from '../../shared/mocks/mock-request.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { SubmissionPatchRequest } from '../data/request.models'; import { SubmissionPatchRequest } from '../data/request.models';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
@@ -23,13 +22,13 @@ import {
StartTransactionPatchOperationsAction StartTransactionPatchOperationsAction
} from './json-patch-operations.actions'; } from './json-patch-operations.actions';
import { MockStore } from '../../shared/testing/mock-store'; import { MockStore } from '../../shared/testing/mock-store';
import { RequestEntry } from '../data/request.reducer';
class TestService extends JsonPatchOperationsService<SubmitDataResponseDefinitionObject, SubmissionPatchRequest> { class TestService extends JsonPatchOperationsService<SubmitDataResponseDefinitionObject, SubmissionPatchRequest> {
protected linkPath = ''; protected linkPath = '';
protected patchRequestConstructor = SubmissionPatchRequest; protected patchRequestConstructor = SubmissionPatchRequest;
constructor( constructor(
protected responseCache: ResponseCacheService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected store: Store<CoreState>,
protected halService: HALEndpointService) { protected halService: HALEndpointService) {
@@ -41,7 +40,6 @@ class TestService extends JsonPatchOperationsService<SubmitDataResponseDefinitio
describe('JsonPatchOperationsService test suite', () => { describe('JsonPatchOperationsService test suite', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: TestService; let service: TestService;
let responseCache: ResponseCacheService;
let requestService: RequestService; let requestService: RequestService;
let rdbService: RemoteDataBuildService; let rdbService: RemoteDataBuildService;
let halService: any; let halService: any;
@@ -84,18 +82,14 @@ describe('JsonPatchOperationsService test suite', () => {
value: ['test'] value: ['test']
}]; }];
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService { const getRequestEntry$ = (successful: boolean) => {
return jasmine.createSpyObj('responseCache', { return observableOf({
get: cold('c-', { response: { isSuccessful: successful, timeAdded: timestampResponse } as any
c: {response: {isSuccessful}, } as RequestEntry)
timeAdded: timestampResponse} };
})
});
}
function initTestService(): TestService { function initTestService(): TestService {
return new TestService( return new TestService(
responseCache,
requestService, requestService,
store, store,
halService halService
@@ -116,8 +110,7 @@ describe('JsonPatchOperationsService test suite', () => {
beforeEach(() => { beforeEach(() => {
store = TestBed.get(Store); store = TestBed.get(Store);
responseCache = initMockResponseCacheService(true); requestService = getMockRequestService(getRequestEntry$(true));
requestService = getMockRequestService();
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
scheduler = getTestScheduler(); scheduler = getTestScheduler();
halService = new HALEndpointServiceStub(resourceEndpointURL); halService = new HALEndpointServiceStub(resourceEndpointURL);
@@ -146,7 +139,7 @@ describe('JsonPatchOperationsService test suite', () => {
scheduler.schedule(() => service.jsonPatchByResourceType(resourceEndpoint, resourceScope, testJsonPatchResourceType).subscribe()); scheduler.schedule(() => service.jsonPatchByResourceType(resourceEndpoint, resourceScope, testJsonPatchResourceType).subscribe());
scheduler.flush(); scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(expected, true); expect(requestService.configure).toHaveBeenCalledWith(expected);
}); });
it('should dispatch a new StartTransactionPatchOperationsAction', () => { it('should dispatch a new StartTransactionPatchOperationsAction', () => {
@@ -170,8 +163,7 @@ describe('JsonPatchOperationsService test suite', () => {
describe('when request is not successful', () => { describe('when request is not successful', () => {
beforeEach(() => { beforeEach(() => {
store = TestBed.get(Store); store = TestBed.get(Store);
responseCache = initMockResponseCacheService(false); requestService = getMockRequestService(getRequestEntry$(false));
requestService = getMockRequestService();
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
scheduler = getTestScheduler(); scheduler = getTestScheduler();
halService = new HALEndpointServiceStub(resourceEndpointURL); halService = new HALEndpointServiceStub(resourceEndpointURL);
@@ -208,7 +200,7 @@ describe('JsonPatchOperationsService test suite', () => {
scheduler.schedule(() => service.jsonPatchByResourceID(resourceEndpoint, resourceScope, testJsonPatchResourceType, testJsonPatchResourceId).subscribe()); scheduler.schedule(() => service.jsonPatchByResourceID(resourceEndpoint, resourceScope, testJsonPatchResourceType, testJsonPatchResourceId).subscribe());
scheduler.flush(); scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(expected, true); expect(requestService.configure).toHaveBeenCalledWith(expected);
}); });
it('should dispatch a new StartTransactionPatchOperationsAction', () => { it('should dispatch a new StartTransactionPatchOperationsAction', () => {
@@ -232,8 +224,7 @@ describe('JsonPatchOperationsService test suite', () => {
describe('when request is not successful', () => { describe('when request is not successful', () => {
beforeEach(() => { beforeEach(() => {
store = TestBed.get(Store); store = TestBed.get(Store);
responseCache = initMockResponseCacheService(false); requestService = getMockRequestService(getRequestEntry$(false));
requestService = getMockRequestService();
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
scheduler = getTestScheduler(); scheduler = getTestScheduler();
halService = new HALEndpointServiceStub(resourceEndpointURL); halService = new HALEndpointServiceStub(resourceEndpointURL);

View File

@@ -1,12 +1,10 @@
import { merge as observableMerge, Observable, of as observableOf, throwError as observableThrowError } from 'rxjs'; import { merge as observableMerge, Observable, of as observableOf } from 'rxjs';
import { distinctUntilChanged, filter, flatMap, map, mergeMap, partition, take, tap } from 'rxjs/operators'; import { distinctUntilChanged, filter, find, flatMap, map, partition, take, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { hasValue, isEmpty, isNotEmpty, isNotUndefined, isUndefined } from '../../shared/empty.util'; import { hasValue, isEmpty, isNotEmpty, isNotUndefined, isUndefined } from '../../shared/empty.util';
import { ErrorResponse, PostPatchSuccessResponse, RestResponse } from '../cache/response-cache.models'; import { ErrorResponse, PostPatchSuccessResponse, RestResponse } from '../cache/response.models';
import { ResponseCacheEntry } from '../cache/response-cache.reducer'; import { PatchRequest } from '../data/request.models';
import { ResponseCacheService } from '../cache/response-cache.service';
import { PatchRequest, RestRequest } from '../data/request.models';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
@@ -18,31 +16,20 @@ import {
StartTransactionPatchOperationsAction StartTransactionPatchOperationsAction
} from './json-patch-operations.actions'; } from './json-patch-operations.actions';
import { JsonPatchOperationModel } from './json-patch.model'; import { JsonPatchOperationModel } from './json-patch.model';
import { getResponseFromEntry } from '../shared/operators';
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
/** /**
* An abstract class that provides methods to make JSON Patch requests. * An abstract class that provides methods to make JSON Patch requests.
*/ */
export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, PatchRequestDefinition extends PatchRequest> { export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, PatchRequestDefinition extends PatchRequest> {
protected abstract responseCache: ResponseCacheService;
protected abstract requestService: RequestService; protected abstract requestService: RequestService;
protected abstract store: Store<CoreState>; protected abstract store: Store<CoreState>;
protected abstract linkPath: string; protected abstract linkPath: string;
protected abstract halService: HALEndpointService; protected abstract halService: HALEndpointService;
protected abstract patchRequestConstructor: any; protected abstract patchRequestConstructor: any;
protected submitData(request: RestRequest): Observable<ResponseDefinitionDomain> {
const responses = this.responseCache.get(request.href).pipe(map((entry: ResponseCacheEntry) => entry.response));
const errorResponses = responses.pipe(
filter((response) => !response.isSuccessful),
mergeMap(() => observableThrowError(new Error(`Couldn't send data to server`)))
);
const successResponses = responses.pipe(
filter((response: PostPatchSuccessResponse) => isNotEmpty(response)),
map((response: PostPatchSuccessResponse) => response.dataDefinition)
);
return observableMerge(errorResponses, successResponses);
}
/** /**
* Submit a new JSON Patch request with all operations stored in the state that are ready to be dispatched * Submit a new JSON Patch request with all operations stored in the state that are ready to be dispatched
* *
@@ -56,6 +43,7 @@ export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, Patch
* observable of response * observable of response
*/ */
protected submitJsonPatchOperations(hrefObs: Observable<string>, resourceType: string, resourceId?: string): Observable<ResponseDefinitionDomain> { protected submitJsonPatchOperations(hrefObs: Observable<string>, resourceType: string, resourceId?: string): Observable<ResponseDefinitionDomain> {
const requestId = this.requestService.generateRequestId();
let startTransactionTime = null; let startTransactionTime = null;
const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => isNotEmpty(request.body))(hrefObs.pipe( const [patchRequest$, emptyRequest$] = partition((request: PatchRequestDefinition) => isNotEmpty(request.body))(hrefObs.pipe(
flatMap((endpointURL: string) => { flatMap((endpointURL: string) => {
@@ -84,7 +72,7 @@ export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, Patch
}) })
} }
} }
return this.getRequestInstance(this.requestService.generateRequestId(), endpointURL, body); return this.getRequestInstance(requestId, endpointURL, body);
})); }));
}))); })));
@@ -96,12 +84,12 @@ export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, Patch
patchRequest$.pipe( patchRequest$.pipe(
filter((request: PatchRequestDefinition) => isNotEmpty(request.body)), filter((request: PatchRequestDefinition) => isNotEmpty(request.body)),
tap(() => this.store.dispatch(new StartTransactionPatchOperationsAction(resourceType, resourceId, startTransactionTime))), tap(() => this.store.dispatch(new StartTransactionPatchOperationsAction(resourceType, resourceId, startTransactionTime))),
tap((request: PatchRequestDefinition) => this.requestService.configure(request, true)), tap((request: PatchRequestDefinition) => this.requestService.configure(request)),
flatMap((request: PatchRequestDefinition) => { flatMap(() => {
const [successResponse$, errorResponse$] = partition((response: RestResponse) => response.isSuccessful)(this.responseCache.get(request.href).pipe( const [successResponse$, errorResponse$] = partition((response: RestResponse) => response.isSuccessful)(this.requestService.getByUUID(requestId).pipe(
filter((entry: ResponseCacheEntry) => startTransactionTime < entry.timeAdded), getResponseFromEntry(),
take(1), find((entry: ObjectCacheEntry) => startTransactionTime < entry.timeAdded),
map((entry: ResponseCacheEntry) => entry.response) map((entry: ObjectCacheEntry) => entry),
)); ));
return observableMerge( return observableMerge(
errorResponse$.pipe( errorResponse$.pipe(

View File

@@ -1,5 +1,5 @@
import { Metadatum } from './metadatum.model' import { Metadatum } from './metadatum.model'
import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { isEmpty, isNotEmpty, isUndefined } from '../../shared/empty.util';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { ResourceType } from './resource-type'; import { ResourceType } from './resource-type';
@@ -12,6 +12,8 @@ import { autoserialize } from 'cerialize';
*/ */
export class DSpaceObject implements CacheableObject, ListableObject { export class DSpaceObject implements CacheableObject, ListableObject {
private _name: string;
self: string; self: string;
/** /**
@@ -35,7 +37,14 @@ export class DSpaceObject implements CacheableObject, ListableObject {
* The name for this DSpaceObject * The name for this DSpaceObject
*/ */
get name(): string { get name(): string {
return this.findMetadata('dc.title'); return (isUndefined(this._name)) ? this.findMetadata('dc.title') : this._name;
}
/**
* The name for this DSpaceObject
*/
set name(name) {
this._name = name;
} }
/** /**

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { DSpaceRESTv2Service, HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { DSpaceRESTv2Service, HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { RestRequestMethod } from '../data/request.models'; import { RestRequestMethod } from '../data/rest-request-method';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
@@ -24,7 +24,7 @@ export class FileService {
downloadFile(url: string) { downloadFile(url: string) {
const headers = new HttpHeaders(); const headers = new HttpHeaders();
const options: HttpOptions = Object.create({headers, responseType: 'blob'}); const options: HttpOptions = Object.create({headers, responseType: 'blob'});
return this.restService.request(RestRequestMethod.Get, url, null, options) return this.restService.request(RestRequestMethod.GET, url, null, options)
.subscribe((data) => { .subscribe((data) => {
saveAs(data.payload as Blob, this.getFileNameFromResponseContentDisposition(data)); saveAs(data.payload as Blob, this.getFileNameFromResponseContentDisposition(data));
}); });

View File

@@ -48,7 +48,7 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
/** /**
* The submission config definition * The submission config definition
*/ */
submissionDefinition: SubmissionDefinitionsModel; submissionDefinition: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
/** /**
* The workspaceitem submitter * The workspaceitem submitter

View File

@@ -3,7 +3,6 @@ import { Store } from '@ngrx/store';
import { getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { SubmissionJsonPatchOperationsService } from './submission-json-patch-operations.service'; import { SubmissionJsonPatchOperationsService } from './submission-json-patch-operations.service';
@@ -14,13 +13,11 @@ describe('SubmissionJsonPatchOperationsService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: SubmissionJsonPatchOperationsService; let service: SubmissionJsonPatchOperationsService;
const requestService = {} as RequestService; const requestService = {} as RequestService;
const responseCache = {} as ResponseCacheService;
const store = {} as Store<CoreState>; const store = {} as Store<CoreState>;
const halEndpointService = {} as HALEndpointService; const halEndpointService = {} as HALEndpointService;
function initTestService() { function initTestService() {
return new SubmissionJsonPatchOperationsService( return new SubmissionJsonPatchOperationsService(
responseCache,
requestService, requestService,
store, store,
halEndpointService halEndpointService

View File

@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { JsonPatchOperationsService } from '../json-patch/json-patch-operations.service'; import { JsonPatchOperationsService } from '../json-patch/json-patch-operations.service';
@@ -16,7 +15,6 @@ export class SubmissionJsonPatchOperationsService extends JsonPatchOperationsSer
protected patchRequestConstructor = SubmissionPatchRequest; protected patchRequestConstructor = SubmissionPatchRequest;
constructor( constructor(
protected responseCache: ResponseCacheService,
protected requestService: RequestService, protected requestService: RequestService,
protected store: Store<CoreState>, protected store: Store<CoreState>,
protected halService: HALEndpointService) { protected halService: HALEndpointService) {

View File

@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
import { ResponseParsingService } from '../data/parsing.service'; import { ResponseParsingService } from '../data/parsing.service';
import { RestRequest } from '../data/request.models'; import { RestRequest } from '../data/request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../cache/response-cache.models'; import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../cache/response.models';
import { isEmpty, isNotEmpty, isNotNull } from '../../shared/empty.util'; import { isEmpty, isNotEmpty, isNotNull } from '../../shared/empty.util';
import { ConfigObject } from '../config/models/config.model'; import { ConfigObject } from '../config/models/config.model';
import { BaseResponseParsingService } from '../data/base-response-parsing.service'; import { BaseResponseParsingService } from '../data/base-response-parsing.service';

View File

@@ -1,17 +1,19 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { BrowseService } from '../browse/browse.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { DataService } from '../data/data.service'; import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model'; import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model';
import { Workflowitem } from './models/workflowitem.model'; import { Workflowitem } from './models/workflowitem.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions } from '../data/request.models'; import { FindAllOptions } from '../data/request.models';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
@Injectable() @Injectable()
export class WorkflowitemDataService extends DataService<NormalizedWorkflowItem, Workflowitem> { export class WorkflowitemDataService extends DataService<NormalizedWorkflowItem, Workflowitem> {
@@ -19,12 +21,15 @@ export class WorkflowitemDataService extends DataService<NormalizedWorkflowItem,
protected forceBypassCache = true; protected forceBypassCache = true;
constructor( constructor(
protected responseCache: ResponseCacheService, protected comparator: DSOChangeAnalyzer,
protected dataBuildService: NormalizedObjectBuildService,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected objectCache: ObjectCacheService,
protected bs: BrowseService, protected store: Store<CoreState>) {
protected halService: HALEndpointService) {
super(); super();
} }

View File

@@ -1,17 +1,19 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { BrowseService } from '../browse/browse.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { DataService } from '../data/data.service'; import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { Workspaceitem } from './models/workspaceitem.model'; import { Workspaceitem } from './models/workspaceitem.model';
import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model'; import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions } from '../data/request.models'; import { FindAllOptions } from '../data/request.models';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
@Injectable() @Injectable()
export class WorkspaceitemDataService extends DataService<NormalizedWorkspaceItem, Workspaceitem> { export class WorkspaceitemDataService extends DataService<NormalizedWorkspaceItem, Workspaceitem> {
@@ -19,12 +21,15 @@ export class WorkspaceitemDataService extends DataService<NormalizedWorkspaceIte
protected forceBypassCache = true; protected forceBypassCache = true;
constructor( constructor(
protected responseCache: ResponseCacheService, protected comparator: DSOChangeAnalyzer,
protected dataBuildService: NormalizedObjectBuildService,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>, protected objectCache: ObjectCacheService,
protected bs: BrowseService, protected store: Store<CoreState>) {
protected halService: HALEndpointService) {
super(); super();
} }

View File

@@ -108,7 +108,6 @@ describe('DeleteComColPageComponent', () => {
it('should show an error notification on failure', () => { it('should show an error notification on failure', () => {
(dsoDataService.delete as any).and.returnValue(observableOf(false)); (dsoDataService.delete as any).and.returnValue(observableOf(false));
spyOn(notificationsService, 'error');
spyOn(router, 'navigate'); spyOn(router, 'navigate');
comp.onConfirm(data2); comp.onConfirm(data2);
fixture.detectChanges(); fixture.detectChanges();
@@ -117,7 +116,6 @@ describe('DeleteComColPageComponent', () => {
}); });
it('should show a success notification on success and navigate', () => { it('should show a success notification on success and navigate', () => {
spyOn(notificationsService, 'success');
spyOn(router, 'navigate'); spyOn(router, 'navigate');
comp.onConfirm(data1); comp.onConfirm(data1);
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -1,14 +1,12 @@
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { ResponseCacheService } from '../../core/cache/response-cache.service';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { CoreState } from '../../core/core.reducers'; import { CoreState } from '../../core/core.reducers';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
export class SubmissionRestServiceStub { export class SubmissionRestServiceStub {
protected linkPath = 'workspaceitems'; protected linkPath = 'workspaceitems';
protected responseCache: ResponseCacheService;
protected requestService: RequestService; protected requestService: RequestService;
protected store: Store<CoreState>; protected store: Store<CoreState>;
protected halService: HALEndpointService; protected halService: HALEndpointService;

View File

@@ -104,9 +104,9 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
onCollectionChange(submissionObject: SubmissionObject) { onCollectionChange(submissionObject: SubmissionObject) {
this.collectionId = (submissionObject.collection as Collection).id; this.collectionId = (submissionObject.collection as Collection).id;
if (this.definitionId !== submissionObject.submissionDefinition.name) { if (this.definitionId !== (submissionObject.submissionDefinition as SubmissionDefinitionsModel).name) {
this.sections = submissionObject.sections; this.sections = submissionObject.sections;
this.submissionDefinition = submissionObject.submissionDefinition; this.submissionDefinition = (submissionObject.submissionDefinition as SubmissionDefinitionsModel);
this.definitionId = this.submissionDefinition.name; this.definitionId = this.submissionDefinition.name;
this.submissionService.resetSubmissionObject( this.submissionService.resetSubmissionObject(
this.collectionId, this.collectionId,

View File

@@ -77,7 +77,6 @@ describe('UploadSectionFileViewComponent test suite', () => {
fixture.detectChanges(); fixture.detectChanges();
console.log(comp.metadata);
expect(comp.metadata.length).toBe(2); expect(comp.metadata.length).toBe(2);
}); });

View File

@@ -1,8 +1,7 @@
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { cold, getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { SubmissionRestService } from './submission-rest.service'; import { SubmissionRestService } from './submission-rest.service';
import { ResponseCacheService } from '../core/cache/response-cache.service';
import { RequestService } from '../core/data/request.service'; import { RequestService } from '../core/data/request.service';
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
import { getMockRequestService } from '../shared/mocks/mock-request.service'; import { getMockRequestService } from '../shared/mocks/mock-request.service';
@@ -19,7 +18,6 @@ import { FormFieldMetadataValueObject } from '../shared/form/builder/models/form
describe('SubmissionRestService test suite', () => { describe('SubmissionRestService test suite', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
let service: SubmissionRestService; let service: SubmissionRestService;
let responseCache: ResponseCacheService;
let requestService: RequestService; let requestService: RequestService;
let rdbService: RemoteDataBuildService; let rdbService: RemoteDataBuildService;
let halService: any; let halService: any;
@@ -31,27 +29,15 @@ describe('SubmissionRestService test suite', () => {
const resourceHref = resourceEndpointURL + '/' + resourceEndpoint + '/' + resourceScope; const resourceHref = resourceEndpointURL + '/' + resourceEndpoint + '/' + resourceScope;
const timestampResponse = 1545994811992; const timestampResponse = 1545994811992;
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
return jasmine.createSpyObj('responseCache', {
get: cold('c-', {
c: {response: {isSuccessful},
timeAdded: timestampResponse}
}),
remove: jasmine.createSpy('remove')
});
}
function initTestService() { function initTestService() {
return new SubmissionRestService( return new SubmissionRestService(
rdbService, rdbService,
responseCache,
requestService, requestService,
halService halService
); );
} }
beforeEach(() => { beforeEach(() => {
responseCache = initMockResponseCacheService(true);
requestService = getMockRequestService(); requestService = getMockRequestService();
rdbService = getMockRemoteDataBuildService(); rdbService = getMockRemoteDataBuildService();
scheduler = getTestScheduler(); scheduler = getTestScheduler();
@@ -86,7 +72,7 @@ describe('SubmissionRestService test suite', () => {
scheduler.schedule(() => service.postToEndpoint(resourceEndpoint, body, resourceScope).subscribe()); scheduler.schedule(() => service.postToEndpoint(resourceEndpoint, body, resourceScope).subscribe());
scheduler.flush(); scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(expected, true); expect(requestService.configure).toHaveBeenCalledWith(expected);
}); });
}); });
@@ -96,7 +82,7 @@ describe('SubmissionRestService test suite', () => {
scheduler.schedule(() => service.patchToEndpoint(resourceEndpoint, body, resourceScope).subscribe()); scheduler.schedule(() => service.patchToEndpoint(resourceEndpoint, body, resourceScope).subscribe());
scheduler.flush(); scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(expected, true); expect(requestService.configure).toHaveBeenCalledWith(expected);
}); });
}); });
}); });

View File

@@ -3,10 +3,7 @@ import { Injectable } from '@angular/core';
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs'; import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/operators'; import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/operators';
import { ResponseCacheService } from '../core/cache/response-cache.service';
import { RequestService } from '../core/data/request.service'; import { RequestService } from '../core/data/request.service';
import { ResponseCacheEntry } from '../core/cache/response-cache.reducer';
import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../core/cache/response-cache.models';
import { isNotEmpty } from '../shared/empty.util'; import { isNotEmpty } from '../shared/empty.util';
import { import {
DeleteRequest, DeleteRequest,
@@ -21,6 +18,8 @@ import { SubmitDataResponseDefinitionObject } from '../core/shared/submit-data-r
import { HttpOptions } from '../core/dspace-rest-v2/dspace-rest-v2.service'; import { HttpOptions } from '../core/dspace-rest-v2/dspace-rest-v2.service';
import { HALEndpointService } from '../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../core/shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../core/cache/response.models';
import { getResponseFromEntry } from '../core/shared/operators';
@Injectable() @Injectable()
export class SubmissionRestService { export class SubmissionRestService {
@@ -28,13 +27,14 @@ export class SubmissionRestService {
constructor( constructor(
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected responseCache: ResponseCacheService,
protected requestService: RequestService, protected requestService: RequestService,
protected halService: HALEndpointService) { protected halService: HALEndpointService) {
} }
protected submitData(request: RestRequest): Observable<SubmitDataResponseDefinitionObject> { protected fetchRequest(requestId: string): Observable<SubmitDataResponseDefinitionObject> {
const responses = this.responseCache.get(request.href).pipe(map((entry: ResponseCacheEntry) => entry.response)); const responses = this.requestService.getByUUID(requestId).pipe(
getResponseFromEntry()
);
const errorResponses = responses.pipe( const errorResponses = responses.pipe(
filter((response: RestResponse) => !response.isSuccessful), filter((response: RestResponse) => !response.isSuccessful),
mergeMap((error: ErrorResponse) => observableThrowError(error)) mergeMap((error: ErrorResponse) => observableThrowError(error))
@@ -47,67 +47,55 @@ export class SubmissionRestService {
return observableMerge(errorResponses, successResponses); return observableMerge(errorResponses, successResponses);
} }
protected fetchRequest(request: RestRequest): Observable<SubmitDataResponseDefinitionObject> {
const responses = this.responseCache.get(request.href).pipe(
map((entry: ResponseCacheEntry) => entry.response),
tap(() => this.responseCache.remove(request.href)));
const errorResponses = responses.pipe(
filter((response: RestResponse) => !response.isSuccessful),
mergeMap((error: ErrorResponse) => observableThrowError(error))
);
const successResponses = responses.pipe(
filter((response: SubmissionSuccessResponse) => response.isSuccessful && isNotEmpty(response)),
map((response: SubmissionSuccessResponse) => response.dataDefinition as any),
distinctUntilChanged()
);
return observableMerge(errorResponses, successResponses);
}
protected getEndpointByIDHref(endpoint, resourceID): string { protected getEndpointByIDHref(endpoint, resourceID): string {
return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`; return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
} }
public deleteById(scopeId: string, linkName?: string): Observable<SubmitDataResponseDefinitionObject> { public deleteById(scopeId: string, linkName?: string): Observable<SubmitDataResponseDefinitionObject> {
const requestId = this.requestService.generateRequestId();
return this.halService.getEndpoint(linkName || this.linkPath).pipe( return this.halService.getEndpoint(linkName || this.linkPath).pipe(
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
map((endpointURL: string) => new SubmissionDeleteRequest(this.requestService.generateRequestId(), endpointURL)), map((endpointURL: string) => new SubmissionDeleteRequest(requestId, endpointURL)),
tap((request: DeleteRequest) => this.requestService.configure(request)), tap((request: DeleteRequest) => this.requestService.configure(request)),
flatMap((request: DeleteRequest) => this.submitData(request)), flatMap((request: DeleteRequest) => this.fetchRequest(requestId)),
distinctUntilChanged()); distinctUntilChanged());
} }
public getDataById(linkName: string, id: string): Observable<any> { public getDataById(linkName: string, id: string): Observable<any> {
const requestId = this.requestService.generateRequestId();
return this.halService.getEndpoint(linkName).pipe( return this.halService.getEndpoint(linkName).pipe(
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)),
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => new SubmissionRequest(this.requestService.generateRequestId(), endpointURL)), map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)),
tap((request: RestRequest) => this.requestService.configure(request, true)), tap((request: RestRequest) => this.requestService.configure(request, true)),
flatMap((request: RestRequest) => this.fetchRequest(request)), flatMap((request: RestRequest) => this.fetchRequest(requestId)),
distinctUntilChanged()); distinctUntilChanged());
} }
public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions): Observable<SubmitDataResponseDefinitionObject> { public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions): Observable<SubmitDataResponseDefinitionObject> {
const requestId = this.requestService.generateRequestId();
return this.halService.getEndpoint(linkName).pipe( return this.halService.getEndpoint(linkName).pipe(
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => new SubmissionPostRequest(this.requestService.generateRequestId(), endpointURL, body, options)), map((endpointURL: string) => new SubmissionPostRequest(requestId, endpointURL, body, options)),
tap((request: PostRequest) => this.requestService.configure(request, true)), tap((request: PostRequest) => this.requestService.configure(request)),
flatMap((request: PostRequest) => this.submitData(request)), flatMap((request: PostRequest) => this.fetchRequest(requestId)),
distinctUntilChanged()); distinctUntilChanged());
} }
public patchToEndpoint(linkName: string, body: any, scopeId?: string): Observable<SubmitDataResponseDefinitionObject> { public patchToEndpoint(linkName: string, body: any, scopeId?: string): Observable<SubmitDataResponseDefinitionObject> {
const requestId = this.requestService.generateRequestId();
return this.halService.getEndpoint(linkName).pipe( return this.halService.getEndpoint(linkName).pipe(
filter((href: string) => isNotEmpty(href)), filter((href: string) => isNotEmpty(href)),
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => new SubmissionPatchRequest(this.requestService.generateRequestId(), endpointURL, body)), map((endpointURL: string) => new SubmissionPatchRequest(requestId, endpointURL, body)),
tap((request: PostRequest) => this.requestService.configure(request, true)), tap((request: PostRequest) => this.requestService.configure(request)),
flatMap((request: PostRequest) => this.submitData(request)), flatMap((request: PostRequest) => this.fetchRequest(requestId)),
distinctUntilChanged()); distinctUntilChanged());
} }

View File

@@ -41,7 +41,7 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
import { SubmissionDefinitionsModel } from '../core/config/models/config-submission-definitions.model'; import { SubmissionDefinitionsModel } from '../core/config/models/config-submission-definitions.model';
import { WorkspaceitemSectionsObject } from '../core/submission/models/workspaceitem-sections.model'; import { WorkspaceitemSectionsObject } from '../core/submission/models/workspaceitem-sections.model';
import { RemoteData } from '../core/data/remote-data'; import { RemoteData } from '../core/data/remote-data';
import { ErrorResponse } from '../core/cache/response-cache.models'; import { ErrorResponse } from '../core/cache/response.models';
import { RemoteDataError } from '../core/data/remote-data-error'; import { RemoteDataError } from '../core/data/remote-data-error';
@Injectable() @Injectable()

View File

@@ -27,4 +27,4 @@ export interface AutoSyncConfig {
* The max number of requests in the buffer before a sync to the server * The max number of requests in the buffer before a sync to the server
*/ */
maxBufferSize: number; maxBufferSize: number;
}; }

View File

@@ -3500,6 +3500,7 @@ fast-glob@^2.0.2:
fast-json-patch@^2.0.7: fast-json-patch@^2.0.7:
version "2.0.7" version "2.0.7"
resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.0.7.tgz#55864b08b1e50381d2f37fd472bb2e18fe54a733" resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.0.7.tgz#55864b08b1e50381d2f37fd472bb2e18fe54a733"
integrity sha512-DQeoEyPYxdTtfmB3yDlxkLyKTdbJ6ABfFGcMynDqjvGhPYLto/pZyb/dG2Nyd/n9CArjEWN9ZST++AFmgzgbGw==
dependencies: dependencies:
deep-equal "^1.0.1" deep-equal "^1.0.1"