Finished patch support

This commit is contained in:
lotte
2018-09-19 15:32:14 +02:00
parent e959542e2d
commit c6f55e424b
14 changed files with 61 additions and 58 deletions

View File

@@ -9,10 +9,10 @@ import { of as observableOf } from 'rxjs';
import { AuthInterceptor } from './auth.interceptor'; import { AuthInterceptor } from './auth.interceptor';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service'; import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service';
import { RestRequestMethod } from '../data/request.models';
import { RouterStub } from '../../shared/testing/router-stub'; import { RouterStub } from '../../shared/testing/router-stub';
import { TruncatablesState } from '../../shared/truncatable/truncatable.reducer'; import { TruncatablesState } from '../../shared/truncatable/truncatable.reducer';
import { AuthServiceStub } from '../../shared/testing/auth-service-stub'; import { AuthServiceStub } from '../../shared/testing/auth-service-stub';
import { RestRequestMethod } from '../data//rest-request-method';
describe(`AuthInterceptor`, () => { describe(`AuthInterceptor`, () => {
let service: DSpaceRESTv2Service; let service: DSpaceRESTv2Service;
@@ -49,7 +49,7 @@ describe(`AuthInterceptor`, () => {
describe('when has a valid token', () => { describe('when has a valid token', () => {
it('should not add an Authorization header when were sending a HTTP request to \'authn\' endpoint', () => { it('should not add an Authorization header when were sending a HTTP request to \'authn\' endpoint', () => {
service.request(RestRequestMethod.Post, 'dspace-spring-rest/api/authn/login', 'password=password&user=user').subscribe((response) => { service.request(RestRequestMethod.POST, 'dspace-spring-rest/api/authn/login', 'password=password&user=user').subscribe((response) => {
expect(response).toBeTruthy(); expect(response).toBeTruthy();
}); });
@@ -60,7 +60,7 @@ describe(`AuthInterceptor`, () => {
}); });
it('should add an Authorization header when were sending a HTTP request to \'authn\' endpoint', () => { it('should add an Authorization header when were sending a HTTP request to \'authn\' endpoint', () => {
service.request(RestRequestMethod.Post, 'dspace-spring-rest/api/submission/workspaceitems', 'test').subscribe((response) => { service.request(RestRequestMethod.POST, 'dspace-spring-rest/api/submission/workspaceitems', 'test').subscribe((response) => {
expect(response).toBeTruthy(); expect(response).toBeTruthy();
}); });
@@ -85,11 +85,11 @@ describe(`AuthInterceptor`, () => {
it('should redirect to login', () => { it('should redirect to login', () => {
service.request(RestRequestMethod.Post, 'dspace-spring-rest/api/submission/workspaceitems', 'password=password&user=user').subscribe((response) => { service.request(RestRequestMethod.POST, 'dspace-spring-rest/api/submission/workspaceitems', 'password=password&user=user').subscribe((response) => {
expect(response).toBeTruthy(); expect(response).toBeTruthy();
}); });
service.request(RestRequestMethod.Post, 'dspace-spring-rest/api/submission/workspaceitems', 'password=password&user=user'); service.request(RestRequestMethod.POST, 'dspace-spring-rest/api/submission/workspaceitems', 'password=password&user=user');
httpMock.expectNone('dspace-spring-rest/api/submission/workspaceitems'); httpMock.expectNone('dspace-spring-rest/api/submission/workspaceitems');
}); });

View File

@@ -1,6 +1,6 @@
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, filter, first, map, mergeMap, take, tap } from 'rxjs/operators'; import { distinctUntilChanged, filter, first, map, mergeMap } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MemoizedSelector, select, Store } from '@ngrx/store'; import { MemoizedSelector, select, Store } from '@ngrx/store';
import { IndexName } from '../index/index.reducer'; import { IndexName } from '../index/index.reducer';
@@ -8,7 +8,8 @@ import { IndexName } from '../index/index.reducer';
import { CacheableObject, ObjectCacheEntry } from './object-cache.reducer'; import { CacheableObject, ObjectCacheEntry } from './object-cache.reducer';
import { import {
AddPatchObjectCacheAction, AddPatchObjectCacheAction,
AddToObjectCacheAction, ApplyPatchObjectCacheAction, AddToObjectCacheAction,
ApplyPatchObjectCacheAction,
RemoveFromObjectCacheAction RemoveFromObjectCacheAction
} from './object-cache.actions'; } from './object-cache.actions';
import { hasNoValue, isNotEmpty } from '../../shared/empty.util'; import { hasNoValue, isNotEmpty } from '../../shared/empty.util';
@@ -18,8 +19,8 @@ import { pathSelector } from '../shared/selectors';
import { NormalizedObjectFactory } from './models/normalized-object-factory'; import { NormalizedObjectFactory } from './models/normalized-object-factory';
import { NormalizedObject } from './models/normalized-object.model'; import { NormalizedObject } from './models/normalized-object.model';
import { applyPatch, Operation } from 'fast-json-patch'; import { applyPatch, Operation } from 'fast-json-patch';
import { RestRequestMethod } from '../data/request.models';
import { AddToSSBAction } from './server-sync-buffer.actions'; import { AddToSSBAction } from './server-sync-buffer.actions';
import { RestRequestMethod } from '../data//rest-request-method';
function selfLinkFromUuidSelector(uuid: string): MemoizedSelector<CoreState, string> { function selfLinkFromUuidSelector(uuid: string): MemoizedSelector<CoreState, string> {
return pathSelector<CoreState, string>(coreSelector, 'index', IndexName.OBJECT, uuid); return pathSelector<CoreState, string>(coreSelector, 'index', IndexName.OBJECT, uuid);
@@ -209,6 +210,8 @@ export class ObjectCacheService {
} }
} }
/** /**
* Add operations to the existing list of operations for an ObjectCacheEntry * Add operations to the existing list of operations for an ObjectCacheEntry
* Makes sure the ServerSyncBuffer for this ObjectCacheEntry is updated * Makes sure the ServerSyncBuffer for this ObjectCacheEntry is updated
@@ -219,7 +222,7 @@ export class ObjectCacheService {
*/ */
public addPatch(uuid: string, patch: Operation[]) { public addPatch(uuid: string, patch: Operation[]) {
this.store.dispatch(new AddPatchObjectCacheAction(uuid, patch)); this.store.dispatch(new AddPatchObjectCacheAction(uuid, patch));
this.store.dispatch(new AddToSSBAction(uuid, RestRequestMethod.Patch)); this.store.dispatch(new AddToSSBAction(uuid, RestRequestMethod.PATCH));
} }
/** /**

View File

@@ -1,9 +1,7 @@
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
import { CacheableObject } from './object-cache.reducer'; import { RestRequestMethod } from '../data//rest-request-method';
import { Operation } from 'fast-json-patch';
import { RestRequest, RestRequestMethod } from '../data/request.models';
/** /**
* The list of ServerSyncBufferAction type definitions * The list of ServerSyncBufferAction type definitions
@@ -33,7 +31,7 @@ export class AddToSSBAction implements Action {
* the unique href of the cached object entry that should be updated * the unique href of the cached object entry that should be updated
*/ */
constructor(href: string, method: RestRequestMethod) { constructor(href: string, method: RestRequestMethod) {
this.payload = { href, method }; this.payload = { href, method: undefined };
} }
} }

View File

@@ -14,22 +14,24 @@ import { Action, select, Store } from '@ngrx/store';
import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer'; import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer';
import { of as observableOf, combineLatest as observableCombineLatest } from 'rxjs'; import { of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { PutRequest, RestRequestMethod } from '../data/request.models'; import { PutRequest } from '../data/request.models';
import { ObjectCacheService } from './object-cache.service'; import { ObjectCacheService } from './object-cache.service';
import { ApplyPatchObjectCacheAction } from './object-cache.actions'; import { ApplyPatchObjectCacheAction } from './object-cache.actions';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { GenericConstructor } from '../shared/generic-constructor'; import { GenericConstructor } from '../shared/generic-constructor';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { RestRequestMethod } from '../data//rest-request-method';
@Injectable() @Injectable()
export class ObjectCacheEffects { export class ServerSyncBufferEffects {
@Effect() setTimeoutForServerSync = this.actions$ @Effect() setTimeoutForServerSync = this.actions$
.pipe( .pipe(
ofType(ServerSyncBufferActionTypes.ADD), ofType(ServerSyncBufferActionTypes.ADD),
exhaustMap((action: AddToSSBAction) => { exhaustMap((action: AddToSSBAction) => {
const autoSyncConfig = this.EnvConfig.cache.autoSync; // const autoSyncConfig = this.EnvConfig.cache.autoSync;
const timeoutInSeconds = autoSyncConfig.timePerMethod[action.type] || autoSyncConfig.defaultTime; // const timeoutInSeconds = autoSyncConfig.timePerMethod[action.type] || autoSyncConfig.defaultTime;
const timeoutInSeconds = 0;
return observableOf(new CommitSSBAction(action.payload.method)).pipe(delay(timeoutInSeconds * 1000)) return observableOf(new CommitSSBAction(action.payload.method)).pipe(delay(timeoutInSeconds * 1000))
}) })
); );
@@ -51,7 +53,7 @@ export class ObjectCacheEffects {
return true; return true;
}) })
.map((entry: ServerSyncBufferEntry) => { .map((entry: ServerSyncBufferEntry) => {
if (entry.method === RestRequestMethod.Patch) { if (entry.method === RestRequestMethod.PATCH) {
return this.applyPatch(entry.href); return this.applyPatch(entry.href);
} else { } else {
/* TODO other request stuff */ /* TODO other request stuff */

View File

@@ -1,4 +1,3 @@
import { RestRequestMethod } from '../data/request.models';
import { hasNoValue, hasValue } from '../../shared/empty.util'; import { hasNoValue, hasValue } from '../../shared/empty.util';
import { import {
AddToSSBAction, AddToSSBAction,
@@ -6,6 +5,7 @@ import {
ServerSyncBufferAction, ServerSyncBufferAction,
ServerSyncBufferActionTypes ServerSyncBufferActionTypes
} from './server-sync-buffer.actions'; } from './server-sync-buffer.actions';
import { RestRequestMethod } from '../data//rest-request-method';
/** /**
* An entry in the ServerSyncBufferState * An entry in the ServerSyncBufferState

View File

@@ -4,11 +4,13 @@ import { ResponseCacheEffects } from './cache/response-cache.effects';
import { UUIDIndexEffects } from './index/index.effects'; import { UUIDIndexEffects } from './index/index.effects';
import { RequestEffects } from './data/request.effects'; import { RequestEffects } from './data/request.effects';
import { AuthEffects } from './auth/auth.effects'; import { AuthEffects } from './auth/auth.effects';
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
export const coreEffects = [ export const coreEffects = [
ResponseCacheEffects, ResponseCacheEffects,
RequestEffects, RequestEffects,
ObjectCacheEffects, ObjectCacheEffects,
UUIDIndexEffects, UUIDIndexEffects,
AuthEffects AuthEffects,
ServerSyncBufferEffects
]; ];

View File

@@ -121,7 +121,7 @@ export abstract class DataService<TNormalized extends NormalizedObject, TDomain>
// .filter((href: string) => hasValue(href)) // .filter((href: string) => hasValue(href))
// .take(1) // .take(1)
// .subscribe((href: string) => { // .subscribe((href: string) => {
// const request = new RestRequest(this.requestService.generateRequestId(), href, RestRequestMethod.Post, dso); // const request = new RestRequest(this.requestService.generateRequestId(), href, RestRequestMethod.POST, dso);
// this.requestService.configure(request); // this.requestService.configure(request);
// }); // });
// //

View File

@@ -1,7 +1,5 @@
import { SortOptions } from '../cache/models/sort-options.model'; import { SortOptions } from '../cache/models/sort-options.model';
import { GenericConstructor } from '../shared/generic-constructor'; import { GenericConstructor } from '../shared/generic-constructor';
import { GlobalConfig } from '../../../config/global-config.interface';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service'; import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
import { DSOResponseParsingService } from './dso-response-parsing.service'; import { DSOResponseParsingService } from './dso-response-parsing.service';
import { ResponseParsingService } from './parsing.service'; import { ResponseParsingService } from './parsing.service';
@@ -10,35 +8,17 @@ import { BrowseResponseParsingService } from './browse-response-parsing.service'
import { ConfigResponseParsingService } from './config-response-parsing.service'; import { ConfigResponseParsingService } from './config-response-parsing.service';
import { AuthResponseParsingService } from '../auth/auth-response-parsing.service'; import { AuthResponseParsingService } from '../auth/auth-response-parsing.service';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { HttpHeaders } from '@angular/common/http';
import { IntegrationResponseParsingService } from '../integration/integration-response-parsing.service'; import { IntegrationResponseParsingService } from '../integration/integration-response-parsing.service';
import { RestRequestMethod } from './/rest-request-method';
/* tslint:disable:max-classes-per-file */ /* tslint:disable:max-classes-per-file */
/**
* Represents a Request Method.
*
* I didn't reuse the RequestMethod enum in @angular/http because
* it uses numbers. The string values here are more clear when
* debugging.
*
* The ones commented out are still unsupported in the rest of the codebase
*/
export enum RestRequestMethod {
Get = 'GET',
Post = 'POST',
Put = 'PUT',
Delete = 'DELETE',
Options = 'OPTIONS',
Head = 'HEAD',
Patch = 'PATCH'
}
export abstract class RestRequest { export abstract class RestRequest {
constructor( constructor(
public uuid: string, public uuid: string,
public href: string, public href: string,
public method: RestRequestMethod = RestRequestMethod.Get, public method: RestRequestMethod = RestRequestMethod.GET,
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
@@ -56,7 +36,7 @@ export class GetRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Get, body) super(uuid, href, RestRequestMethod.GET, body)
} }
} }
@@ -67,7 +47,7 @@ export class PostRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Post, body) super(uuid, href, RestRequestMethod.POST, body)
} }
} }
@@ -78,7 +58,7 @@ export class PutRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Put, body) super(uuid, href, RestRequestMethod.PUT, body)
} }
} }
@@ -89,7 +69,7 @@ export class DeleteRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Delete, body) super(uuid, href, RestRequestMethod.DELETE, body)
} }
} }
@@ -100,7 +80,7 @@ export class OptionsRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Options, body) super(uuid, href, RestRequestMethod.OPTIONS, body)
} }
} }
@@ -111,7 +91,7 @@ export class HeadRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Head, body) super(uuid, href, RestRequestMethod.HEAD, body)
} }
} }
@@ -122,7 +102,7 @@ export class PatchRequest extends RestRequest {
public body?: any, public body?: any,
public options?: HttpOptions public options?: HttpOptions
) { ) {
super(uuid, href, RestRequestMethod.Patch, body) super(uuid, href, RestRequestMethod.PATCH, body)
} }
} }

View File

@@ -14,10 +14,11 @@ import { IndexName } from '../index/index.reducer';
import { pathSelector } from '../shared/selectors'; 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, RestRequestMethod } 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';
@Injectable() @Injectable()
export class RequestService { export class RequestService {
@@ -71,7 +72,7 @@ export class RequestService {
// TODO to review "overrideRequest" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed // TODO to review "overrideRequest" 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 (!isGetRequest || !this.isCachedOrPending(request) || forceBypassCache) { if (!isGetRequest || !this.isCachedOrPending(request) || forceBypassCache) {
this.dispatchRequest(request); this.dispatchRequest(request);
if (isGetRequest && !forceBypassCache) { if (isGetRequest && !forceBypassCache) {

View File

@@ -0,0 +1,18 @@
/**
* Represents a Request Method.
*
* I didn't reuse the RequestMethod enum in @angular/http because
* it uses numbers. The string values here are more clear when
* debugging.
*
* The ones commented out are still unsupported in the rest of the codebase
*/
export enum RestRequestMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
OPTIONS = 'OPTIONS',
HEAD = 'HEAD',
PATCH = 'PATCH'
}

View File

@@ -3,10 +3,10 @@ import {catchError, map} from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Request } from '@angular/http'; import { Request } from '@angular/http';
import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http' import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'
import { RestRequestMethod } from '../data/request.models';
import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model'; import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model';
import { HttpObserve } from '@angular/common/http/src/client'; import { HttpObserve } from '@angular/common/http/src/client';
import { RestRequestMethod } from '../data//rest-request-method';
export interface HttpOptions { export interface HttpOptions {
body?: any; body?: any;

View File

@@ -7,10 +7,10 @@ import {
RemoveFromObjectCacheAction RemoveFromObjectCacheAction
} from '../cache/object-cache.actions'; } from '../cache/object-cache.actions';
import { RequestActionTypes, RequestConfigureAction } from '../data/request.actions'; import { RequestActionTypes, RequestConfigureAction } from '../data/request.actions';
import { RestRequestMethod } from '../data/request.models';
import { AddToIndexAction, RemoveFromIndexByValueAction } from './index.actions'; import { AddToIndexAction, RemoveFromIndexByValueAction } from './index.actions';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { IndexName } from './index.reducer'; import { IndexName } from './index.reducer';
import { RestRequestMethod } from '../data//rest-request-method';
@Injectable() @Injectable()
export class UUIDIndexEffects { export class UUIDIndexEffects {
@@ -42,7 +42,7 @@ export class UUIDIndexEffects {
@Effect() addRequest$ = this.actions$ @Effect() addRequest$ = this.actions$
.pipe( .pipe(
ofType(RequestActionTypes.CONFIGURE), ofType(RequestActionTypes.CONFIGURE),
filter((action: RequestConfigureAction) => action.payload.method === RestRequestMethod.Get), filter((action: RequestConfigureAction) => action.payload.method === RestRequestMethod.GET),
map((action: RequestConfigureAction) => { map((action: RequestConfigureAction) => {
return new AddToIndexAction( return new AddToIndexAction(
IndexName.REQUEST, IndexName.REQUEST,

View File

@@ -1,6 +1,5 @@
import { RestRequestMethod } from '../app/core/data/request.models'; import { RestRequestMethod } from '../app/core/data//rest-request-method';
/* enum indices */
type TimePerMethod = { type TimePerMethod = {
[method in RestRequestMethod]: number; [method in RestRequestMethod]: number;
}; };

View File

@@ -4,5 +4,5 @@ import { AutoSyncConfig } from './auto-sync-config.interface';
export interface CacheConfig extends Config { export interface CacheConfig extends Config {
msToLive: number, msToLive: number,
control: string, control: string,
autoSync: AutoSyncConfig // autoSync: AutoSyncConfig
} }