update generic service
This commit is contained in:
parent
5f5f753583
commit
c09cd590b1
@ -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);
|
||||
|
||||
|
||||
@ -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('');
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
77
src/lib/utils/headers-configuration.ts
Normal file
77
src/lib/utils/headers-configuration.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
@ -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';
|
||||
|
||||
1
src/lib/utils/types/iqser-types.ts
Normal file
1
src/lib/utils/types/iqser-types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type List<T = string> = readonly T[];
|
||||
Loading…
x
Reference in New Issue
Block a user