UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

295 lines (286 loc) 12.4 kB
import * as i0 from '@angular/core'; import { Injectable, Optional, SkipSelf, runInInjectionContext, Injector, inject, provideAppInitializer, NgModule } from '@angular/core'; import * as i1 from '@c8y/client'; import { FetchClient, BasicAuth, CookieAuth, Realtime, EventBinaryService, EventService, InventoryService, MeasurementService, AlarmService, OperationBulkService, OperationService, ApplicationService, UserService, TenantService, SystemOptionsService, TenantOptionsService, TenantSecurityOptionsService, TenantLoginOptionsService, AuditService, InventoryRoleService, InventoryBinaryService, DeviceRegistrationService, DeviceRegistrationBulkService, UserRoleService, UserGroupService, IdentityService, TrustedCertificateService, CrlService, SmartGroupsService, SmartRulesService, FeatureService, BearerAuthFromSessionStorage } from '@c8y/client'; import { from, Subject, pipe } from 'rxjs'; import { filter, map, scan, distinctUntilChanged, shareReplay } from 'rxjs/operators'; import { pickAuthStrategy } from '@c8y/bootstrap'; class HttpHandler { } class HttpInterceptHandler extends HttpHandler { constructor(interceptor, nextHandler) { super(); this.interceptor = interceptor; this.nextHandler = nextHandler; } handle(req) { return this.interceptor.intercept(req, this.nextHandler); } } class HttpRequestHandler extends HttpHandler { constructor(fetch, apiService) { super(); this.fetch = fetch; this.apiService = apiService; } handle(req) { const { options, url } = req; const { method } = options; this.apiService?.onStart({ method, options, url }); let fetchPromise = this.fetch(url, options); if (typeof options.responseInterceptor === 'function') { fetchPromise = fetchPromise.then(options.responseInterceptor); } fetchPromise.then((response) => this.apiService?.onFinish({ method, options, url, response }), (response) => this.apiService?.onFinish({ method, options, url, response })); return from(fetchPromise); } } class ApiService { constructor(client) { this.client = client; this.callsSubject = new Subject(); this.interceptors = new Map(); this.interceptorCounter = 0; this.calls = this.callsSubject.asObservable(); this.hookIntoClientFetch(); this.isLoading$ = this.calls.pipe(filter(({ url }) => !/notification\/realtime/.test(url)), map(({ phase }) => (phase === 'start' ? 1 : -1)), scan((count, item) => count + item, 0), map(count => count > 0), distinctUntilChanged(), shareReplay(1)); } /** * Allows to hook into the responses received by the FetchClient. * This is meant to be used to react on the responses, not for manipulation of the responses. * @param hookFilter A filter function to filter for specific responses. * @returns An Observable of the filtered responses. */ hookResponse(hookFilter) { return this.callsSubject.pipe(filter(({ phase }) => phase === 'finish'), filter(hookFilter)); } /** * Allows to hook into the requests performed by the FetchClient. * This is meant to be used to react on the requests, not for manipulation of the requests. * @param hookFilter A filter function to filter for specific requests. * @returns An Observable of the filtered requests. */ hookRequest(hookFilter) { return this.callsSubject.pipe(filter(({ phase }) => phase === 'start'), filter(hookFilter)); } /** * Notifies observers that an API call has finished. * @param call The API call that has finished. */ onFinish(call) { this.callsSubject.next({ phase: 'finish', ...call }); } /** * Notifies observers that an API call has started. * @param call The API call that has started. */ onStart(call) { this.callsSubject.next({ phase: 'start', ...call }); } /** * Resolves data from an API call response. * @returns A Promise containing an object with resolved data, method, and URL. */ resolveData(call) { const { response, method, url } = call; if ('data' in response) { return Promise.resolve({ data: response.data, method, url }); } else { // No Content success status, for example DELETE request. if (response?.status === 204) { return Promise.resolve({ data: null, method, url }); } const cb = data => ({ data, method, url }); return response.clone().json().then(cb, cb); } } /** * Can be added to a pipe to exclude any permission call. Permission calls are PUT * request with only an id in it, to verify if the user has access to this managed object. * @returns The operator to be added to a pipe. */ excludePermissionCall() { return pipe(filter(({ method, options }) => { if (method === 'PUT' && options.body && typeof options.body === 'string') { const parsedBody = JSON.parse(options.body); const bodyKeys = Object.keys(parsedBody); return !(bodyKeys.length === 1 && bodyKeys[0] === 'id'); } return true; })); } /** * Allows to intercept requests performed via the FetchClient requests. * @param interceptor The interceptor to be added. * @param id An optional unique identifier for the interceptor. The chain of interceptors is ordered by this id. And it can be used to remove the interceptor later on. * @returns The id of the interceptor (same as provided id if one was provided, otherwise an id will be generated). */ addInterceptor(interceptor, id) { if (!id) { id = `${++this.interceptorCounter}`; } this.interceptors.set(id, interceptor); return id; } /** * Allows to remove a previously added interceptor by it's id. * @param id The id of the interceptor that should be removed. * @returns true if an interceptor existed and has been removed, or false if id does not exist. */ removeInterceptor(id) { return this.interceptors.delete(id); } /** * Checks if an interceptor with a given id exists. * @param id The id of the interceptor. * @returns - Returns true if an interceptor with the given id exists, otherwise false. */ hasInterceptor(id) { return this.interceptors.has(id); } hookIntoClientFetch() { const fetch = this.client.fetch.bind(this.client); const requestHandler = new HttpRequestHandler(fetch, this); this.client.fetch = async (url, options = { method: 'GET' }) => { const { method } = options; return this.createInterceptorChain({ url, options, method }, requestHandler).toPromise(); }; } createInterceptorChain(call, requestHandler) { let handler = requestHandler; // Do some sorting to always apply the interceptors in the specific order const sortedInterceptorIds = Array.from(this.interceptors.keys()).sort((a, b) => b.localeCompare(a)); for (const interceptorId of sortedInterceptorIds) { handler = new HttpInterceptHandler(this.interceptors.get(interceptorId), handler); } return handler.handle(call); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApiService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApiService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApiService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.FetchClient }] }); const defaultServicesFromClientLib = [ FetchClient, BasicAuth, CookieAuth, Realtime, EventBinaryService, EventService, InventoryService, MeasurementService, AlarmService, OperationBulkService, OperationService, ApplicationService, UserService, TenantService, SystemOptionsService, TenantOptionsService, TenantSecurityOptionsService, TenantLoginOptionsService, AuditService, InventoryRoleService, InventoryBinaryService, DeviceRegistrationService, DeviceRegistrationBulkService, UserRoleService, UserGroupService, IdentityService, TrustedCertificateService, CrlService, SmartGroupsService, SmartRulesService, FeatureService ]; function toProvider(provide) { let deps = [FetchClient, Realtime]; if (provide === FetchClient) { deps = [CookieAuth]; } if (provide === BasicAuth || provide === CookieAuth) { deps = []; } if (provide === Realtime) { deps = [FetchClient]; } const depsWithExistingProvider = [ [provide, new Optional(), new SkipSelf()], // let's see if we can find an existing provider (possibly null) ...deps ]; return { provide, deps: depsWithExistingProvider, useFactory: (...args) => { // if there was an existing provider, it would be first in the list and we can return it if (args.length && args[0] !== null) { return args[0]; } // otherwise we need to drop the first element as it would be null const providers = args.slice(1); // the other providers are the dependencies we need. They match the order of the deps array const existingProviders = deps.map((dep, index) => { return { provide: dep, useValue: providers[index] }; }); // use this to dependency inject the dependencies and instantiate the class return runInInjectionContext(Injector.create({ providers: [...existingProviders, { provide, useClass: provide, deps: deps }] }), () => { return inject(provide); }); } }; } /** * Provides all services from `@c8y/client` library. * @returns An array of providers for all services from `@c8y/client` library. */ function provideClientLibServices() { const providers = defaultServicesFromClientLib .map(service => toProvider(service)) .concat([ { provide: ApiService, useClass: ApiService, deps: [FetchClient] }, // ensure that the auth strategy is set before the app is initialized provideAppInitializer(() => { const client = inject(FetchClient); const auth = pickAuthStrategy(BearerAuthFromSessionStorage, BasicAuth, CookieAuth); client.setAuth(auth); }) ]); return providers; } /** * @deprecated use provideClientLibServices instead */ class DataModule { static providers() { return provideClientLibServices(); } static forRoot() { return { ngModule: DataModule, providers: provideClientLibServices() }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DataModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: DataModule }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DataModule, providers: provideClientLibServices() }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DataModule, decorators: [{ type: NgModule, args: [{ providers: provideClientLibServices() }] }] }); // do not expose as it might confuse people on what to implement // export * from './http-handler.model'; /** * Generated bundle index. Do not edit. */ export { ApiService, DataModule, HttpHandler, defaultServicesFromClientLib, provideClientLibServices }; //# sourceMappingURL=c8y-ngx-components-api.mjs.map