@opra/angular
Version:
Opra internationalization package
409 lines (402 loc) • 15.4 kB
JavaScript
import * as Angular from '@angular/common/http';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import typeIs from '@browsery/type-is';
import { HttpBackend, HttpEventType, HttpResponse, HttpClientBase, kBackend, kClient } from '@opra/client';
import { isBlob } from '@opra/common';
import { Observable } from 'rxjs';
import { isReadableStreamLike } from 'rxjs/internal/util/isReadableStreamLike';
import * as i0 from '@angular/core';
import { NgModule } from '@angular/core';
// @ts-ignore
// @ts-ignore
/**
* Angular specific implementation of {@link HttpBackend} using Angular's HttpClient.
*
* @class AngularBackend
*/
class AngularBackend extends HttpBackend {
httpClient;
/** Default request options */
defaults;
/**
* Creates a new instance of AngularBackend.
*
* @param httpClient The Angular HttpClient instance.
* @param serviceUrl The base URL of the service.
* @param options Configuration options.
*/
constructor(httpClient, serviceUrl, options) {
super(serviceUrl, options);
this.httpClient = httpClient;
this.defaults = {
...options?.defaults,
headers: options?.defaults?.headers instanceof Headers
? options?.defaults?.headers
: new Headers(options?.defaults?.headers),
params: options?.defaults?.params instanceof URLSearchParams
? options?.defaults?.params
: new URLSearchParams(options?.defaults?.params),
};
}
/**
* Handles the HTTP request using Angular's HttpClient.
*
* @param init The request initialization parameters.
* @returns An observable of {@link HttpEvent}.
*/
handle(init) {
const requestInit = this.prepareRequest(init);
const request = new Angular.HttpRequest(requestInit.method, requestInit.url.toString(), {
...requestInit,
headers: new HttpHeaders(requestInit.headers),
});
const _this = this;
return new Observable(subscriber => {
// Send request
this.send(request).subscribe({
next(event) {
if (event.type === Angular.HttpEventType.Sent) {
// Emit 'Sent' event
subscriber.next({
type: HttpEventType.Sent,
request,
});
return;
}
if (event.type === Angular.HttpEventType.ResponseHeader) {
// Emit 'ResponseHeader' event
const headersResponse = _this.createResponse({
url: request.url,
headers: requestInit.headers,
status: event.status,
statusText: event.statusText,
hasBody: event.headers.has('Content-Type') ||
event.headers.has('Content-Length'),
});
subscriber.next({
request,
type: HttpEventType.ResponseHeader,
response: headersResponse,
});
return;
}
if (event.type === Angular.HttpEventType.DownloadProgress) {
// Emit 'DownloadProgress' event
subscriber.next({
request,
type: HttpEventType.DownloadProgress,
loaded: event.loaded,
total: event.total,
});
}
if (event.type === Angular.HttpEventType.UploadProgress) {
// Emit 'UploadProgress' event
subscriber.next({
request,
type: HttpEventType.UploadProgress,
loaded: event.loaded,
total: event.total,
});
}
if (event.type === Angular.HttpEventType.Response) {
const headers = new Headers();
event.headers
.keys()
.forEach(k => headers.set(k, event.headers.get(k) || ''));
const response = _this.createResponse({
url: request.url,
headers,
status: event.status,
statusText: event.statusText,
hasBody: !!event.body,
body: event.body,
});
// Emit 'Response' event
subscriber.next({
type: HttpEventType.Response,
request,
response,
});
}
},
error(error) {
subscriber.error(error);
},
complete() {
subscriber.complete();
},
});
});
}
/**
* Sends the actual HTTP request.
*
* @param request The Angular HttpRequest instance.
* @protected
*/
send(request) {
return this.httpClient.request(request);
}
/**
* Prepares the request by applying defaults and handling body content.
*
* @param init The initial request parameters.
* @returns The prepared request initialization parameters.
* @protected
*/
prepareRequest(init) {
const headers = init.headers || new Headers();
const requestInit = {
...init,
headers,
};
this.defaults.headers.forEach((val, key) => {
if (!headers.has(key))
headers.set(key, val);
});
const url = new URL(requestInit.url, this.serviceUrl);
if (this.defaults.params.size) {
this.defaults.params.forEach((val, key) => {
if (!url.searchParams.has(key))
url.searchParams.set(key, val);
});
requestInit.url = url.toString();
}
if (requestInit.body) {
let body;
let contentType;
if (typeof requestInit.body === 'string' ||
typeof requestInit.body === 'number' ||
typeof requestInit.body === 'boolean') {
contentType = 'text/plain; charset=UTF-8"';
body = String(requestInit.body);
headers.delete('Content-Size');
}
else if (isReadableStreamLike(requestInit.body)) {
contentType = 'application/octet-stream';
body = requestInit.body;
}
else if (Buffer.isBuffer(requestInit.body)) {
contentType = 'application/octet-stream';
body = requestInit.body;
headers.set('Content-Size', String(requestInit.body.length));
}
else if (isBlob(requestInit.body)) {
contentType = requestInit.body.type || 'application/octet-stream';
body = requestInit.body;
headers.set('Content-Size', String(requestInit.body.size));
}
else {
contentType = 'application/json';
body = JSON.stringify(requestInit.body);
headers.delete('Content-Size');
}
if (!headers.has('Content-Type') && contentType)
headers.set('Content-Type', contentType);
requestInit.body = body;
}
return requestInit;
}
/**
* Creates a {@link HttpResponse} instance.
*
* @param init The response initiator parameters.
* @returns A new HttpResponse instance.
* @protected
*/
createResponse(init) {
return new HttpResponse(init);
}
/**
* Parses the response body based on Content-Type.
*
* @param fetchResponse The response to parse.
* @returns The parsed body.
* @protected
*/
async parseBody(fetchResponse) {
let body;
const contentType = fetchResponse.headers.get('Content-Type') || '';
if (typeIs.is(contentType, ['json', 'application/*+json'])) {
body = await fetchResponse.json();
if (typeof body === 'string')
body = JSON.parse(body);
}
else if (typeIs.is(contentType, ['text']))
body = await fetchResponse.text();
else if (typeIs.is(contentType, ['multipart']))
body = await fetchResponse.formData();
else {
const buf = await fetchResponse.arrayBuffer();
if (buf.byteLength)
body = buf;
}
return body;
}
}
// @ts-ignore
/**
* Angular specific implementation of {@link HttpClientBase}.
*
* @class OpraAngularClient
*/
class OpraAngularClient extends HttpClientBase {
/**
* Creates a new instance of OpraAngularClient.
*
* @param httpClient The Angular HttpClient instance.
* @param serviceUrl The base URL of the service.
* @param options Configuration options.
*/
constructor(httpClient, serviceUrl, options) {
super(new AngularBackend(httpClient, serviceUrl, options));
}
/**
* Gets the default request options from the backend.
*/
get defaults() {
return this[kBackend].defaults;
}
}
const OPRA_CLIENT_MODULE_OPTIONS = 'OPRA_CLIENT_MODULE_OPTIONS';
/**
* Angular module for OPRA client.
*/
class OpraClientModule {
/**
* Registers a client provider.
*
* @param options Configuration options for the client.
* @returns A ModuleWithProviders instance.
*/
static registerClient(options) {
const CLIENT_TOKEN = options.token || OpraAngularClient;
return {
ngModule: OpraClientModule,
providers: [
{
provide: CLIENT_TOKEN,
deps: [HttpClient],
useFactory: (httpClient) => new OpraAngularClient(httpClient, options.serviceUrl, options),
},
],
};
}
/**
* Registers a service provider.
*
* @param serviceClass The service class to register.
* @param options Configuration options for the service.
* @returns A ModuleWithProviders instance.
*/
static registerService(serviceClass, options) {
const SERVICE_TOKEN = options.token || serviceClass;
return {
ngModule: OpraClientModule,
providers: [
{
provide: SERVICE_TOKEN,
deps: [HttpClient],
useFactory: (httpClient) => {
const opraAngularClient = new OpraAngularClient(httpClient, options.serviceUrl, options);
const service = new serviceClass(opraAngularClient);
service[kClient] = opraAngularClient;
return service;
},
},
],
};
}
/**
* Registers a client provider asynchronously.
*
* @param options Asynchronous configuration options for the client.
* @returns A ModuleWithProviders instance.
*/
static registerClientAsync(options) {
const CLIENT_TOKEN = options.token || OpraAngularClient;
const asyncProviders = this._createAsyncProviders(options);
return {
ngModule: OpraClientModule,
providers: [
...asyncProviders,
{
provide: CLIENT_TOKEN,
deps: [HttpClient, OPRA_CLIENT_MODULE_OPTIONS],
useFactory: (httpClient, opts) => new OpraAngularClient(httpClient, opts.serviceUrl, opts),
},
],
};
}
/**
* Registers a service provider asynchronously.
*
* @param serviceClass The service class to register.
* @param options Asynchronous configuration options for the service.
* @returns A ModuleWithProviders instance.
*/
static registerServiceAsync(serviceClass, options) {
const SERVICE_TOKEN = options.token || serviceClass;
const asyncProviders = this._createAsyncProviders(options);
return {
ngModule: OpraClientModule,
providers: [
...asyncProviders,
{
provide: SERVICE_TOKEN,
deps: [HttpClient, OPRA_CLIENT_MODULE_OPTIONS],
useFactory: (httpClient, opts) => {
const opraAngularClient = new OpraAngularClient(httpClient, opts.serviceUrl, opts);
const service = new serviceClass(opraAngularClient);
service[kClient] = opraAngularClient;
return service;
},
},
],
};
}
static _createAsyncProviders(options) {
if (options.useExisting || options.useFactory)
return [this._createAsyncOptionsProvider(options)];
if (options.useClass) {
return [
this._createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
}
static _createAsyncOptionsProvider(options) {
if (options.useFactory) {
return {
provide: OPRA_CLIENT_MODULE_OPTIONS,
useFactory: options.useFactory,
deps: options.deps || [],
};
}
const useClass = options.useClass || options.useExisting;
if (useClass) {
return {
provide: OPRA_CLIENT_MODULE_OPTIONS,
useFactory: o => o,
deps: [useClass],
};
}
throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OpraClientModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.7", ngImport: i0, type: OpraClientModule });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OpraClientModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OpraClientModule, decorators: [{
type: NgModule,
args: [{}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { AngularBackend, OPRA_CLIENT_MODULE_OPTIONS, OpraClientModule };
//# sourceMappingURL=opra-angular.mjs.map