Skip to content

Commit c0261f4

Browse files
author
Ovidiu Barabula
committed
feat(core): add config prefixer
Adds a custom prefix to config object keys
1 parent d218096 commit c0261f4

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed

src/config-manager/prefixer.spec.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { assert, expect } from 'chai';
2+
import 'mocha';
3+
import Prefixer, { ConfigPrefixer, ERRORS } from './prefixer';
4+
5+
6+
describe('ConfigPrefixer', () => {
7+
let prefixer: ConfigPrefixer;
8+
const prefix = 'prefix:';
9+
const unprefixedConfig = {
10+
anOption: 'aValue',
11+
anotherOption: 'anotherValue',
12+
};
13+
const prefixedConfig = {
14+
[`${prefix}anOption`]: 'aValue',
15+
[`${prefix}anotherOption`]: 'anotherValue',
16+
};
17+
18+
before(() => prefixer = Prefixer(prefix));
19+
20+
it('instantiates', () => {
21+
expect(Prefixer(prefix)).to.be.an('object')
22+
.to.contain.keys('apply', 'remove');
23+
});
24+
25+
26+
it('adds prefix to object keys', () => {
27+
expect(prefixer.apply(unprefixedConfig)).to.deep.equal(prefixedConfig);
28+
});
29+
30+
31+
it('removes prefix from object keys', () => {
32+
expect(prefixer.remove(prefixedConfig)).to.deep.equal(unprefixedConfig);
33+
});
34+
35+
36+
it('doesn\'t prefix already prefixed keys', () => {
37+
expect(prefixer.apply(prefixedConfig)).to.deep.equal(prefixedConfig);
38+
});
39+
40+
41+
it('returns same object if it\'s not prefixed', () => {
42+
expect(prefixer.remove(unprefixedConfig)).to.deep.equal(unprefixedConfig);
43+
});
44+
45+
46+
it('adds prefix only to non-prefixed keys', () => {
47+
const config = {
48+
anOption: 'aValue',
49+
[`${prefix}anotherOption`]: 'anotherValue',
50+
};
51+
expect(prefixer.apply(config)).to.deep.equal({
52+
[`${prefix}anOption`]: 'aValue',
53+
[`${prefix}anotherOption`]: 'anotherValue',
54+
});
55+
});
56+
57+
58+
it('removes prefix only from prefixed keys', () => {
59+
const config = {
60+
anOption: 'aValue',
61+
[`${prefix}anotherOption`]: 'anotherValue',
62+
};
63+
expect(prefixer.remove(config)).to.deep.equal({
64+
anOption: 'aValue',
65+
anotherOption: 'anotherValue',
66+
});
67+
});
68+
69+
70+
it('throws if <prefix> parameter is not passed', () => {
71+
assert.throws(() => Prefixer(), ERRORS.PREFIX_REQUIRED);
72+
});
73+
74+
75+
it('throws if apply() method is called without parameter', () => {
76+
assert.throws(() => prefixer.apply(), ERRORS.CONFIG_REQUIRED);
77+
});
78+
79+
80+
it('throws if remove() method is called without parameter', () => {
81+
assert.throws(() => prefixer.remove(), ERRORS.CONFIG_REQUIRED);
82+
});
83+
84+
85+
it('throws if performOperation() method receives unknown operation', () => {
86+
assert.throws(() => prefixer.performOperation(100, unprefixedConfig), ERRORS.UNKNOWN_OPERATION);
87+
});
88+
});

src/config-manager/prefixer.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Name: prefixer.ts
3+
* Description: Configuration object key prefixer
4+
* Author: Ovidiu Barabula <lectii2008@gmail.com>
5+
* @since 0.1.0
6+
*/
7+
8+
import { Config } from '../config-manager';
9+
import { required } from '../util/utility-functions';
10+
11+
12+
export enum PrefixOperation {
13+
Remove,
14+
Add,
15+
}
16+
17+
export interface ConfigPrefixer {
18+
apply(config: Config): Config;
19+
remove(config: Config): Config;
20+
performOperation?(type: PrefixOperation, config: Config): Config;
21+
}
22+
23+
24+
// Custom error messages
25+
export const ERRORS = {
26+
CONFIG_REQUIRED: 'ConfigPrefixer> apply() and remove() methods require first parameter <config> of type \'object\'',
27+
PREFIX_REQUIRED: 'ConfigPrefixer() requires first parameter <prefix> of type \'string\'',
28+
UNKNOWN_OPERATION: 'ConfigPrefixer> An unknonw operation was passed to performOperation() method',
29+
};
30+
31+
32+
/**
33+
* Prefix config keys with custom string
34+
* @param prefix Prefix string to be added to each key
35+
*/
36+
function ConfigPrefixer(
37+
prefix: string = required(ERRORS.PREFIX_REQUIRED),
38+
): ConfigPrefixer {
39+
/**
40+
* Check if key already has prefix applied
41+
* @param key Key string
42+
*/
43+
function hasPrefix(key: string): boolean {
44+
return key.includes(prefix) && key.indexOf(prefix) === 0;
45+
}
46+
47+
48+
/**
49+
* Add prefix to key
50+
* @param key Object key string
51+
*/
52+
function addPrefix(key: string): string {
53+
return !hasPrefix(key) ? `${prefix}${key}` : key;
54+
}
55+
56+
57+
/**
58+
* Remove prefix from key
59+
* @param key Object key string
60+
*/
61+
function removePrefix(key: string): string {
62+
return hasPrefix(key) ? key.substring(prefix.length) : key;
63+
}
64+
65+
66+
/**
67+
* Perform an operation on passed in configuration object
68+
* @param type Type of the operation
69+
* @param config Configuration object
70+
*/
71+
function performOperation(type: PrefixOperation, config: Config): Config {
72+
let operation: (key: string) => string;
73+
74+
if (type === PrefixOperation.Add) {
75+
operation = (key: string) => addPrefix(key);
76+
} else if (type === PrefixOperation.Remove) {
77+
operation = (key: string) => removePrefix(key);
78+
} else {
79+
throw new Error(ERRORS.UNKNOWN_OPERATION);
80+
}
81+
82+
return Object.keys(config)
83+
.reduce((newConfig, key) => ({
84+
...newConfig,
85+
...{ [operation(key)]: config[key] },
86+
}), {});
87+
}
88+
89+
90+
/**
91+
* Apply prefix to object keys
92+
* @param config Configuration object
93+
*/
94+
function apply(config: Config = required(ERRORS.CONFIG_REQUIRED)): Config {
95+
return performOperation(PrefixOperation.Add, config);
96+
}
97+
98+
99+
/**
100+
* Remove prefix from object keys
101+
* @param config Prefixed configuration object
102+
*/
103+
function remove(config: Config = required(ERRORS.CONFIG_REQUIRED)): Config {
104+
return performOperation(PrefixOperation.Remove, config);
105+
}
106+
107+
108+
// Creating the public API object
109+
let publicApi: ConfigPrefixer = {
110+
apply,
111+
remove,
112+
};
113+
114+
// Adding private methods to public API in test environment
115+
/* test:start */
116+
publicApi = {...publicApi,
117+
performOperation,
118+
};
119+
/* test:end */
120+
121+
// Returning config wizard public API
122+
return Object.freeze(publicApi);
123+
}
124+
125+
export default ConfigPrefixer;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy