Skip to content

Commit 6726246

Browse files
lundibundijasnell
authored andcommitted
lib: allow to validate enums with validateOneOf
PR-URL: #34070 Reviewed-By: Zeyu Yang <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent c62cf1d commit 6726246

10 files changed

+108
-40
lines changed

lib/_http_agent.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ const { async_id_symbol } = require('internal/async_hooks').symbols;
3939
const {
4040
codes: {
4141
ERR_INVALID_ARG_TYPE,
42-
ERR_INVALID_OPT_VALUE,
4342
ERR_OUT_OF_RANGE,
4443
},
4544
} = require('internal/errors');
4645
const { once } = require('internal/util');
47-
const { validateNumber } = require('internal/validators');
46+
const { validateNumber, validateOneOf } = require('internal/validators');
4847

4948
const kOnKeylog = Symbol('onkeylog');
5049
const kRequestOptions = Symbol('requestOptions');
@@ -99,9 +98,7 @@ function Agent(options) {
9998
this.maxTotalSockets = this.options.maxTotalSockets;
10099
this.totalSocketCount = 0;
101100

102-
if (this.scheduling !== 'fifo' && this.scheduling !== 'lifo') {
103-
throw new ERR_INVALID_OPT_VALUE('scheduling', this.scheduling);
104-
}
101+
validateOneOf(this.scheduling, 'scheduling', ['fifo', 'lifo'], true);
105102

106103
if (this.maxTotalSockets !== undefined) {
107104
validateNumber(this.maxTotalSockets, 'maxTotalSockets');

lib/dns.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const {
4949
const {
5050
validatePort,
5151
validateString,
52+
validateOneOf,
5253
} = require('internal/validators');
5354

5455
const {
@@ -114,8 +115,7 @@ function lookup(hostname, options, callback) {
114115
family = options >>> 0;
115116
}
116117

117-
if (family !== 0 && family !== 4 && family !== 6)
118-
throw new ERR_INVALID_OPT_VALUE('family', family);
118+
validateOneOf(family, 'family', [0, 4, 6], true);
119119

120120
if (!hostname) {
121121
emitInvalidHostnameWarning(hostname);

lib/internal/child_process.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const {
2121
ERR_MISSING_ARGS
2222
}
2323
} = require('internal/errors');
24-
const { validateString } = require('internal/validators');
24+
const { validateString, validateOneOf } = require('internal/validators');
2525
const EventEmitter = require('events');
2626
const net = require('net');
2727
const dgram = require('dgram');
@@ -345,13 +345,9 @@ ChildProcess.prototype.spawn = function(options) {
345345
const ipcFd = stdio.ipcFd;
346346
stdio = options.stdio = stdio.stdio;
347347

348-
if (options.serialization !== undefined &&
349-
options.serialization !== 'json' &&
350-
options.serialization !== 'advanced') {
351-
throw new ERR_INVALID_OPT_VALUE('options.serialization',
352-
options.serialization);
353-
}
354348

349+
validateOneOf(options.serialization, 'options.serialization',
350+
[undefined, 'json', 'advanced'], true);
355351
const serialization = options.serialization || 'json';
356352

357353
if (ipc !== undefined) {

lib/internal/dns/promises.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ const {
3131
} = codes;
3232
const {
3333
validatePort,
34-
validateString
34+
validateString,
35+
validateOneOf,
3536
} = require('internal/validators');
3637

3738
function onlookup(err, addresses) {
@@ -116,8 +117,7 @@ function lookup(hostname, options) {
116117
family = options >>> 0;
117118
}
118119

119-
if (family !== 0 && family !== 4 && family !== 6)
120-
throw new ERR_INVALID_OPT_VALUE('family', family);
120+
validateOneOf(family, 'family', [0, 4, 6], true);
121121

122122
return createLookupPromise(family, hostname, all, hints, verbatim);
123123
}

lib/internal/validators.js

+17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const {
1313
ERR_SOCKET_BAD_PORT,
1414
ERR_INVALID_ARG_TYPE,
1515
ERR_INVALID_ARG_VALUE,
16+
ERR_INVALID_OPT_VALUE,
1617
ERR_OUT_OF_RANGE,
1718
ERR_UNKNOWN_SIGNAL,
1819
ERR_INVALID_CALLBACK,
@@ -126,6 +127,21 @@ function validateNumber(value, name) {
126127
throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
127128
}
128129

130+
const validateOneOf = hideStackFrames((value, name, oneOf, option = false) => {
131+
if (!oneOf.includes(value)) {
132+
const allowed = oneOf
133+
.map((v) => (typeof v === 'string' ? `'${v}'` : String(v)))
134+
.join(', ');
135+
if (!option) {
136+
const reason = 'must be one of: ' + allowed;
137+
throw new ERR_INVALID_ARG_VALUE(name, value, reason);
138+
} else {
139+
const reason = 'Must be one of: ' + allowed;
140+
throw new ERR_INVALID_OPT_VALUE(name, value, reason);
141+
}
142+
}
143+
});
144+
129145
function validateBoolean(value, name) {
130146
if (typeof value !== 'boolean')
131147
throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value);
@@ -212,6 +228,7 @@ module.exports = {
212228
validateInteger,
213229
validateNumber,
214230
validateObject,
231+
validateOneOf,
215232
validatePort,
216233
validateSignalName,
217234
validateString,

lib/vm.js

+6-20
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ const {
3939
const {
4040
ERR_CONTEXT_NOT_INITIALIZED,
4141
ERR_INVALID_ARG_TYPE,
42-
ERR_INVALID_ARG_VALUE,
4342
} = require('internal/errors').codes;
4443
const {
4544
isArrayBufferView,
@@ -52,6 +51,7 @@ const {
5251
validateBoolean,
5352
validateBuffer,
5453
validateObject,
54+
validateOneOf,
5555
} = require('internal/validators');
5656
const {
5757
kVmBreakFirstLineSymbol,
@@ -246,17 +246,11 @@ function createContext(contextObject = {}, options = {}) {
246246

247247
let microtaskQueue = null;
248248
if (microtaskMode !== undefined) {
249-
validateString(microtaskMode, 'options.microtaskMode');
249+
validateOneOf(microtaskMode, 'options.microtaskMode',
250+
['afterEvaluate', undefined]);
250251

251-
if (microtaskMode === 'afterEvaluate') {
252+
if (microtaskMode === 'afterEvaluate')
252253
microtaskQueue = new MicrotaskQueue();
253-
} else {
254-
throw new ERR_INVALID_ARG_VALUE(
255-
'options.microtaskQueue',
256-
microtaskQueue,
257-
'must be \'afterEvaluate\' or undefined'
258-
);
259-
}
260254
}
261255

262256
makeContext(contextObject, name, origin, strings, wasm, microtaskQueue);
@@ -395,16 +389,8 @@ function measureMemory(options = {}) {
395389
emitExperimentalWarning('vm.measureMemory');
396390
validateObject(options, 'options');
397391
const { mode = 'summary', execution = 'default' } = options;
398-
if (mode !== 'summary' && mode !== 'detailed') {
399-
throw new ERR_INVALID_ARG_VALUE(
400-
'options.mode', options.mode,
401-
'must be either \'summary\' or \'detailed\'');
402-
}
403-
if (execution !== 'default' && execution !== 'eager') {
404-
throw new ERR_INVALID_ARG_VALUE(
405-
'options.execution', options.execution,
406-
'must be either \'default\' or \'eager\'');
407-
}
392+
validateOneOf(mode, 'options.mode', ['summary', 'detailed']);
393+
validateOneOf(execution, 'options.execution', ['default', 'eager']);
408394
const result = _measureMemory(measureMemoryModes[mode],
409395
measureMemoryExecutions[execution]);
410396
if (result === undefined) {

test/parallel/test-child-process-advanced-serialization.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ if (process.argv[2] !== 'child') {
1111
}, {
1212
code: 'ERR_INVALID_OPT_VALUE',
1313
message: `The value "${value}" is invalid ` +
14-
'for option "options.serialization"'
14+
'for option "options.serialization". ' +
15+
"Must be one of: undefined, 'json', 'advanced'"
1516
});
1617
}
1718

test/parallel/test-dns-lookup.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ assert.throws(() => {
7272
const err = {
7373
code: 'ERR_INVALID_OPT_VALUE',
7474
name: 'TypeError',
75-
message: 'The value "20" is invalid for option "family"'
75+
message: 'The value "20" is invalid for option "family". ' +
76+
'Must be one of: 0, 4, 6'
7677
};
7778
const options = {
7879
hints: 0,

test/parallel/test-http-agent-scheduling.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ function badSchedulingOptionTest() {
137137
assert.strictEqual(err.code, 'ERR_INVALID_OPT_VALUE');
138138
assert.strictEqual(
139139
err.message,
140-
'The value "filo" is invalid for option "scheduling"'
140+
'The value "filo" is invalid for option "scheduling". ' +
141+
"Must be one of: 'fifo', 'lifo'"
141142
);
142143
}
143144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
require('../common');
5+
const assert = require('assert');
6+
const { validateOneOf } = require('internal/validators');
7+
8+
{
9+
// validateOneOf number incorrect.
10+
const allowed = [2, 3];
11+
assert.throws(() => validateOneOf(1, 'name', allowed), {
12+
code: 'ERR_INVALID_ARG_VALUE',
13+
// eslint-disable-next-line quotes
14+
message: `The argument 'name' must be one of: 2, 3. Received 1`
15+
});
16+
assert.throws(() => validateOneOf(1, 'name', allowed, true), {
17+
code: 'ERR_INVALID_OPT_VALUE',
18+
message: 'The value "1" is invalid for option "name". ' +
19+
'Must be one of: 2, 3'
20+
});
21+
}
22+
23+
{
24+
// validateOneOf number correct.
25+
validateOneOf(2, 'name', [1, 2]);
26+
}
27+
28+
{
29+
// validateOneOf string incorrect.
30+
const allowed = ['b', 'c'];
31+
assert.throws(() => validateOneOf('a', 'name', allowed), {
32+
code: 'ERR_INVALID_ARG_VALUE',
33+
// eslint-disable-next-line quotes
34+
message: `The argument 'name' must be one of: 'b', 'c'. Received 'a'`
35+
});
36+
assert.throws(() => validateOneOf('a', 'name', allowed, true), {
37+
code: 'ERR_INVALID_OPT_VALUE',
38+
// eslint-disable-next-line quotes
39+
message: `The value "a" is invalid for option "name". ` +
40+
"Must be one of: 'b', 'c'",
41+
});
42+
}
43+
44+
{
45+
// validateOneOf string correct.
46+
validateOneOf('two', 'name', ['one', 'two']);
47+
}
48+
49+
{
50+
// validateOneOf Symbol incorrect.
51+
const allowed = [Symbol.for('b'), Symbol.for('c')];
52+
assert.throws(() => validateOneOf(Symbol.for('a'), 'name', allowed), {
53+
code: 'ERR_INVALID_ARG_VALUE',
54+
// eslint-disable-next-line quotes
55+
message: `The argument 'name' must be one of: Symbol(b), Symbol(c). ` +
56+
'Received Symbol(a)'
57+
});
58+
assert.throws(() => validateOneOf(Symbol.for('a'), 'name', allowed, true), {
59+
code: 'ERR_INVALID_OPT_VALUE',
60+
message: 'The value "Symbol(a)" is invalid for option "name". ' +
61+
'Must be one of: Symbol(b), Symbol(c)',
62+
});
63+
}
64+
65+
{
66+
// validateOneOf Symbol correct.
67+
const allowed = [Symbol.for('b'), Symbol.for('c')];
68+
validateOneOf(Symbol.for('b'), 'name', allowed);
69+
}

0 commit comments

Comments
 (0)