forked from renzenicolai/nicolai-jsonrpc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
checkParameters.js
177 lines (168 loc) · 8.4 KB
/
checkParameters.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
* Function for checking user input against a schema
*
* Copyright 2021 Renze Nicolai
* This code is released under the MIT license.
* SPDX-License-Identifier: MIT
*/
"use strict";
function checkParameters(parameters, constraints, path="/") {
if (Array.isArray(constraints)) {
for (let index = 0; index < constraints.length; index++) {
let [result, reason] = checkParameters(parameters, constraints[index], "{" + index + "}" + path);
if (result) {
return [result, reason];
}
}
return [false, "Supplied parameters don't match with any of the accepted types (" + path + ")"];
} else {
if (typeof constraints !== "object") {
throw Error("Expected constraints to be an object");
}
if (Array.isArray(constraints.type)) {
for (let typeIndex = 0; typeIndex < constraints.type.length; typeIndex++) {
let [typeResult, typeReason] = checkParameters(parameters, {type: constraints.type[typeIndex]}, "<" + typeIndex + ">" + path);
if (typeResult) {
return [typeResult, typeReason];
}
}
return [false, "Constraints specify to accept any of " + constraints.type.join(", ") + " (" + path + ")"];
} else if (typeof constraints.type === "undefined") {
return [true, "Constraints don't specify a type"];
} else if (typeof constraints.type !== "string") {
console.error("Constraints don't contain a type", constraints, "Path:", path);
throw Error("Expected constraints to contain a type (" + path + ")");
}
switch (constraints.type) {
case "any":
return [true, "Constraints specify to accept everything (" + path + ")"];
case "none":
case "null":
case null:
return [(parameters === null), "Constraints specify to accept no parameters (" + path + ")"];
case "array":
if (Array.isArray(parameters)) {
return checkArray(parameters, constraints, path);
} else {
return [false, "Constraints specify to accept an array (" + path + ")"];
}
case "object":
if (Array.isArray(parameters)) {
return [false, "Constraints specify to accept an object, not an array (" + path + ")"];
} else if (typeof parameters === "object") {
return checkObject(parameters, constraints, path);
} else {
return [false, "Constraints specify to accept an object (" + path + ")"];
}
case "string":
case "number":
case "boolean":
default:
return [(typeof parameters === constraints.type), "Constraints specify to accept a " + constraints.type + " (" + path + ")"];
}
}
}
function checkArray(parameters, constraints, path="/") {
if ((typeof constraints.length === "number") && (parameters.length !== constraints.length)) {
// Length is defined and does not match
return [false, "Expected an array with " + constraints.length + " elements, found " + parameters.length + " elements (" + path + ")"];
}
if ((typeof constraints.minlength === "number") && (parameters.length < constraints.minlength)) {
// Minimum length is defined and does not match
return [false, "Expected an array with at least " + constraints.minlength + " elements, found " + parameters.length + " elements (" + path + ")"];
}
if ((typeof constraints.maxlength === "number") && (parameters.length > constraints.maxlength)) {
// Maximum length is defined and does not match
return [false, "Expected an array with at most " + constraints.maxlength + " elements, found " + parameters.length + " elements (" + path + ")"];
}
if (typeof constraints.contains === "string") {
for (let index = 0; index < parameters.length; index++ ) {
let [result, reason] = checkParameters(parameters[index], { type: constraints.contains }, "[" + index + "]" + path);
if (!result) {
return [result, reason];
}
}
} else if (Array.isArray(constraints.contains)) {
for (let index = 0; index < parameters.length; index++ ) {
let result = false;
let reason = "";
for (let constraintIndex = 0; constraintIndex < constraints.contains.length; constraintIndex++) {
let constraint = constraints.contains[constraintIndex];
if (typeof constraint === "string") {
constraint = {type: constraint};
}
[result, reason] = checkParameters(parameters[index], constraint, "[" + index + "]" + path);
if (result) {
break;
}
}
if (!result) {
return [result, reason];
}
}
} else if (typeof constraints.contains === "object") {
for (let index = 0; index < parameters.length; index++ ) {
let [result, reason] = checkParameters(parameters[index], constraints.contains, "[" + index + "]" + path);
if (!result) {
return [result, reason];
}
}
} else if (typeof constraints.contains !== "undefined") {
throw Error("Expected constraints.contains to be a string, an object or undefined (" + path + ")");
}
return [true, "Array contents match the constraints (" + path + ")"];
}
function checkObject(parameters, constraints, path="/") {
if ((typeof constraints.contains === "object") && (typeof constraints.required === "undefined")) {
constraints.required = constraints.contains;
}
if ((typeof constraints.required === "undefined") && (typeof constraints.optional === "undefined")) {
// When the object has no constraints
return [true, "Object has no further constraints (" + path + ")"];
}
// When the object has required parameters
if (typeof constraints.required !== "undefined") {
for (let item in constraints.required) {
if (typeof parameters[item] === "undefined") {
// And a required parameter is missing
return [false, "Object is missing required parameter '" + item + "' (" + path + ")"];
}
if (typeof constraints.required[item] === "object") {
// If constraints are set for the content of the required parameter
let [result, subReason] = checkParameters(parameters[item], constraints.required[item], path + item + "/");
if (!result) {
// The constraints of the parameter were not met
return [false, subReason];
break;
}
} else if (typeof item !== "string") {
throw Error("Required fields of an object must be specified as an array of string keynames or objects with a type field");
}
}
}
// Check that the object does not contain stray parameters
for (let item in parameters) {
if (Array.isArray(constraints.required) && (constraints.required.indexOf(item) >= 0)) {
// The parameter is a required parameter
continue;
} else if ((typeof constraints.required === "object") && (item in constraints.required)) {
// The parameter is a required parameter
continue;
} else if ((typeof constraints.optional !== "undefined") && (item in constraints.optional)) {
// The parameter is an optional parameter
if (typeof constraints.optional[item].type !== "undefined") {
// If constraints are set for the contents of the optional parameter
let [result, subReason] = checkParameters(parameters[item], constraints.optional[item], path + item + "/");
if (!result) {
// The constraints of the parameter were not met
return [false, subReason];
}
}
} else {
// The parameter is neither a required or an optional parameter
return [false, "Found stray parameter " + item + " (" + path + ")"];
}
}
return [true, "Object matches the constraints (" + path + ")"];
}
module.exports = checkParameters;