src/lib/service/backend/backend.service.ts
HttpOptions for http request
Properties |
|
headers |
headers:
|
Type : HttpHeaders | literal type
|
Optional |
observe |
observe:
|
Type : any
|
Optional |
params |
params:
|
Type : HttpParams | literal type
|
Optional |
reportProgress |
reportProgress:
|
Type : boolean
|
Optional |
responseType |
responseType:
|
Type : any
|
Optional |
withCredentials |
withCredentials:
|
Type : boolean
|
Optional |
BackendService
encapsulates the communication with the yuuvis® RAD REST endpoints.
As yuuvis® RAD provides different backend services, this service is also capable of providing the base URIs for those services.
import {of as observableOf, Observable, MonoTypeOperatorFunction} from 'rxjs';
import {map, tap} from 'rxjs/operators';
import {Inject, Injectable} from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import {Config} from '../config/config.service';
import {DmsObject} from '../../model/dms-object.model';
import * as FileSaver from 'file-saver';
import {Logger} from '../logger/logger';
import {AuthProfile} from '../auth/auth-profile.interface';
import {HttpParams} from '@angular/common/http';
import {finalize, shareReplay} from 'rxjs/operators';
import {CoreConfig} from '../../config/core-config';
import {CORE_CONFIG} from '../../config/core-init.tokens';
/**
* HttpOptions for http request
* @param observe: 'body' | 'events' | 'response'
* @param responseType: 'arraybuffer' | 'blob' | 'json' | 'text'
*/
export interface HttpOptions {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: any;
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: any;
withCredentials?: boolean;
}
// @ts-ignore
/**
* `BackendService` encapsulates the communication with the yuuvis<sup>®</sup> RAD REST endpoints.
*
* As yuuvis<sup>®</sup> RAD provides different services, this service is also capable of providing the base URIs for those services.
*/
@Injectable({
providedIn: 'root'
})
export class BackendService {
public gridDataFilter: MonoTypeOperatorFunction<any> = map((data: any[]) => data?.filter((v: any) => !v.__removed));
private authProfile: AuthProfile;
private cache = new Map<string, any>();
private temp = new Map<string, Observable<any>>();
private headers = this.setDefaultHeaders();
/**
* @ignore
*/
constructor(private http: HttpClient,
@Inject(CORE_CONFIG) public coreConfig: CoreConfig,
private logger: Logger,
private config: Config) {
/**
* default profile is supposed to use NTLM
* @type {host: string; ntlm: boolean}
*/
this.authProfile = {
host: '',
ntlm: true
};
}
/**
* @ignore
*/
public setAuthProfile(profile?: AuthProfile) {
if (profile) {
this.headers = this.setDefaultHeaders();
this.authProfile = profile;
if (this.authProfile.auth) {
// this.setHeader('Authorization', this.authProfile.auth);
}
this.headers.delete('Authorization');
}
}
/**
* Add a new header.
* @param key The headers name
* @param value The value
*/
public setHeader(key: string, value: string) {
if (value && value.length) {
this.headers = this.headers.set(key, value);
}
}
/**
* Retrieves the current headers
* @returns The `HttpHeaders` that are currently set up
*/
public getHeaders(): HttpHeaders {
return this.headers;
}
/**
* @ignore
*/
public getHttpOptions(requestOptions?: HttpOptions): HttpOptions {
return Object.assign({headers: this.getHeaders(), withCredentials: this.coreConfig.withCredentials}, requestOptions);
}
/**
* Gets the base URI for the DMS-Service endpoint
* @returns Base URI
*/
public getServiceBase() {
return this.config.getUri('serviceBase');
}
/**
* Gets the base URI for the Search-Service endpoint
* @returns Base URI
*/
public getSearchBase() {
return this.config.getUri('searchBase');
}
/**
* Gets the base URI for the Context-Service endpoint
* @returns Base URI
*/
public getContextBase() {
return this.config.getUri('contextBase');
}
/**
* Gets the base URI for the Inbox-Service endpoint
* @returns Base URI
*/
public getInboxBase() {
return this.config.getUri('inboxBase');
}
/**
* Gets the base URI for the BPM-Service endpoint
* @returns Base URI
*/
public getBpmBase() {
return this.config.getUri('bpmBase');
}
/**
* Gets the base URI for the Agent-Service endpoint
* @returns Base URI
*/
public getAgentBase() {
return this.config.getUri('agentBase');
}
/**
* Gets the base URI for the Status-Service endpoint
* @returns Base URI
*/
public getStatusBase() {
return this.config.getUri('statusBase');
}
/**
* @ignore
*/
public getIconBase(id: string = '') {
return this.getBaseWithContext(this.getServiceBase()) + '/ui/icon/' + id;
}
/**
* @ignore
*/
public getHost() {
return this.authProfile.host || '';
}
public getBaseWithContext(base: string) {
return base?.startsWith('/') ? this.config.getContextPath() + base : base;
}
private getContextPath(base?: string): string {
return this.getHost() + (this.getBaseWithContext(base || this.getServiceBase()));
}
/**
* Wrapped HTTP GET method
* @param uri The REST URI to be queried
* @param base The Base URI (backend service) to be used
* @param requestOptions Additional request options
* @returns The data retrieved from the given endpoint
*/
public get(uri: string, base?: string, requestOptions?: HttpOptions): Observable<any> {
return this.http.get(this.getContextPath(base) + uri, this.getHttpOptions(requestOptions));
}
/**
* Wrapped HTTP PUT method
* @param uri The target REST URI
* @param data Data to be 'posted'
* @param base The Base URI (backend service) to be used
* @param requestOptions Additional request options
* @returns The return value of the target PUT endpoint
*/
public put(uri: string, data?: any, base?: string, requestOptions?: HttpOptions): Observable<any> {
return this.http.put(this.getContextPath(base) + uri, data, this.getHttpOptions(requestOptions));
}
/**
* @ignore
*/
public getJson(uri: string, base?: string): Observable<any> {
return this.http.get(this.getContextPath(base) + uri, this.getHttpOptions({responseType: 'json'}));
}
/**
* Wrapped HTTP POST method
* @param uri The target REST URI
* @param data Data to be 'posted'
* @param base The Base URI (backend service) to be used
* @param requestOptions Additional request options
* @returns The return value of the target POST endpoint
*/
public post(uri: string, data?, base?: string, requestOptions?: HttpOptions): Observable<any> {
const baseUri = this.getContextPath(base);
const payload = data ? JSON.stringify(data) : '';
return this.http.post(`${baseUri}${uri}`, payload, this.getHttpOptions(requestOptions));
}
/**
* Performs a multipart form data POST request.
* @param uri The target REST URI
* @param formData FormData to be 'posted'
* @param base The Base URI (backend service) to be used
* @param requestOptions Additional request options
* @returns The return value of the target POST endpoint
*/
public postMultiPart(uri: string, formData: FormData, base?: string, requestOptions?: HttpOptions): Observable<any> {
const baseUri = this.getContextPath(base);
const headers = this.getHeaders();
headers.delete('Content-Type');
const reqOptions = Object.assign({headers: headers, withCredentials: this.coreConfig.withCredentials}, requestOptions);
return this.http.post(`${baseUri}${uri}`, formData, reqOptions);
}
/**
* Wrapped HTTP DELETE method
* @param uri The target REST URI
* @param base The Base URI (backend service) to be used
* @param requestOptions Additional request options
* @returns The return value of the target DELETE endpoint
*/
public del(uri: string, base?: string, requestOptions?: HttpOptions): Observable<any> {
return this.http.delete(this.getContextPath(base) + uri, this.getHttpOptions(requestOptions));
}
/**
* @ignore
* Soft update for lists - enables grid to handle all changes
* to add new - only item should be specified
* to remove - only id should be specified
* to update - both params should be specified
*
* @param any[] list Original list of items
* @param id?: string; item?: any data[]
* @returns any[]
*/
public update(list: any[], data: {id?: string, item?: any}[]) {
data.forEach(({id, item}) => {
if (id) {
const _item = list.find(l => l.id === id);
Object.assign(_item, item, item ? {__updated: true} : {__removed: true});
} else if (item) {
list.unshift(Object.assign(item, {__added: true}));
}
});
return list;
}
/**
* @ignore
* Cache for small requests like icons and configs
*
* @param string uri
* @returns Observable<any>
*/
public getViaCache(uri: string): Observable<any> {
if (this.cache.has(uri)) {
return observableOf(this.cache.get(uri));
} else {
return this.getViaTempCache(uri, () => this.http.get(uri, {withCredentials: this.coreConfig.withCredentials, responseType: 'text'}).pipe(
tap(text => this.cache.set(uri, text))));
}
}
/**
* @ignore
* Temporary Cache for multiple identical requests
*
* @param string id
* @param Function request
* @returns Observable<any>
*/
public getViaTempCache(id: string, request?: Function): Observable<any> {
if (this.temp.has(id)) {
return this.temp.get(id);
} else {
const resp = (request ? request() : this.get(id)).pipe(
finalize(() => this.temp.delete(id)),
shareReplay(1)
);
this.temp.set(id, resp);
return resp;
}
}
/**
* Download the content of dms objects.
*
* @param DmsObject[] dmsObjects Array of dms objects to be downloaded
* @param "PDF" | "TIFF" | "TEXT" | "JPEG" rendition The type of rendition to be downloaded. If not specified, the original content will be downloaded.
* Possible renditions are `PDF`, `TIFF`, `TEXT`, `JPEG`.
*/
public downloadContent(dmsObjects: DmsObject[], rendition?: 'PDF' | 'TIFF' | 'TEXT' | 'JPEG', fileNameWithVersionNumber?: boolean, recyclebin?: boolean) {
const item = dmsObjects[0];
if (dmsObjects.length === 1) {
if (item.content) {
let uri = `/dms/${item.content.id}/content?type=${item.content.type}&asdownload=true&recyclebin=${!!recyclebin}`;
if (rendition) {
uri += `&rendition=${rendition}&_intent=DOWNLOAD_${rendition}`;
} else {
uri += '&_intent=DOWNLOAD';
}
if (item.content.id === item.id) {
uri += `&version=${item.version}`;
}
this.download(uri, fileNameWithVersionNumber ? item.version : null);
} else {
this.logger.error('The provided dms object has no content', item);
}
}
if (dmsObjects.length > 1) {
let uri = '/dms/batch/export';
if (item.content) {
const expressions = dmsObjects.map(obj => obj.content.id);
const data = {
query: {
expression: [...expressions]
},
options: {
rendition: ''
}
};
data.options.rendition = rendition ? 'PDF' : null;
this.downloadMulti(uri, fileNameWithVersionNumber ? item.version : null, data);
} else {
this.logger.error('The provided dms object has no content', item);
}
}
}
public download(uri: string, version?: number) {
const baseUri = this.getContextPath();
this.http.get(`${baseUri}${uri}`, {observe: 'response', responseType: 'blob', withCredentials: this.coreConfig.withCredentials})
.subscribe((res) => {
FileSaver.saveAs(
res.body,
this.getFileNameFromHttpResponse(res, version)
);
});
}
public downloadMulti(uri: string, version?: number, _body?: any) {
const baseUri = this.getContextPath();
this.http.post(`${baseUri}${uri}`, _body, {observe: 'response', responseType: 'blob', withCredentials: this.coreConfig.withCredentials})
.subscribe((res) => {
FileSaver.saveAs(
res.body,
this.getFileNameFromHttpResponse(res, version)
);
});
}
private getFileNameFromHttpResponse(res: HttpResponse<any>, version: number) {
const contentDispositionHeader = res.headers.get('Content-Disposition') || '';
const encodedFileNameMatchResult = contentDispositionHeader.match('filename\\*=UTF-8\'\'(.*)')
|| contentDispositionHeader.match('filename=\"(.*)\"');
const encodedFileName = encodedFileNameMatchResult ? encodedFileNameMatchResult[1] : null;
const filename = encodedFileName ? decodeURIComponent(encodedFileName) : 'unknown';
const index = !~filename.lastIndexOf('.') ? filename.length : filename.lastIndexOf('.');
return !version ? filename : `${filename.slice(0, index)}_v${version}${filename.slice(index)}`;
}
/**
* Getter for the HTTP Headers
*
* @returns HttpHeaders
*/
private setDefaultHeaders(): HttpHeaders {
return new HttpHeaders({
'Content-Type': 'application/json',
'X-os-include-links': 'false',
'X-os-include-actions': 'false',
'X-os-sync-index': 'true'
});
}
}