149 lines
5.2 KiB
TypeScript
149 lines
5.2 KiB
TypeScript
import { HttpClient, HttpEvent, HttpParams } from '@angular/common/http';
|
|
import { Injector } from '@angular/core';
|
|
import { Observable } from 'rxjs';
|
|
import { CustomHttpUrlEncodingCodec, HeadersConfiguration, List, RequiredParam, Validate } from '../utils';
|
|
import { map, tap } from 'rxjs/operators';
|
|
|
|
const ROOT_CHANGES_KEY = 'root';
|
|
|
|
export interface HeaderOptions {
|
|
readonly authorization?: boolean;
|
|
readonly accept?: boolean;
|
|
readonly contentType?: boolean;
|
|
}
|
|
|
|
export interface QueryParam {
|
|
readonly key: string;
|
|
readonly value: string | number | boolean;
|
|
}
|
|
|
|
/**
|
|
* I for interface
|
|
* R for response
|
|
*/
|
|
export abstract class GenericService<I> {
|
|
protected readonly _http = this._injector.get(HttpClient);
|
|
protected readonly _lastCheckedForChanges = new Map<string, string>([[ROOT_CHANGES_KEY, '0']]);
|
|
|
|
protected constructor(protected readonly _injector: Injector, protected readonly _defaultModelPath: string) {}
|
|
|
|
get<T = I[]>(): Observable<T>;
|
|
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
|
get<T = I>(id: string, ...args: unknown[]): Observable<T>;
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
get<T>(id?: string, ...args: unknown[]): Observable<T> {
|
|
return id ? this._getOne<T>([id]) : this.getAll<T>();
|
|
}
|
|
|
|
getAll<R = I[]>(modelPath = this._defaultModelPath, queryParams?: List<QueryParam>): Observable<R> {
|
|
return this._http
|
|
.get<R>(`/${encodeURI(modelPath)}`, {
|
|
headers: HeadersConfiguration.getHeaders({ contentType: false }),
|
|
observe: 'body',
|
|
params: this._queryParams(queryParams),
|
|
})
|
|
.pipe(tap(() => this._updateLastChanged()));
|
|
}
|
|
|
|
getFor<R = I[]>(entityId: string, queryParams?: List<QueryParam>): Observable<R> {
|
|
return this.getAll<R>(`${this._defaultModelPath}/${entityId}`, queryParams).pipe(tap(() => this._updateLastChanged(entityId)));
|
|
}
|
|
|
|
@Validate()
|
|
delete(@RequiredParam() body: unknown, modelPath = this._defaultModelPath, queryParams?: List<QueryParam>): Observable<unknown> {
|
|
let path = `/${encodeURI(modelPath)}`;
|
|
|
|
if (typeof body === 'string') {
|
|
path += `/${encodeURIComponent(body)}`;
|
|
}
|
|
|
|
return this._http.delete(path, {
|
|
body: body,
|
|
params: this._queryParams(queryParams),
|
|
headers: HeadersConfiguration.getHeaders({ contentType: false }),
|
|
observe: 'body',
|
|
});
|
|
}
|
|
|
|
@Validate()
|
|
upload<R = I>(data?: Blob, modelPath = this._defaultModelPath): Observable<HttpEvent<R>> {
|
|
const formParams = new FormData();
|
|
|
|
if (data !== undefined) {
|
|
formParams.append('file', data);
|
|
}
|
|
|
|
const headers = HeadersConfiguration.getHeaders({ contentType: false });
|
|
|
|
return this._http.post<R>(`/${encodeURI(modelPath)}`, formParams, {
|
|
headers,
|
|
observe: 'events',
|
|
reportProgress: true,
|
|
});
|
|
}
|
|
|
|
hasChanges$(key = ROOT_CHANGES_KEY): Observable<boolean> {
|
|
const modelPath = key === ROOT_CHANGES_KEY ? '' : `${key}/`;
|
|
const body = { value: this._lastCheckedForChanges.get(key) ?? '0' };
|
|
return this._post<{ value: boolean }>(body, `${this._defaultModelPath}/${modelPath}changes`).pipe(
|
|
map((res: { value: boolean }) => res.value),
|
|
);
|
|
}
|
|
|
|
@Validate()
|
|
protected _post<R = I>(
|
|
@RequiredParam() body: unknown,
|
|
modelPath = this._defaultModelPath,
|
|
queryParams?: List<QueryParam>,
|
|
): Observable<R> {
|
|
return this._http.post<R>(`/${encodeURI(modelPath)}`, body, {
|
|
params: this._queryParams(queryParams),
|
|
headers: HeadersConfiguration.getHeaders(),
|
|
observe: 'body',
|
|
});
|
|
}
|
|
|
|
@Validate()
|
|
protected _put<R = I>(
|
|
@RequiredParam() body: unknown,
|
|
modelPath = this._defaultModelPath,
|
|
queryParams?: List<QueryParam>,
|
|
): Observable<R> {
|
|
return this._http.put<R>(`/${encodeURI(modelPath)}`, body, {
|
|
params: this._queryParams(queryParams),
|
|
headers: HeadersConfiguration.getHeaders(),
|
|
observe: 'body',
|
|
});
|
|
}
|
|
|
|
@Validate()
|
|
protected _getOne<R = I>(
|
|
@RequiredParam() path: List,
|
|
modelPath = this._defaultModelPath,
|
|
queryParams?: List<QueryParam>,
|
|
): Observable<R> {
|
|
const entityPath = path.map(item => encodeURIComponent(item)).join('/');
|
|
|
|
return this._http.get<R>(`/${encodeURI(modelPath)}/${entityPath}`, {
|
|
headers: HeadersConfiguration.getHeaders({ contentType: false }),
|
|
params: this._queryParams(queryParams),
|
|
observe: 'body',
|
|
});
|
|
}
|
|
|
|
protected _queryParams(queryParams?: List<QueryParam>): HttpParams {
|
|
let queryParameters = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });
|
|
queryParams?.forEach(param => {
|
|
if (param?.value !== undefined && param?.value !== null) {
|
|
queryParameters = queryParameters.append(param.key, param.value);
|
|
}
|
|
});
|
|
|
|
return queryParameters;
|
|
}
|
|
|
|
protected _updateLastChanged(key = ROOT_CHANGES_KEY): void {
|
|
this._lastCheckedForChanges.set(key, new Date().toISOString());
|
|
}
|
|
}
|