update generic service

This commit is contained in:
Dan Percic 2021-09-27 21:03:43 +03:00
parent 5f5f753583
commit c09cd590b1
6 changed files with 118 additions and 79 deletions

View File

@ -38,8 +38,8 @@ export class EntitiesService<E extends IListable, I = E> extends GenericService<
private _displayed: E[] = [];
private readonly _selected$ = new BehaviorSubject<(string | number)[]>([]);
constructor(protected readonly _injector: Injector, @Optional() @Inject(ENTITY_PATH) protected readonly _defaultEntityPath = '') {
super(_injector, _defaultEntityPath);
constructor(protected readonly _injector: Injector, @Optional() @Inject(ENTITY_PATH) protected readonly _defaultModelPath = '') {
super(_injector, _defaultModelPath);
this.all$ = this._all$.asObservable();
this.allLength$ = this._all$.pipe(getLength);

View File

@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { KeysOf } from '../utils';
import { IListable } from '../listing';
@Injectable()
@ -24,8 +23,6 @@ export class SearchService<T extends IListable> {
return entities.filter(entity => entity.searchKey?.includes(searchValue));
}
setSearchKey(value: KeysOf<T>): void {}
reset(): void {
this._query$.next('');
}

View File

@ -1,8 +1,8 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { BASE_PATH, Configuration, List } from '@redaction/red-ui-http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injector } from '@angular/core';
import { Observable } from 'rxjs';
import { CustomHttpUrlEncodingCodec, RequiredParam, Validate } from '../utils';
import { CustomHttpUrlEncodingCodec, List, RequiredParam, Validate } from '../utils';
import { HeadersConfiguration } from '../utils/headers-configuration';
export interface HeaderOptions {
readonly authorization?: boolean;
@ -10,34 +10,29 @@ export interface HeaderOptions {
readonly contentType?: boolean;
}
export abstract class GenericService<Entity> {
protected readonly _defaultHeaders = new HttpHeaders();
protected readonly _configuration = this._injector.get(Configuration, new Configuration());
protected readonly _http = this._injector.get(HttpClient);
protected readonly _basePath = this._injector.get(BASE_PATH, this._configuration.basePath);
export interface QueryParam {
readonly key: string;
readonly value: string;
}
protected constructor(protected readonly _injector: Injector, protected readonly _defaultEntityPath: string) {}
export abstract class GenericService<Entity> {
protected readonly _http = this._injector.get(HttpClient);
protected constructor(protected readonly _injector: Injector, protected readonly _defaultModelPath: string) {}
@Validate()
post<R = Entity>(@RequiredParam() body: unknown | List<[string, string]>, entityPath = this._defaultEntityPath): Observable<R> {
let queryParams;
getAll<R = Entity>(modelPath = this._defaultModelPath): Observable<R[]> {
console.log(`GET request from ${this.constructor.name}`);
if (Array.isArray(body) && Array.isArray(body[0]) && body[0].length === 2) {
queryParams = this._queryParams(body);
}
console.log(`POST request from ${this.constructor.name} with body `, body);
return this._http.post<R>(`${this._basePath}/${entityPath}`, body, {
withCredentials: this._configuration.withCredentials,
params: queryParams,
headers: this._headers(),
return this._http.get<R[]>(`/${encodeURI(modelPath)}`, {
headers: HeadersConfiguration.getHeaders({ contentType: false }),
observe: 'body'
});
}
@Validate()
delete(@RequiredParam() body: string | List<[string, string]>, entityPath = this._defaultEntityPath): Observable<unknown> {
let path = `${this._basePath}/${entityPath}`;
delete(@RequiredParam() body: string | List<QueryParam>, modelPath = this._defaultModelPath): Observable<unknown> {
let path = `/${encodeURI(modelPath)}`;
let queryParams;
if (typeof body === 'string') {
@ -48,77 +43,45 @@ export abstract class GenericService<Entity> {
console.log(`DELETE request from ${this.constructor.name} with body `, body);
return this._http.delete(path, {
withCredentials: this._configuration.withCredentials,
params: queryParams,
headers: this._headers({ contentType: false }),
headers: HeadersConfiguration.getHeaders({ contentType: false }),
observe: 'body'
});
}
@Validate()
getAll<R = Entity>(entityPath = this._defaultEntityPath): Observable<R[]> {
console.log(`GET request from ${this.constructor.name}`);
return this._http.get<R[]>(`${this._basePath}/${entityPath}`, {
withCredentials: this._configuration.withCredentials,
headers: this._headers({ contentType: false }),
protected _post<R = Entity>(
@RequiredParam() body: unknown,
modelPath = this._defaultModelPath,
queryParams?: List<QueryParam>
): Observable<R> {
console.log(`POST request from ${this.constructor.name} with body `, body);
console.log('To path ', `/${encodeURI(modelPath)}`);
return this._http.post<R>(`/${encodeURI(modelPath)}`, body, {
params: queryParams ? this._queryParams(queryParams) : undefined,
headers: HeadersConfiguration.getHeaders(),
observe: 'body'
});
}
@Validate()
getOne<R = Entity>(@RequiredParam() entityId: string, entityPath = this._defaultEntityPath): Observable<R> {
const path = `${this._basePath}/${entityPath}/${encodeURIComponent(String(entityId))}`;
protected _getOne<R = Entity>(@RequiredParam() path: List, modelPath = this._defaultModelPath): Observable<R> {
const entityPath = path.map(item => encodeURIComponent(item)).join('/');
console.log(`GET request from ${this.constructor.name} with id ${entityId}`);
return this._http.get<R>(path, {
withCredentials: this._configuration.withCredentials,
headers: this._headers({ contentType: false }),
console.log(`GET request from ${this.constructor.name} with path ${encodeURI(modelPath)}/${entityPath}`);
return this._http.get<R>(`/${encodeURI(modelPath)}/${entityPath}`, {
headers: HeadersConfiguration.getHeaders({ contentType: false }),
observe: 'body'
});
}
get<R = Entity>(): Observable<R[]>;
get<R = Entity>(entityId: string, entityPath?: string): Observable<R>;
get<R = Entity>(entityId?: string, entityPath = this._defaultEntityPath): Observable<R[] | R> {
if (entityId) {
return this.getOne(entityId, entityPath);
}
return this.getAll(entityPath);
}
protected _queryParams(queryParams: List<[string, string]>): HttpParams {
protected _queryParams(queryParams: List<QueryParam>): HttpParams {
let queryParameters = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });
for (const [key, value] of queryParams) {
queryParameters = queryParameters.set(key, value);
for (const param of queryParams) {
queryParameters = queryParameters.set(param.key, param.value);
}
return queryParameters;
}
protected _headers(options?: HeaderOptions): HttpHeaders {
let headers = this._defaultHeaders;
// authentication (RED-OAUTH) required
if ((options?.authorization === undefined || options.authorization) && this._configuration.accessToken) {
const accessToken =
typeof this._configuration.accessToken === 'function' ? this._configuration.accessToken() : this._configuration.accessToken;
headers = headers.set('Authorization', 'Bearer ' + accessToken);
}
if (options?.accept === undefined || options.accept) {
const httpHeaderAcceptSelected = this._configuration.selectHeaderAccept(['application/json']);
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
}
if (options?.contentType === undefined || options.contentType) {
const httpContentTypeSelected = this._configuration.selectHeaderContentType(['application/json']);
if (httpContentTypeSelected !== undefined) {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
}
return headers;
}
}

View File

@ -0,0 +1,77 @@
import { HttpHeaders } from '@angular/common/http';
import { HeaderOptions } from '@iqser/common-ui';
export class HeadersConfiguration {
static getHeaders(options?: HeaderOptions): HttpHeaders {
let headers = new HttpHeaders();
if (options?.accept === undefined || options.accept) {
const httpHeaderAcceptSelected = HeadersConfiguration.selectHeaderAccept(['application/json']);
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
}
if (options?.contentType === undefined || options.contentType) {
const httpContentTypeSelected = HeadersConfiguration.selectHeaderContentType(['application/json']);
if (httpContentTypeSelected !== undefined) {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
}
return headers;
}
/**
* Select the correct content-type to use for a request.
* Uses {@link HeadersConfiguration#isJsonMime} to determine the correct content-type.
* If no content type is found return the first found type if the contentTypes is not empty
* @param contentTypes - the array of content types that are available for selection
* @returns the selected content-type or <code>undefined</code> if no selection could be made.
*/
static selectHeaderContentType(contentTypes: string[]): string | undefined {
if (contentTypes.length === 0) {
return undefined;
}
const type = contentTypes.find(x => this.isJsonMime(x));
if (type === undefined) {
return contentTypes[0];
}
return type;
}
/**
* Select the correct accept content-type to use for a request.
* Uses {@link HeadersConfiguration#isJsonMime} to determine the correct accept content-type.
* If no content type is found return the first found type if the contentTypes is not empty
* @param accepts - the array of content types that are available for selection.
* @returns the selected content-type or <code>undefined</code> if no selection could be made.
*/
static selectHeaderAccept(accepts: string[]): string | undefined {
if (accepts.length === 0) {
return undefined;
}
const type = accepts.find(x => this.isJsonMime(x));
if (type === undefined) {
return accepts[0];
}
return type;
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
* @param mime - MIME (Multipurpose Internet Mail Extensions)
* @return True if the given MIME is JSON, false otherwise.
*/
static isJsonMime(mime: string): boolean {
const jsonMime = new RegExp('^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
}
}

View File

@ -11,3 +11,4 @@ export * from './decorators/required-param.decorator';
export * from './decorators/debounce.decorator';
export * from './decorators/on-change.decorator';
export * from './http-encoder';
export * from './types/iqser-types';

View File

@ -0,0 +1 @@
export type List<T = string> = readonly T[];