Skip to content

Commit

Permalink
extend: Add extend config key format #25
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjD90 authored Oct 13, 2023
1 parent fcd024a commit 4cdf21c
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 57 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/node.js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ jobs:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- name: Setup yarn
run: npm install -g yarn

- name: Setup Nodejs with yarn caching
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: yarn
Expand All @@ -45,3 +46,4 @@ jobs:

- name: Push coverage
run: yarn coverage
if: ${{ matrix.node-version == '20.x' }}
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,56 @@ or
npm install --save @neo9/n9-node-conf
```

## V2 Upgrade

- Change extendConfig setting from `string` to `object`.
Example :

| Before (V1) | After (V2) |

<table>
<tr>
<th>
Before (V1)
</th>
<th>
After (V2)
</th>
</tr>
<tr>
<td>
<pre>

```ts
{
...,
extendConfig: {
key: 'appName'
}
}
```

</pre>
</td>
<td>
<pre>

```ts
{
...,
extendConfig: {
key: {
name: 'appName'
}
}
}
```

</pre>
</td>
</tr>
</table>

## Usage

`n9NodeConf([options])`
Expand Down Expand Up @@ -83,10 +133,18 @@ Example : `'./env.json'`

##### key

###### name

Type: `string`\
Default the app name from `package.json`.`name`\
The key to use in configuration extension. The path to load the conf will be `{env}.{app name}`

###### format

Type: `ExtendConfigKeyFormat`\
Default to undefined.\
The format to apply to the `packageJSON.name` to find the key name. The path to load the conf will be `{env}.{format}({app name})`

##### mergeStrategy

Type: `N9ConfMergeStrategy` (`v1` or `v2`)\
Expand Down
45 changes: 43 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ export enum N9ConfMergeStrategy {
V2 = 'v2',
}

export enum ExtendConfigKeyFormat {
DROMEDARY_CASE = 'dromedary-case',
PASCAL_CASE = 'pascal-case',
KEBAB_CASE = 'kebab-case',
UPPER_KEBAB_CASE = 'upper-kebab-case',
SNAKE_CASE = 'snake_case',
UPPER_SNAKE_CASE = 'upper_snake_case',
}

export interface N9ConfOptions {
path?: string;
extendConfig?: {
Expand All @@ -28,7 +37,10 @@ export interface N9ConfOptions {
*/
relative?: string;
};
key?: string;
key?: {
name?: string;
format?: ExtendConfigKeyFormat;
};
mergeStrategy?: N9ConfMergeStrategy;
};
overridePackageJsonDirPath?: string;
Expand Down Expand Up @@ -144,6 +156,28 @@ function loadExtendConfig(
}
}

function getConfigKeyWithFormat(
format: ExtendConfigKeyFormat,
app: { name: string; version: string },
): string {
switch (format) {
case ExtendConfigKeyFormat.DROMEDARY_CASE:
return _.chain(app.name).camelCase().lowerFirst().value();
case ExtendConfigKeyFormat.PASCAL_CASE:
return _.chain(app.name).camelCase().upperFirst().value();
case ExtendConfigKeyFormat.KEBAB_CASE:
return _.chain(app.name).kebabCase().value();
case ExtendConfigKeyFormat.UPPER_KEBAB_CASE:
return _.chain(app.name).kebabCase().toUpper().value();
case ExtendConfigKeyFormat.SNAKE_CASE:
return _.chain(app.name).snakeCase().value();
case ExtendConfigKeyFormat.UPPER_SNAKE_CASE:
return _.chain(app.name).snakeCase().toUpper().value();
default:
throw new Error(`unknown-extend-config-key-format-${format}`);
}
}

export default (options: N9ConfOptions = {}): object | any => {
const rootDir = appRootDir.get();
const confPath: string = process.env.NODE_CONF_PATH || options.path || Path.join(rootDir, 'conf');
Expand All @@ -160,7 +194,14 @@ export default (options: N9ConfOptions = {}): object | any => {
/* eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires, global-require */
const app: { name: string; version: string } = require(packageJsonDirPath);

const extendConfigKey: string = options.extendConfig?.key ?? app.name;
let extendConfigKey: string = app.name;
if (options.extendConfig) {
if (options.extendConfig.key?.name) extendConfigKey = options.extendConfig.key.name;
else if (options.extendConfig.key?.format) {
extendConfigKey = getConfigKeyWithFormat(options.extendConfig.key.format, app);
}
}

const environments = ['application', `${currentEnvironment}`, 'local']; // Files to load
const sources: N9ConfBaseConf[] = []; // Sources of each config file
let extendConfig: { metadata: { mergeStrategy: N9ConfMergeStrategy } };
Expand Down
103 changes: 103 additions & 0 deletions test/extend-conf-key-format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import test, { ExecutionContext } from 'ava';
import { join } from 'path';

import src, { ExtendConfigKeyFormat } from '../src';

test.beforeEach(() => {
delete process.env.NODE_ENV;
});

const formatWithExpectedValues: Record<ExtendConfigKeyFormat, string> = {
[ExtendConfigKeyFormat.DROMEDARY_CASE]: 'dromedaryCase',
[ExtendConfigKeyFormat.PASCAL_CASE]: 'PascalCase',
[ExtendConfigKeyFormat.KEBAB_CASE]: 'kebab-case',
[ExtendConfigKeyFormat.UPPER_KEBAB_CASE]: 'UPPER-KEBAB-CASE',
[ExtendConfigKeyFormat.SNAKE_CASE]: 'snake_case',
[ExtendConfigKeyFormat.UPPER_SNAKE_CASE]: 'UPPER_SNAKE_CASE',
};

for (const [name, expectedSuffix] of Object.entries(formatWithExpectedValues)) {
test(`Extendable conf with key format : ${name}`, (t: ExecutionContext) => {
const conf: { textValue: string } = src({
path: join(__dirname, './fixtures/extend-conf-key-format'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-key-format/env.yaml'),
},
key: { format: name as ExtendConfigKeyFormat },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-key-format'),
});

t.is(conf.textValue, `text override ${expectedSuffix}`, `value is overridden`);
});
}
test(`Extendable conf with key format : unknown format`, (t: ExecutionContext) => {
t.throws(
() =>
src({
path: join(__dirname, './fixtures/extend-conf-key-format'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-key-format/env.yaml'),
},
key: { format: 'wrong-format-name' as ExtendConfigKeyFormat },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-key-format'),
}),
{ message: 'unknown-extend-config-key-format-wrong-format-name' },
);
});
test(`Extendable conf with key format. Name is used if defined.`, (t: ExecutionContext) => {
const conf: { textValue: string } = src({
path: join(__dirname, './fixtures/extend-conf-key-format'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-key-format/env.yaml'),
},
key: { format: ExtendConfigKeyFormat.DROMEDARY_CASE, name: 'APP_NAME' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-key-format'),
});

t.is(
conf.textValue,
`text override UPPER_SNAKE_CASE`,
`value is overridden with UPPER_SNAKE_CASE, format key is ignored`,
);
});

test(`Extendable conf without telling the name or format. PackageJSON.name is used.`, (t: ExecutionContext) => {
let conf: { textValue: string } = src({
path: join(__dirname, './fixtures/extend-conf-key-format'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-key-format/env.yaml'),
},
key: {},
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-key-format'),
});

t.is(
conf.textValue,
`text override kebab-case`,
`value is overridden with kebab-case, key is app-name`,
);

conf = src({
path: join(__dirname, './fixtures/extend-conf-key-format'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-key-format/env.yaml'),
},
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-key-format'),
});

t.is(
conf.textValue,
`text override kebab-case`,
`value is overridden with kebab-case, key is app-name 2`,
);
});
32 changes: 15 additions & 17 deletions test/extend-conf-yaml.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import ava, { ExecutionContext } from 'ava';
import test, { ExecutionContext } from 'ava';
import { join } from 'path';

import src from '../src';

ava('Simple use case with extendable conf in yaml', (t: ExecutionContext) => {
test.beforeEach(() => {
delete process.env.NODE_ENV;
});

test('Simple use case with extendable conf in yaml', (t: ExecutionContext) => {
const conf: { textValue: string; array: string[] } = src({
path: join(__dirname, './fixtures/extend-conf-yaml-1'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-yaml-1/env.yaml'),
},
key: 'appName',
key: { name: 'appName' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-yaml-1'),
});
Expand All @@ -24,15 +27,14 @@ ava('Simple use case with extendable conf in yaml', (t: ExecutionContext) => {
);
});

ava('Can call extending file env.yaml', (t: ExecutionContext) => {
delete process.env.NODE_ENV;
test('Can call extending file env.yaml', (t: ExecutionContext) => {
const conf: { textValue: string; array: string[] } = src({
path: join(__dirname, './fixtures/extend-conf-yaml-1'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-yaml-1/env.json'),
},
key: 'appName',
key: { name: 'appName' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-yaml-1'),
});
Expand All @@ -45,15 +47,14 @@ ava('Can call extending file env.yaml', (t: ExecutionContext) => {
);
});

ava('Can call extending file env.yml', (t: ExecutionContext) => {
delete process.env.NODE_ENV;
test('Can call extending file env.yml', (t: ExecutionContext) => {
const conf: { textValue: string; array: string[] } = src({
path: join(__dirname, './fixtures/extend-conf-yaml-1'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-yml-1/env.json'),
},
key: 'appName',
key: { name: 'appName' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-yaml-1'),
});
Expand All @@ -66,15 +67,14 @@ ava('Can call extending file env.yml', (t: ExecutionContext) => {
);
});

ava('Can call extending file env.json', (t: ExecutionContext) => {
delete process.env.NODE_ENV;
test('Can call extending file env.json', (t: ExecutionContext) => {
const conf: { textValue: string; array: string[] } = src({
path: join(__dirname, './fixtures/extend-conf-1'),
extendConfig: {
path: {
absolute: join(__dirname, './fixtures/extend-conf-1/env.yaml'),
},
key: 'appName',
key: { name: 'appName' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-1'),
});
Expand All @@ -87,8 +87,7 @@ ava('Can call extending file env.json', (t: ExecutionContext) => {
);
});

ava("Can't load unknown file type", (t: ExecutionContext) => {
delete process.env.NODE_ENV;
test("Can't load unknown file type", (t: ExecutionContext) => {
const error = t.throws(
() => {
src({
Expand All @@ -111,8 +110,7 @@ ava("Can't load unknown file type", (t: ExecutionContext) => {
);
});

ava('Extendable conf does not exists is ignored', (t: ExecutionContext) => {
delete process.env.NODE_ENV;
test('Extendable conf does not exists is ignored', (t: ExecutionContext) => {
process.env.NODE_CONF_EXTEND_ABSOLUTE_PATH = join(__dirname, './fixtures/wrong-path/env.yaml');
t.notThrows(() => {
src({
Expand All @@ -126,7 +124,7 @@ ava('Extendable conf does not exists is ignored', (t: ExecutionContext) => {
delete process.env.NODE_CONF_EXTEND_ABSOLUTE_PATH;
});

ava('Extendable conf error due to invalid yaml', (t: ExecutionContext) => {
test('Extendable conf error due to invalid yaml', (t: ExecutionContext) => {
const error = t.throws(
() => {
src({
Expand Down
2 changes: 1 addition & 1 deletion test/extend-conf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ava('Simple use case with extendable conf', (t: ExecutionContext) => {
path: {
absolute: join(__dirname, './fixtures/extend-conf-1/env.json'),
},
key: 'appName',
key: { name: 'appName' },
},
overridePackageJsonDirPath: join(__dirname, './fixtures/extend-conf-1'),
});
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/extend-conf-key-format/application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
textValue: 'base value',
};
Loading

0 comments on commit 4cdf21c

Please sign in to comment.