1
0

Add config endpoint service

This commit is contained in:
Giuseppe Digilio
2017-10-26 15:04:03 +02:00
parent 311fef21f4
commit 01f26bd8c2
15 changed files with 364 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
import { RequestError } from '../data/request.models';
import { PageInfo } from '../shared/page-info.model';
import { ConfigObject } from '../shared/config/config.model';
/* tslint:disable:max-classes-per-file */
export class RestResponse {
@@ -41,4 +42,13 @@ export class ErrorResponse extends RestResponse {
this.errorMessage = error.message;
}
}
export class ConfigSuccessResponse extends RestResponse {
constructor(
public configDefinition: ConfigObject[],
public statusCode: string
) {
super(true, statusCode);
}
}
/* tslint:enable:max-classes-per-file */

View File

@@ -0,0 +1,137 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { RequestService } from '../data/request.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { GlobalConfig } from '../../../config/global-config.interface';
import { ConfigSuccessResponse, EndpointMap, RootSuccessResponse } from '../cache/response-cache.models';
import { ConfigRequest, FindAllOptions, RestRequest, RootEndpointRequest } from '../data/request.models';
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { ConfigObject } from '../shared/config/config.model';
@Injectable()
export abstract class ConfigService {
protected request: ConfigRequest;
protected abstract responseCache: ResponseCacheService;
protected abstract requestService: RequestService;
protected abstract linkName: string;
protected abstract EnvConfig: GlobalConfig;
protected abstract browseEndpoint: string;
protected getConfig(request: RestRequest): Observable<ConfigObject[]> {
return this.responseCache.get(request.href)
.map((entry: ResponseCacheEntry) => entry.response)
.filter((response: ConfigSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.configDefinition))
.map((response: ConfigSuccessResponse) => response.configDefinition)
.distinctUntilChanged();
}
protected getConfigByIDHref(endpoint, resourceID): string {
return `${endpoint}/${resourceID}`;
}
protected getConfigSearchHref(endpoint, options: FindAllOptions = {}): string {
let result;
const args = [];
if (hasValue(options.scopeID)) {
result = `${endpoint}/${this.browseEndpoint}`
args.push(`uuid=${options.scopeID}`);
} else {
result = endpoint;
}
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
args.push(`page=${options.currentPage - 1}`);
}
if (hasValue(options.elementsPerPage)) {
args.push(`size=${options.elementsPerPage}`);
}
if (hasValue(options.sort)) {
let direction = 'asc';
if (options.sort.direction === 1) {
direction = 'desc';
}
args.push(`sort=${options.sort.field},${direction}`);
}
if (isNotEmpty(args)) {
result = `${result}?${args.join('&')}`;
}
return result;
}
protected getEndpointMap(): Observable<EndpointMap> {
const request = new RootEndpointRequest(this.EnvConfig);
setTimeout(() => {
this.requestService.configure(request);
}, 0);
return this.responseCache.get(request.href)
.map((entry: ResponseCacheEntry) => entry.response)
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
.map((response: RootSuccessResponse) => response.endpointMap)
.distinctUntilChanged();
}
public getConfigAll(): Observable<ConfigObject[]> {
return this.getEndpoint()
.filter((href: string) => isNotEmpty(href))
.distinctUntilChanged()
.map((endpointURL: string) => new ConfigRequest(endpointURL))
.do((request: RestRequest) => {
setTimeout(() => {
this.requestService.configure(request);
}, 0);
})
.flatMap((request: RestRequest) => this.getConfig(request))
.distinctUntilChanged();
}
public getConfigByHref(href: string): Observable<ConfigObject[]> {
const request = new ConfigRequest(href);
this.requestService.configure(request);
return this.getConfig(request);
}
public getConfigById(id: string): Observable<ConfigObject[]> {
return this.getEndpoint()
.map((endpoint: string) => this.getConfigByIDHref(endpoint, id))
.filter((href: string) => isNotEmpty(href))
.distinctUntilChanged()
.map((endpointURL: string) => new ConfigRequest(endpointURL))
.do((request: RestRequest) => {
setTimeout(() => {
this.requestService.configure(request);
}, 0);
})
.flatMap((request: RestRequest) => this.getConfig(request))
.distinctUntilChanged();
}
public getConfigBySearch(options: FindAllOptions = {}): Observable<ConfigObject[]> {
return this.getEndpoint()
.map((endpoint: string) => this.getConfigSearchHref(endpoint, options))
.filter((href: string) => isNotEmpty(href))
.distinctUntilChanged()
.map((endpointURL: string) => new ConfigRequest(endpointURL))
.do((request: RestRequest) => {
setTimeout(() => {
this.requestService.configure(request);
}, 0);
})
.flatMap((request: RestRequest) => this.getConfig(request))
.distinctUntilChanged();
}
public getEndpoint(): Observable<string> {
return this.getEndpointMap()
.map((map: EndpointMap) => map[this.linkName])
.distinctUntilChanged();
}
}

