Skip to content

Commit b0a7948

Browse files
feat: add optional joi env var schema
1 parent fc4a392 commit b0a7948

7 files changed

+241
-1
lines changed

lib/joi/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './joi.schema';

lib/joi/joi.schema.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Joi from 'joi';
2+
import { LoggerLevel } from '../logger.interfaces';
3+
import {
4+
JOI_LOGGER_COLORIZE,
5+
JOI_LOGGER_CUSTOM_LEVELS_ORDER,
6+
JOI_LOGGER_EXCLUDE,
7+
JOI_LOGGER_PRETTY,
8+
JOI_LOGGER_REDACT,
9+
LOGGER_LEVEL,
10+
} from '../logger.defaults';
11+
12+
const customSchema = Joi.any().custom((value) =>
13+
value
14+
.split(',')
15+
.map((el: string) => el.trim())
16+
.filter((el: string) => el !== ''),
17+
);
18+
19+
export const loggerSchema = Joi.object({
20+
LOGGER_LEVEL: Joi.string()
21+
.valid(...Object.values(LoggerLevel))
22+
.default(LOGGER_LEVEL),
23+
LOGGER_CUSTOM_LEVELS_ORDER: Joi.boolean().default(JOI_LOGGER_CUSTOM_LEVELS_ORDER),
24+
LOGGER_PRETTY: Joi.boolean().default(JOI_LOGGER_PRETTY),
25+
LOGGER_COLORIZE: Joi.boolean().default(JOI_LOGGER_COLORIZE),
26+
LOGGER_REDACT: customSchema.default(JOI_LOGGER_REDACT),
27+
LOGGER_EXCLUDE: customSchema.default(JOI_LOGGER_EXCLUDE),
28+
});

lib/logger.defaults.ts

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ export const CLASS_VALIDATOR_LOGGER_COLORIZE = true;
1414
export const CLASS_VALIDATOR_LOGGER_REDACT = [];
1515
export const CLASS_VALIDATOR_LOGGER_EXCLUDE = [];
1616

