import { isString } from 'util'; import { isNonArrayObject, merge } from '../lib'; import { ConfigNodeBase } from './ConfigNodeBase'; import { ConfigNodeArgs, ConfigNodePropertyFactory, ConfigPropertyNodeFactoryArgs, ProfileConfigNodeOptions, } from './types'; export const defaultOptions: ProfileConfigNodeOptions = { extendsPropertyName: '__extends', profilePropertyName: 'profile', profilesPropertyName: 'profiles', }; /** * Extends Config Node to support "Profiles". Supports a keyed collection of "Profiles", each defining configuration data, and a Node propety to * indicate the current "Profile". The value of the "Profile" property is used as key in the "Profiles" collection which provides base values * for the root of this Node. */ export class ProfilesConfigNodeBase< TValue = {}, TOptions extends ProfileConfigNodeOptions = ProfileConfigNodeOptions, TArgs extends ConfigNodeArgs = ConfigNodeArgs > extends ConfigNodeBase { protected get isProfile(): boolean { return ( this.parentNode && this.parentNode.propertyName && this.parentNode.propertyName === this.options.profilesPropertyName ); } public get profiles(): { [name: string]: ConfigNodeBase } { // get value to access merged profiles keys const val = super.getValue(); // make sure val has profiles map if (val && isNonArrayObject(val) && isNonArrayObject(val[this.options.profilesPropertyName])) { // get profiles names const keys = Object.keys(val[this.options.profilesPropertyName]); // loop over profile keys and get property node for each one, and return return keys.reduce((acc, curr) => { acc[curr] = super.getPropertyNode([this.options.profilesPropertyName, curr]); return acc; }, {}); } return {}; } protected constructor(args: ConfigPropertyNodeFactoryArgs | ConfigNodeArgs) { super(args); this.options = merge([this.options, defaultOptions, args.options]); } public async setProfile(profile: string) { await this.applyValue({ profile }); } public getProfileValue(profile: string) { const node = this.getProfileNode(profile); return node ? node.value : undefined; } public getProfileNode(profile: string): ConfigNodeBase | undefined { return this.getPropertyNode([this.options.profilesPropertyName, profile]); } public getValueForProfile(profile: string, omitProperties: string[] = []): TValue { return this._getValueForProfile(profile, undefined, omitProperties); } public getValue(excludeInherited = false, omitProperties: string[] = []): TValue { // if this node is a profile, return the node's direct value without inheriting other profiles if (this.isProfile) { return this.buildProfileValue(omitProperties); } else { return this._getValueForProfile(undefined, excludeInherited, omitProperties); } } protected buildProfileValue = (omitProperties: string[] = []) => { let value = super.getValue(true, omitProperties); if (this.isProfile && this.options.extendsPropertyName) { const extendsPropNode = this.getPropertyNode(this.options.extendsPropertyName, false); if (extendsPropNode) { const baseProfileName = extendsPropNode.value; if (isString(baseProfileName)) { const baseProfileNode = (this.parentNode.parentNode as ProfilesConfigNodeBase).getProfileNode( baseProfileName ); if (baseProfileNode) { value = this.merge([baseProfileNode.value, value]); } } delete value[this.options.extendsPropertyName]; } } return value; }; protected _getValueForProfile(profile?: string, excludeInherited = false, omitProperties: string[] = []): TValue { try { // call base class to build value let value = super.getValue(excludeInherited, omitProperties); if (value) { profile = profile || value[this.options.profilePropertyName]; if (profile) { const profileValue = this.getProfileValue(profile); value = merge([{}, profileValue, value]); } // delete the profiles property from resulting value delete value[this.options.profilesPropertyName]; } return value; } catch (e) { // tslint:disable-next-line: no-debugger debugger; } } protected get defaultPropertyNodeFactory(): ConfigNodePropertyFactory { // return this.createPropertyNode; return async (args: TArgs & ConfigPropertyNodeFactoryArgs): Promise => { const node = new ProfilesConfigNodeBase(args); await node.init(); return node; }; } }