View File

@@ -0,0 +1,21 @@
import { Inject, Injectable } from '@angular/core';
import { ConfigService } from './config.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service';
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
@Injectable()
export class SubmissionDefinitionsConfigService extends ConfigService {
protected linkName = 'submissiondefinitions';
protected browseEndpoint = 'search/findByCollection';
constructor(
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig) {
super();
}
}

View File

@@ -0,0 +1,21 @@
import { Inject, Injectable } from '@angular/core';
import { ConfigService } from './config.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service';
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
@Injectable()
export class SubmissionFormsConfigService extends ConfigService {
protected linkName = 'submissionforms';
protected browseEndpoint = '';
constructor(
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig) {
super();
}
}

View File

@@ -0,0 +1,21 @@
import { Inject, Injectable } from '@angular/core';
import { ConfigService } from './config.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from '../data/request.service';
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
@Injectable()
export class SubmissionSectionsConfigService extends ConfigService {
protected linkName = 'submissionsections';
protected browseEndpoint = '';
constructor(
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig) {
super();
}
}

View File

@@ -30,6 +30,10 @@ import { ResponseCacheService } from './cache/response-cache.service';
import { RootResponseParsingService } from './data/root-response-parsing.service';
import { ServerResponseService } from '../shared/server-response.service';
import { NativeWindowFactory, NativeWindowService } from '../shared/window.service';
import { SubmissionDefinitionsConfigService } from './config/submission-definitions-config.service';
import { ConfigResponseParsingService } from './data/config-response-parsing.service';
import { SubmissionFormsConfigService } from './config/submission-forms-config.service';
import { SubmissionSectionsConfigService } from './config/submission-sections-config.service';
const IMPORTS = [
CommonModule,
@@ -61,6 +65,10 @@ const PROVIDERS = [
ResponseCacheService,
RootResponseParsingService,
ServerResponseService,
ConfigResponseParsingService,
SubmissionDefinitionsConfigService,
SubmissionFormsConfigService,
SubmissionSectionsConfigService,
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
];

View File

@@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import { ResponseParsingService } from './parsing.service';
import { RestRequest } from './request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { ConfigSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
import { isNotEmpty } from '../../shared/empty.util';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { ConfigObjectFactory } from '../shared/config/config-object-factory';
@Injectable()
export class ConfigResponseParsingService implements ResponseParsingService {
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links)) {
let configDefinition;
if (isNotEmpty(data.payload._embedded) && Array.isArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]])) {
const type = Object.keys(data.payload._embedded)[0];
const serializer = new DSpaceRESTv2Serializer(ConfigObjectFactory.getConstructor(type));
configDefinition = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]);
} else {
const serializer = new DSpaceRESTv2Serializer(ConfigObjectFactory.getConstructor(data.payload.type));
configDefinition = serializer.deserialize(data.payload);
}
return new ConfigSuccessResponse(configDefinition, data.statusCode);
} else {
return new ErrorResponse(
Object.assign(
new Error('Unexpected response from config endpoint'),
{statusText: data.statusCode}
)
);
}
}
}