17+
export const JOI_LOGGER_CUSTOM_LEVELS_ORDER = false;
18+
export const JOI_LOGGER_PRETTY = true;
19+
export const JOI_LOGGER_COLORIZE = true;
20+
export const JOI_LOGGER_REDACT = [];
21+
export const JOI_LOGGER_EXCLUDE = [];
22+
1723
export const loggerModuleOptions = {
1824
level: LoggerLevel.Debug,
1925
customLevelsOrder: false,

lib/test/class-validator.schema.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { plainToInstance } from 'class-transformer';
22
import { validateSync } from 'class-validator';
33
import { LoggerSchema } from '../class-validator';
44

5-
describe('OSLocalSchema (spec)', () => {
5+
describe('class-validator: LoggerSchema (spec)', () => {
66
[
77
{
88
description: 'Should fail if LOGGER_CUSTOM_LEVELS cannot be parsed',

lib/test/joi.schema.spec.ts

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { loggerSchema } from '../joi';
2+
import {
3+
JOI_LOGGER_COLORIZE,
4+
JOI_LOGGER_CUSTOM_LEVELS_ORDER,
5+
JOI_LOGGER_EXCLUDE,
6+
JOI_LOGGER_PRETTY,
7+
JOI_LOGGER_REDACT,
8+
LOGGER_LEVEL,
9+
} from '../logger.defaults';
10+
import { LoggerLevel } from '../logger.interfaces';
11+
12+
describe('joi: loggerSchema (spec)', () => {
13+
// Joi type coercion by default
14+
// Fails asap
15+
[
16+
{
17+
description: 'Should fail if LOGGER_LEVEL is unknown',
18+
env: {
19+
LOGGER_LEVEL: 'whatever',
20+
},
21+
expected: `"LOGGER_LEVEL" must be one of [${Object.values(LoggerLevel).join(', ')}]`,
22+
},
23+
{
24+
description: 'Should fail if LOGGER_CUSTOM_LEVELS_ORDER is not a valid bool',
25+
env: {
26+
LOGGER_CUSTOM_LEVELS_ORDER: 'whatever',
27+
},
28+
expected: '"LOGGER_CUSTOM_LEVELS_ORDER" must be a boolean',
29+
},
30+
{
31+
description: 'Should fail if LOGGER_PRETTY is not a valid bool',
32+
env: {
33+
LOGGER_PRETTY: 'whatever',
34+
},
35+
expected: '"LOGGER_PRETTY" must be a boolean',
36+
},
37+
{
38+
description: 'Should fail if LOGGER_COLORIZE is not a valid bool',
39+
env: {
40+
LOGGER_COLORIZE: 'whatever',
41+
},
42+
expected: '"LOGGER_COLORIZE" must be a boolean',
43+
},
44+
{
45+
description: 'Should fail if LOGGER_REDACT is invalid',
46+
env: {
47+
LOGGER_REDACT: 'whatever,',
48+
},
49+
expected: undefined,
50+
},
51+
{
52+
description: 'Should fail if LOGGER_EXCLUDE is invalid',
53+
env: {
54+
LOGGER_EXCLUDE: 'whatever,',
55+
},
56+
expected: undefined,
57+
},
58+
].forEach(({ description, env, expected }) =>
59+
it(`${description}`, () => {
60+
const r = loggerSchema.validate(env);
61+
62+
expect(r.error?.details[0]?.message).toStrictEqual(expected);
63+
}),
64+
);
65+
66+
[
67+
{
68+
description: 'Should NOT fail if NO env is provided',
69+
env: {},
70+
expected: {
71+
LOGGER_LEVEL: LOGGER_LEVEL,
72+
LOGGER_CUSTOM_LEVELS_ORDER: JOI_LOGGER_CUSTOM_LEVELS_ORDER,
73+
LOGGER_PRETTY: JOI_LOGGER_PRETTY,
74+
LOGGER_COLORIZE: JOI_LOGGER_COLORIZE,
75+
LOGGER_REDACT: JOI_LOGGER_REDACT,
76+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
77+
},
78+
},
79+
{
80+
description: 'Should NOT fail if LOGGER_CUSTOM_LEVELS_ORDER is a valid bool string',
81+
env: {
82+
LOGGER_CUSTOM_LEVELS_ORDER: 'true',
83+
},
84+
expected: {
85+
LOGGER_LEVEL: LOGGER_LEVEL,
86+
LOGGER_CUSTOM_LEVELS_ORDER: true,
87+
LOGGER_PRETTY: JOI_LOGGER_PRETTY,
88+
LOGGER_COLORIZE: JOI_LOGGER_COLORIZE,
89+
LOGGER_REDACT: JOI_LOGGER_REDACT,
90+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
91+
},
92+
},
93+
{
94+
description: 'Should fail if LOGGER_PRETTY is a valid bool string',
95+
env: {
96+
LOGGER_PRETTY: 'false',
97+
},
98+
expected: {
99+
LOGGER_LEVEL: LOGGER_LEVEL,
100+
LOGGER_CUSTOM_LEVELS_ORDER: JOI_LOGGER_CUSTOM_LEVELS_ORDER,
101+
LOGGER_PRETTY: false,
102+
LOGGER_COLORIZE: JOI_LOGGER_COLORIZE,
103+
LOGGER_REDACT: JOI_LOGGER_REDACT,
104+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
105+
},
106+
},
107+
{
108+
description: 'Should fail if LOGGER_COLORIZE is a valid bool string',
109+
env: {
110+
LOGGER_COLORIZE: 'false',
111+
},
112+
expected: {
113+
LOGGER_LEVEL: LOGGER_LEVEL,
114+
LOGGER_CUSTOM_LEVELS_ORDER: JOI_LOGGER_CUSTOM_LEVELS_ORDER,
115+
LOGGER_PRETTY: JOI_LOGGER_PRETTY,
116+
LOGGER_COLORIZE: false,
117+
LOGGER_REDACT: JOI_LOGGER_REDACT,
118+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
119+
},
120+
},
121+
{
122+
description: 'Should NOT fail if LOGGER_REDACT is an empty string',
123+
env: {
124+
LOGGER_REDACT: '',
125+
},
126+
expected: {
127+
LOGGER_LEVEL: LOGGER_LEVEL,
128+
LOGGER_CUSTOM_LEVELS_ORDER: JOI_LOGGER_CUSTOM_LEVELS_ORDER,
129+
LOGGER_PRETTY: JOI_LOGGER_PRETTY,
130+
LOGGER_COLORIZE: JOI_LOGGER_COLORIZE,
131+
LOGGER_REDACT: JOI_LOGGER_REDACT,
132+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
133+
},
134+
},
135+
{
136+
description: 'Should NOT fail if LOGGER_EXCLUDE is an empty string',
137+
env: {
138+
LOGGER_EXCLUDE: '',
139+
},
140+
expected: {
141+
LOGGER_LEVEL: LOGGER_LEVEL,
142+
LOGGER_CUSTOM_LEVELS_ORDER: JOI_LOGGER_CUSTOM_LEVELS_ORDER,
143+
LOGGER_PRETTY: JOI_LOGGER_PRETTY,
144+
LOGGER_COLORIZE: JOI_LOGGER_COLORIZE,
145+
LOGGER_REDACT: JOI_LOGGER_REDACT,
146+
LOGGER_EXCLUDE: JOI_LOGGER_EXCLUDE,
147+
},
148+
},
149+
].forEach(({ description, env, expected }) =>
150+
it(`${description}`, () => {
151+
const r = loggerSchema.validate(env);
152+
153+
expect(r.value).toStrictEqual(expected);
154+
expect(r.error?.details).toStrictEqual(undefined);
155+
}),
156+
);
157+
});

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@nestjs/platform-fastify": "^10.0.0",
3939
"class-transformer": "^0.5.1",
4040
"class-validator": "^0.14.1",
41+
"joi": "^17.13.3",
4142
"reflect-metadata": "^0.2.2",
4243
"rxjs": "^7.8.1",
4344
"zod": "^3.0.0"
@@ -55,6 +56,9 @@
5556
"class-validator": {
5657
"optional": true
5758
},
59+
"joi": {
60+
"optional": true
61+
},
5862
"rxjs": {
5963
"optional": true
6064
},
@@ -92,6 +96,7 @@
9296
"fastify": "^4.28.1",
9397
"husky": "^9.1.4",
9498
"jest": "^29.7.0",
99+
"joi": "^17.13.3",
95100
"lint-staged": "^15.2.9",
96101
"prettier": "^3.3.3",
97102
"reflect-metadata": "^0.2.2",

pnpm-lock.yaml

+43
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)