@finebi/finebi-cli 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +312 -0
- package/package.json +37 -0
- package/src/access-key-contract.js +2 -0
- package/src/access-key.js +174 -0
- package/src/auth-failure.js +18 -0
- package/src/auth-headers.js +21 -0
- package/src/auth.js +116 -0
- package/src/branding.js +31 -0
- package/src/browser-auth.js +331 -0
- package/src/capability.js +122 -0
- package/src/config-permissions.js +87 -0
- package/src/config.js +189 -0
- package/src/dynamic-command.js +324 -0
- package/src/error-catalog.js +470 -0
- package/src/errors.js +22 -0
- package/src/help.js +452 -0
- package/src/index.js +1338 -0
- package/src/output-limits.js +188 -0
- package/src/output.js +33 -0
- package/src/profiles.js +158 -0
- package/src/schema-options.js +265 -0
- package/src/schema-validator.js +194 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {cliError} from "./errors.js";
|
|
2
|
+
|
|
3
|
+
export function validateSchemaValue(value, schema, path) {
|
|
4
|
+
const effectiveSchema = schema || {};
|
|
5
|
+
// nullable 是 capability 显式声明的字段级约束;required 只控制字段是否必须出现。
|
|
6
|
+
if (value === null && effectiveSchema.nullable === true) {
|
|
7
|
+
return { ok: true };
|
|
8
|
+
}
|
|
9
|
+
const typeError = validateType(value, effectiveSchema, path);
|
|
10
|
+
if (typeError) {
|
|
11
|
+
return typeError;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (effectiveSchema.discriminator && Array.isArray(effectiveSchema.variants)) {
|
|
15
|
+
return validateDiscriminator(value, effectiveSchema, path);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (effectiveSchema.type === "object") {
|
|
19
|
+
return validateObject(value, effectiveSchema, path);
|
|
20
|
+
}
|
|
21
|
+
if (effectiveSchema.type === "array") {
|
|
22
|
+
return validateArray(value, effectiveSchema, path);
|
|
23
|
+
}
|
|
24
|
+
return validateScalar(value, effectiveSchema, path);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function validateType(value, schema, path) {
|
|
28
|
+
switch (schema.type) {
|
|
29
|
+
case "object":
|
|
30
|
+
if (!isPlainObject(value)) {
|
|
31
|
+
return invalidRequestJson(path, `Expected object at "${path}".`);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
case "array":
|
|
35
|
+
if (!Array.isArray(value)) {
|
|
36
|
+
return invalidRequestJson(path, `Expected array at "${path}".`);
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
case "string":
|
|
40
|
+
if (typeof value !== "string") {
|
|
41
|
+
return invalidRequestJson(path, `Expected string at "${path}".`);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
case "integer":
|
|
45
|
+
if (!Number.isInteger(value)) {
|
|
46
|
+
return invalidRequestJson(path, `Expected integer at "${path}".`);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
case "number":
|
|
50
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
51
|
+
return invalidRequestJson(path, `Expected number at "${path}".`);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
case "boolean":
|
|
55
|
+
if (typeof value !== "boolean") {
|
|
56
|
+
return invalidRequestJson(path, `Expected boolean at "${path}".`);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
default:
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function validateDiscriminator(value, schema, path) {
|
|
65
|
+
const discriminator = schema.discriminator;
|
|
66
|
+
const discriminatorPath = joinPath(path, discriminator);
|
|
67
|
+
if (!Object.prototype.hasOwnProperty.call(value, discriminator)) {
|
|
68
|
+
return invalidRequestJson(discriminatorPath, `Missing discriminator field "${discriminator}".`);
|
|
69
|
+
}
|
|
70
|
+
const discriminatorValue = value[discriminator];
|
|
71
|
+
const variant = schema.variants.find((item) => item.value === discriminatorValue);
|
|
72
|
+
if (!variant) {
|
|
73
|
+
return invalidRequestJson(
|
|
74
|
+
discriminatorPath,
|
|
75
|
+
`Unsupported discriminator value "${discriminatorValue}" for "${discriminator}". Expected one of: ${schema.variants.map((item) => item.value).join(", ")}.`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const validation = validateSchemaValue(value, variant.schema, path);
|
|
79
|
+
if (!validation.ok && validation.error.message.startsWith("Unsupported field ")) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: {
|
|
83
|
+
...validation.error,
|
|
84
|
+
message: `${validation.error.message.slice(0, -1)} for discriminator "${discriminator}" value "${discriminatorValue}".`,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return validation;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validateObject(value, schema, path) {
|
|
92
|
+
const properties = schema.properties || {};
|
|
93
|
+
for (const requiredField of schema.required || []) {
|
|
94
|
+
if (!Object.prototype.hasOwnProperty.call(value, requiredField)) {
|
|
95
|
+
return invalidRequestJson(joinPath(path, requiredField), `Missing required field "${requiredField}".`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (Object.keys(properties).length > 0) {
|
|
99
|
+
for (const fieldName of Object.keys(value)) {
|
|
100
|
+
if (!Object.prototype.hasOwnProperty.call(properties, fieldName)) {
|
|
101
|
+
return invalidRequestJson(joinPath(path, fieldName), `Unsupported field "${fieldName}".`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const fieldName of Object.keys(properties)) {
|
|
106
|
+
if (!Object.prototype.hasOwnProperty.call(value, fieldName)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const validation = validateSchemaValue(value[fieldName], nestedSchema(properties[fieldName]), joinPath(path, fieldName));
|
|
110
|
+
if (!validation.ok) {
|
|
111
|
+
return validation;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { ok: true };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateArray(value, schema, path) {
|
|
118
|
+
if (schema.minItems !== undefined && schema.minItems !== null && value.length < Number(schema.minItems)) {
|
|
119
|
+
return invalidRequestJson(path, `"${path}" must contain at least ${schema.minItems} item(s).`);
|
|
120
|
+
}
|
|
121
|
+
const itemSchema = schema.items;
|
|
122
|
+
if (!itemSchema) {
|
|
123
|
+
return { ok: true };
|
|
124
|
+
}
|
|
125
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
126
|
+
const validation = validateSchemaValue(value[index], nestedSchema(itemSchema), `${path}[${index}]`);
|
|
127
|
+
if (!validation.ok) {
|
|
128
|
+
return validation;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function validateScalar(value, schema, path) {
|
|
135
|
+
const constValue = schema.constValue ?? schema.const;
|
|
136
|
+
if (constValue !== undefined && constValue !== null && value !== constValue) {
|
|
137
|
+
return invalidRequestJson(path, `"${path}" must equal ${JSON.stringify(constValue)}.`);
|
|
138
|
+
}
|
|
139
|
+
const enumValues = schema.enumValues || schema.enum || [];
|
|
140
|
+
if (enumValues.length > 0 && !enumValues.includes(value)) {
|
|
141
|
+
return invalidRequestJson(path, `"${path}" must be one of: ${enumValues.join(", ")}.`);
|
|
142
|
+
}
|
|
143
|
+
if ((schema.type === "integer" || schema.type === "number") && schema.minimum !== undefined && schema.minimum !== null && value < Number(schema.minimum)) {
|
|
144
|
+
return invalidRequestJson(path, `"${path}" must be greater than or equal to ${schema.minimum}.`);
|
|
145
|
+
}
|
|
146
|
+
if ((schema.type === "integer" || schema.type === "number") && schema.maximum !== undefined && schema.maximum !== null && value > Number(schema.maximum)) {
|
|
147
|
+
return invalidRequestJson(path, `"${path}" must be less than or equal to ${schema.maximum}.`);
|
|
148
|
+
}
|
|
149
|
+
if (schema.type === "string" && schema.pattern && !new RegExp(schema.pattern).test(value)) {
|
|
150
|
+
return invalidRequestJson(path, `"${path}" must match pattern ${schema.pattern}.`);
|
|
151
|
+
}
|
|
152
|
+
if (schema.type === "string" && schema.minLength !== undefined && schema.minLength !== null
|
|
153
|
+
&& value.length < Number(schema.minLength)) {
|
|
154
|
+
return invalidRequestJson(path, `"${path}" must contain at least ${schema.minLength} character(s).`);
|
|
155
|
+
}
|
|
156
|
+
if (schema.type === "string" && schema.maxLength !== undefined && schema.maxLength !== null
|
|
157
|
+
&& value.length > Number(schema.maxLength)) {
|
|
158
|
+
return invalidRequestJson(path, `"${path}" must contain at most ${schema.maxLength} character(s).`);
|
|
159
|
+
}
|
|
160
|
+
return { ok: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function nestedSchema(property) {
|
|
164
|
+
if (!property.schema) {
|
|
165
|
+
return property;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
...property.schema,
|
|
169
|
+
nullable: property.nullable ?? property.schema.nullable,
|
|
170
|
+
discriminator: property.discriminator ?? property.schema.discriminator,
|
|
171
|
+
variants: property.variants ?? property.schema.variants,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function joinPath(base, fieldName) {
|
|
176
|
+
return base ? `${base}.${fieldName}` : fieldName;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isPlainObject(value) {
|
|
180
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function invalidRequestJson(path, message) {
|
|
184
|
+
return {
|
|
185
|
+
ok: false,
|
|
186
|
+
error: cliError("INVALID_REQUEST_JSON", {
|
|
187
|
+
path,
|
|
188
|
+
message,
|
|
189
|
+
hint: "Remove unsupported fields or check the command schema.",
|
|
190
|
+
retryable: false,
|
|
191
|
+
nextActions: ["finebi-cli --help"],
|
|
192
|
+
}),
|
|
193
|
+
};
|
|
194
|
+
}
|