UNPKG

@dagonmetric/ng-config

Version:

Configuration and options service for Angular applications.

490 lines (482 loc) 15.6 kB
import { InjectionToken, EventEmitter, ɵɵdefineInjectable, ɵɵinject, INJECTOR, Injectable, Inject, Injector, Optional, APP_INITIALIZER, NgModule } from '@angular/core'; import { Observable, of, forkJoin } from 'rxjs'; import { tap, map, mapTo, share, take } from 'rxjs/operators'; /** * @fileoverview added by tsickle * Generated from: src/config-options.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const CONFIG_OPTIONS = new InjectionToken('ConfigOptions'); /** * @fileoverview added by tsickle * Generated from: src/config-provider.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const CONFIG_PROVIDER = new InjectionToken('ConfigProvider'); /** * @fileoverview added by tsickle * Generated from: src/logger.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const NG_CONFIG_LOGGER = new InjectionToken('NG-CONFIG Logger'); /** * @fileoverview added by tsickle * Generated from: src/util.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} options * @param {?} configSection * @return {?} */ function mapOptionValues(options, configSection) { /** @type {?} */ const keys = Object.keys(options); for (const key of keys) { if (!Object.prototype.hasOwnProperty.call(configSection, key)) { continue; } // const popDescriptor = Object.getOwnPropertyDescriptor(options, key); // if (!popDescriptor?.writable) { // continue; // } /** @type {?} */ const optionsValue = options[key]; /** @type {?} */ const configValue = configSection[key]; if (configValue == null) { options[key] = null; continue; } if (optionsValue == null) { options[key] = configValue; continue; } if (optionsValue === configValue) { continue; } if (typeof optionsValue === 'string') { if (typeof configValue === 'string') { options[key] = configValue; } else { options[key] = JSON.stringify(configValue); } } else if (typeof optionsValue === 'boolean') { if (typeof configValue === 'boolean') { options[key] = configValue; } else if (typeof configValue === 'string') { options[key] = ['true', '1', 'on', 'yes'].indexOf(configValue.toLowerCase()) > -1; } else if (typeof configValue === 'number') { options[key] = configValue === 1; } else { options[key] = false; } } else if (typeof optionsValue === 'number') { options[key] = Number(configValue) || 0; } else if (Array.isArray(optionsValue)) { if (Array.isArray(configValue)) { if (configValue.length > 0 && configValue.filter((/** * @param {?} v * @return {?} */ (v) => typeof v == 'string')).length === configValue.length) { options[key] = [...configValue]; } else { options[key] = []; } } else if (typeof configValue === 'string') { options[key] = configValue .split(';') .map((/** * @param {?} s * @return {?} */ (s) => s.trim())) .filter((/** * @param {?} s * @return {?} */ (s) => s.length > 0)); } } else if (typeof optionsValue === 'object' && Object.prototype.toString.call(optionsValue) !== '[object Date]') { if (!Array.isArray(configValue) && typeof configValue === 'object') { mapOptionValues(optionsValue, configValue); } } } } /** * @param {?} a * @param {?} b * @return {?} */ function equalDeep(a, b) { if (a === null && b === null) { return true; } if (Array.isArray(a)) { if (!b || !Array.isArray(b)) { return false; } if (a.length !== b.length) { return false; } for (let i = a.length - 1; i >= 0; i--) { if (!equalDeep(a[i], b[i])) { return false; } } return true; } if (Array.isArray(b)) { return false; } if (a && b && typeof a == 'object' && typeof b == 'object') { /** @type {?} */ const keys = Object.keys(a); if (keys.length !== Object.keys(b).length) { return false; } for (const key of keys) { if (!equalDeep(a[key], b[key])) { return false; } } return true; } return a === b; } /** * @fileoverview added by tsickle * Generated from: src/config.service.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * The core service for loading and getting configuration value the from configuration providers. */ class ConfigService { /** * @param {?} configProviders * @param {?} injector * @param {?=} options * @param {?=} logger */ constructor(configProviders, injector, options, logger) { this.injector = injector; this.loading = false; this.activated = false; this.currentLoad = new Observable(); this.loadedConfig = {}; this.optionsRecord = new Map(); this.sortedConfigProviders = configProviders.reverse(); this.options = options || {}; this.valueChanges = new EventEmitter(); if (logger) { this.logger = logger; } else { this.logger = { debug: (/** * @param {?} message * @param {?} data * @return {?} */ (message, data) => { if (data) { // eslint-disable-next-line no-console console.log(`${message}, data: `, data); } else { // eslint-disable-next-line no-console console.log(message); } }) }; } this.currentLoad = this.initLoad(); this.subscribeCurrentLoad(false); } /** * @return {?} */ get providers() { return this.sortedConfigProviders; } /** * Call this method to ensure configurations are fetched and activated. * @return {?} */ ensureInitialized() { if (this.activated) { return of(this.activated); } return this.currentLoad.pipe(tap((/** * @param {?} config * @return {?} */ (config) => { this.activateConfig(config, false); })), map((/** * @return {?} */ () => this.activated))); } /** * Call this method to reload fresh configuration values from config providers. * @return {?} */ reload() { this.currentLoad = this.initLoad(); this.subscribeCurrentLoad(true); return this.currentLoad.pipe(mapTo(void 0)); } /** * Use this method to get loaded configuration value with a given string key. * @param {?} key The config key string. * @return {?} */ getValue(key) { return this.getConfigValue(key, this.loadedConfig); } /** * Use this method to map loaded configuration values to the instance of options class type. * @template T * @param {?} key The config key string. * @param {?} optionsClass The options class type to be mapped. * @return {?} */ mapType(key, optionsClass) { /** @type {?} */ const optionsObj = this.injector.get(optionsClass, new optionsClass()); this.mapObject(key, optionsObj); return optionsObj; } /** * Use this method to map loaded configuration values to the options object. * @template T * @param {?} key The config key string. * @param {?} optionsObj The options object to be mapped with configuration values. * @return {?} */ mapObject(key, optionsObj) { /** @type {?} */ const cachedOptions = (/** @type {?} */ (this.optionsRecord.get(key))); if (cachedOptions != null) { if (cachedOptions === optionsObj) { return cachedOptions; } this.optionsRecord.delete(key); } /** @type {?} */ const configValue = this.getValue(key); if (configValue == null || Array.isArray(configValue) || typeof configValue !== 'object') { return optionsObj; } mapOptionValues((/** @type {?} */ (optionsObj)), configValue); this.optionsRecord.set(key, optionsObj); return optionsObj; } /** * @private * @return {?} */ initLoad() { if (this.currentLoadSubscription) { this.currentLoadSubscription.unsubscribe(); this.currentLoadSubscription = null; } if (!this.loading) { this.log('Cconfiguration loading started.'); this.loading = true; } return forkJoin(this.providers.map((/** * @param {?} configProvider * @return {?} */ (configProvider) => { /** @type {?} */ const providerName = configProvider.name; /** @type {?} */ const loadObs = configProvider.load().pipe(tap((/** * @param {?} config * @return {?} */ (config) => { this.log(providerName, config); })), share()); return loadObs.pipe(take(1), share()); }))).pipe(map((/** * @param {?} configs * @return {?} */ (configs) => { /** @type {?} */ let mergedConfig = {}; configs.forEach((/** * @param {?} config * @return {?} */ (config) => { mergedConfig = Object.assign(Object.assign({}, mergedConfig), config); })); return mergedConfig; }))); } /** * @private * @param {?} reActivate * @return {?} */ subscribeCurrentLoad(reActivate) { this.currentLoadSubscription = this.currentLoad.subscribe((/** * @param {?} config * @return {?} */ (config) => { this.activateConfig(config, reActivate); }), (/** * @return {?} */ () => { this.loading = false; })); } /** * @private * @param {?} config * @param {?} reActivate * @return {?} */ activateConfig(config, reActivate) { this.loading = false; if (this.activated && !reActivate) { return; } if (!equalDeep(config, this.loadedConfig)) { this.optionsRecord.clear(); this.loadedConfig = config; this.activated = true; this.log('Configuration loading completed.'); ((/** @type {?} */ (this.valueChanges))).emit(config); } else { this.activated = true; this.log('Configuration loading completed.'); } } /** * @private * @param {?} key * @param {?} config * @return {?} */ getConfigValue(key, config) { /** @type {?} */ const keyArray = key.split(/:/); /** @type {?} */ const result = keyArray.reduce((/** * @param {?} acc * @param {?} current * @return {?} */ (acc, current) => acc && acc[current]), config); if (result === undefined) { return null; } return result; } /** * @private * @param {?} msg * @param {?=} data * @return {?} */ log(msg, data) { if (!this.options.debug) { return; } this.logger.debug(`[ConfigService] ${msg}`, data); } } /** @nocollapse */ ConfigService.ɵprov = ɵɵdefineInjectable({ factory: function ConfigService_Factory() { return new ConfigService(ɵɵinject(CONFIG_PROVIDER), ɵɵinject(INJECTOR), ɵɵinject(CONFIG_OPTIONS, 8), ɵɵinject(NG_CONFIG_LOGGER, 8)); }, token: ConfigService, providedIn: "root" }); ConfigService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ ConfigService.ctorParameters = () => [ { type: Array, decorators: [{ type: Inject, args: [CONFIG_PROVIDER,] }] }, { type: Injector }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [CONFIG_OPTIONS,] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [NG_CONFIG_LOGGER,] }] } ]; /** * @fileoverview added by tsickle * Generated from: src/config.module.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} configService * @return {?} */ function configAppInitializerFactory(configService) { /** @type {?} */ const res = (/** * @return {?} */ async () => configService.ensureInitialized().toPromise()); return res; } /** * The `NGMODULE` for providing `ConfigService`. Call `configure` method to provide options for `ConfigService`. */ class ConfigModule { /** * Call this method in root module to provide options for `ConfigService`. * @param {?=} loadOnStartUp If `true` configuration values are loaded at app starts. Default is `true`. * @param {?=} options Option object for `ConfigService`. * @return {?} */ static configure(loadOnStartUp = true, options = {}) { return { ngModule: ConfigModule, providers: [ { provide: CONFIG_OPTIONS, useValue: options }, loadOnStartUp ? { provide: APP_INITIALIZER, useFactory: configAppInitializerFactory, deps: [ConfigService], multi: true } : [] ] }; } } ConfigModule.decorators = [ { type: NgModule, args: [{ providers: [ConfigService] },] } ]; export { CONFIG_OPTIONS, CONFIG_PROVIDER, ConfigModule, ConfigService, NG_CONFIG_LOGGER, configAppInitializerFactory }; //# sourceMappingURL=ng-config.js.map