View File

@@ -5,6 +5,7 @@ import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
import { DSOResponseParsingService } from './dso-response-parsing.service';
import { ResponseParsingService } from './parsing.service';
import { RootResponseParsingService } from './root-response-parsing.service';
import { ConfigResponseParsingService } from './config-response-parsing.service';
/* tslint:disable:max-classes-per-file */
export class RestRequest {
@@ -53,6 +54,16 @@ export class RootEndpointRequest extends RestRequest {
}
}
export class ConfigRequest extends RestRequest {
constructor(href: string) {
super(href);
}
getResponseParser(): GenericConstructor<ResponseParsingService> {
return ConfigResponseParsingService;
}
}
export class RequestError extends Error {
statusText: string;
}

View File

@@ -1,5 +1,6 @@
export interface DSpaceRESTV2Response {
payload: {
[name: string]: string;
_embedded?: any;
_links?: any;
page?: any;

View File

@@ -0,0 +1,30 @@
import { GenericConstructor } from '../../shared/generic-constructor';
import { SubmissionSectionModel } from './config-submission-section.model';
import { SubmissionFormsModel } from './config-submission-forms.model';
import { SubmissionDefinitionsModel } from './config-submission-definitions.model';
import { ConfigType } from './config-type';
import { ConfigObject } from './config.model';
export class ConfigObjectFactory {
public static getConstructor(type): GenericConstructor<ConfigObject> {
switch (type) {
case ConfigType.SubmissionDefinition:
case ConfigType.SubmissionDefinitions: {
return SubmissionDefinitionsModel
}
case ConfigType.SubmissionForm:
case ConfigType.SubmissionForms: {
return SubmissionFormsModel
}
case ConfigType.SubmissionSection:
case ConfigType.SubmissionSections: {
return SubmissionSectionModel
}
default: {
return undefined;
}
}
}
}

View File

@@ -0,0 +1,15 @@
import { autoserialize, inheritSerialization } from 'cerialize';
import { ConfigObject } from './config.model';
import { SubmissionSectionModel } from './config-submission-section.model';
import { RemoteData } from '../../data/remote-data';
@inheritSerialization(ConfigObject)
export class SubmissionDefinitionsModel extends ConfigObject {
@autoserialize
isDefault: boolean;
@autoserialize
sections: RemoteData<SubmissionSectionModel[]>;
}

View File

@@ -0,0 +1,10 @@
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
import { ConfigObject } from './config.model';
@inheritSerialization(ConfigObject)
export class SubmissionFormsModel extends ConfigObject {
@autoserialize
fields: any[];
}

View File

@@ -0,0 +1,16 @@
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
import { ConfigObject } from './config.model';
@inheritSerialization(ConfigObject)
export class SubmissionSectionModel extends ConfigObject {
@autoserialize
header: string;
@autoserialize
mandatory: boolean;
@autoserialize
sectionType: string;
}

View File

@@ -0,0 +1,12 @@
/**
* TODO replace with actual string enum after upgrade to TypeScript 2.4:
* https://github.com/Microsoft/TypeScript/pull/15486
*/
export enum ConfigType {
SubmissionDefinitions = 'submissiondefinitions',
SubmissionDefinition = 'submissiondefinition',
SubmissionForm = 'submissionform',
SubmissionForms = 'submissionforms',
SubmissionSections = 'submissionsections',
SubmissionSection = 'submissionsection'
}

View File

@@ -0,0 +1,15 @@
import { autoserialize, autoserializeAs } from 'cerialize';
export abstract class ConfigObject {
@autoserialize
public name: string;
@autoserialize
public type: string;
@autoserialize
public _links: {
[name: string]: string
}
}