Skip to content

Commit

Permalink
feat(config): allow extends env by key(identifier)
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonornela committed Dec 3, 2016
1 parent 1c52406 commit 840f427
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 68 deletions.
168 changes: 106 additions & 62 deletions src/providers/config-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,32 @@ export abstract class ConfigBase implements Config {

protected static envs: Array<string> = [];

protected envExtend: {[key: string]: string} = {};
protected envsExtend: {[key: string]: string} = {};

protected keysEnvExtend: {[key: string]: {[key: string]: string}} = {};

constructor(private env?: string, ...args: Array<any>) {
if (env) {
ConfigBase.envs.push(env);
}

this.data = this.getData.apply(this, args);
this.validate();
this.extractExtends();
}

protected abstract getData(...args: Array<any>): Object;

validate() {}
extractExtends() {
for (let key in this.data) {
for (let keyEnv in this.data[key]) {
if (keyEnv.indexOf(':') !== -1) {
let env = keyEnv.substr(0, keyEnv.indexOf(':'));
let envExtend = keyEnv.substr(keyEnv.indexOf(':') + 1);
this.setKeyEnvExtend(key, env, envExtend);
}
}
}
}

setEnv(env: string): this {
ConfigBase.envs.push(env);
Expand All @@ -36,13 +48,32 @@ export abstract class ConfigBase implements Config {
return this.env;
}

setEnvExtends(env: string, envExtend: string): this {
this.envExtend[env] = envExtend;
setEnvExtend(env: string, envExtend: string): this {
this.envsExtend[env] = envExtend;
return this;
}

getEnvExtends(env: string): string | void {
return this.envExtend[env] ? this.envExtend[env] : null;
getEnvExtend(env: string): string | void {
return this.envsExtend[env] ? this.envsExtend[env] : null;
}

setKeyEnvExtend(key: string, env: string, envExtend: string): this {
this.keysEnvExtend[key] = this.keysEnvExtend[key] || {};
this.keysEnvExtend[key][env] = envExtend;

return this;
}

getKeyEnvExtend(key: string, env: string): string | void {
if (this.keysEnvExtend[key] === undefined) {
return null;
}

if (this.keysEnvExtend[key][env] === undefined) {
return null;
}

return this.keysEnvExtend[key][env];
}

set(key: string, value: any, env?: boolean | string): this {
Expand All @@ -51,40 +82,15 @@ export abstract class ConfigBase implements Config {
if (env === false) {
_env = '';
} else if (typeof env === 'string') {
// validate extends
// validate extends prod:dev
_env = env;
if (env.indexOf(':') !== -1) {
// validate if value is object
if (typeof value !== 'object' || value === null) {
throw new Error('To extends value should be object');
}
let envExtend = env.substr(env.indexOf(':') + 1);
let data = this.get(key, envExtend);
if (typeof data !== 'object' || data === null) {
throw new Error(`The env '${envExtend}' should be object`);
}
this.checkExtends(env, key, value);
}
_env = env;
}

if (_env) {
// validate values already assign
if (typeof this.data[key] !== 'object') {
if (this.data[key] !== undefined) {
throw new Error('Not allow assign to value initialized how scalar');
}
} else if (this.data[key] !== null) {
let envs = ConfigBase.envs.filter((value, index, array) => array.indexOf(value) === index);
let isThrow = true;
for (let i = 0, length = envs.length; i < length; i++) {
if (envs[i] in this.data[key]) {
isThrow = false;
}
}
if (isThrow) {
throw new Error('Not allow assign to value initialized how object');
}
}

this.validateDataAlreadyAssign(key);
this.data[key] = this.data[key] || {};
this.data[key][_env] = value;
ConfigBase.envs.push(_env);
Expand All @@ -95,6 +101,42 @@ export abstract class ConfigBase implements Config {
return this;
}

protected validateDataAlreadyAssign(key: string) {
// validate values already assign
if (typeof this.data[key] !== 'object') {
if (this.data[key] !== undefined) {
throw new Error('Not allow assign to value initialized how scalar');
}
} else if (this.data[key] !== null) {
let envs = ConfigBase.envs.filter((value, index, array) => array.indexOf(value) === index);
let isThrow = true;
for (let i = 0, length = envs.length; i < length; i++) {
if (envs[i] in this.data[key]) {
isThrow = false;
}
}
if (isThrow) {
throw new Error('Not allow assign to value initialized how object');
}
}
}

protected checkExtends(env: string, key: string, value: any) {
// validate if value is object
if (typeof value !== 'object' || value === null) {
throw new Error('To extends value should be object');
}
let envExtend = env.substr(env.indexOf(':') + 1);
let data = this.get(key, envExtend);
if (typeof data !== 'object' || data === null) {
throw new Error(`The env '${envExtend}' should be object`);
}
env = env.substr(0, env.indexOf(':'));

// assign key extends ex my-key extends dev:dev1
this.setKeyEnvExtend(key, env, envExtend);
}

get(key: string, env?: boolean | string): any {
let result: any = this.data[key] !== undefined ? this.data[key] : null;

Expand All @@ -109,60 +151,62 @@ export abstract class ConfigBase implements Config {

// env assign in arg
if (typeof env === 'string') {
return this.getResult(result, env);
return this.getResult(result, key, env);
}

// catch env global
if (result[this.env] !== undefined) {
return this.getResult(result, this.env);
return this.getResult(result, key, this.env);
}

return result;
}

protected getResult(result: any, env: string) {
protected getResult(result: any, key: string, env: string) {

let keyExtends = [env, ':'].join('');
let envExtend: string | void;

Object.keys(result).forEach((keyEnv: any) => {
if (keyEnv.indexOf(keyExtends) === 0) {
env = keyEnv;
envExtend = keyEnv.split(':')[1];
}
});
let envExtend = this.getKeyEnvExtend(key, env);

if (!envExtend) {
envExtend = this.getEnvExtends(env);
}
envExtend = this.getEnvExtend(env);
}

// not extends
if (!envExtend) {
return result[env] !== undefined ? result[env] : null;
}

let lastResult = result[env];
if (!lastResult) {
lastResult = result[[ env, ':', envExtend ].join('')];
}

let argumentsAssign = [
{}
];
this.orderEnvExtends(result, envExtend, argumentsAssign);
argumentsAssign.push(result[env]);

this.orderEnvExtends(result, key, envExtend, argumentsAssign);
argumentsAssign.push(lastResult);

return Object.assign.apply(null, argumentsAssign);
}

protected orderEnvExtends(result: any, envExtend: string, argumentsApply: Array<any>) {
if (result[envExtend]) {
argumentsApply.unshift(result[envExtend]);
protected orderEnvExtends(result: any, key: string, env: string, argumentsApply: Array<any>) {
if (result[env]) {
argumentsApply.unshift(result[env]);
return;
}

let keyExtend = [envExtend, ':'].join('');
Object.keys(result).forEach((env: any) => {
if (env.indexOf(envExtend) === 0) {
argumentsApply.unshift(result[env]);
this.orderEnvExtends(result, env.substr(keyExtend.length), argumentsApply);
}
});
let envExtend = this.getKeyEnvExtend(key, env);

if (!envExtend) {
envExtend = this.getEnvExtend(env);
}

if (envExtend) {
let envConcat = [ env, ':', envExtend].join('');
argumentsApply.unshift(result[envConcat]);
this.orderEnvExtends(result, key, envExtend, argumentsApply);
}
}

getAll(): any {
Expand Down
6 changes: 4 additions & 2 deletions src/providers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { Injectable } from '@angular/core';
@Injectable()
export abstract class Config {
abstract setEnv(env: string): this;
abstract setEnvExtends(env: string, envExtend: string): this;
abstract getEnvExtends(env: string): string | void;
abstract setEnvExtend(env: string, envExtend: string): this;
abstract getEnvExtend(env: string): string | void;
abstract setKeyEnvExtend(key: string, env: string, envExtend: string): this;
abstract getKeyEnvExtend(key: string, env: string): string | void;
abstract getEnv(): string;
abstract set(key: string, value: any, env?: boolean | string): this;
abstract get(key: string, env?: boolean | string): any;
Expand Down
7 changes: 3 additions & 4 deletions test/config-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function configModule(data?: Object, loader?: Function) {
};

TestBed.configureTestingModule({
imports: [ConfigurationModule.initialize(data, 'dev', loader)]
imports: [ ConfigurationModule.initialize(data, 'dev', loader) ]
});
}

Expand Down Expand Up @@ -84,7 +84,7 @@ describe('Module Config', () => {
config.setEnv('dev').set('config-fluent', 'Config');
}));

it('Set env env', inject([Config], (config: Config) => {
it('Set env', inject([Config], (config: Config) => {
config.set('my-config-setenv', 'valueDev');
config.set('my-config-setenv', 'valueProd', 'prod');
expect(config.get('my-config-setenv')).toBe('valueDev');
Expand Down Expand Up @@ -128,15 +128,14 @@ describe('Module Config', () => {
expect(configExtendStaging.age).toEqual(30);
}));


describe('Env extends global', () => {

beforeEach(() => {
configModule();
});

it('all prod extend dev', inject([Config], (config: any) => {
config.setEnvExtends('prod2', 'dev')
config.setEnvExtend('prod2', 'dev')
.set('my-config-extends', {name: 'Ramon'}, 'prod2');

let configExtendData = config.get('my-config-extends', 'prod2');
Expand Down

0 comments on commit 840f427

Please sign in to comment.