@boltic/cli 1.0.6 → 1.0.8
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 +0 -9
- package/commands/integration.js +7 -256
- package/helper/validation.js +89 -23
- package/package.json +1 -1
- package/templates/component-schemas.js +41 -1
package/README.md
CHANGED
|
@@ -519,15 +519,6 @@ npm install -g @boltic/cli@latest
|
|
|
519
519
|
boltic --version
|
|
520
520
|
```
|
|
521
521
|
|
|
522
|
-
**Development mode troubleshooting**:
|
|
523
|
-
|
|
524
|
-
```bash
|
|
525
|
-
# For contributors using development installation
|
|
526
|
-
npm install # Update dependencies
|
|
527
|
-
npm link # Re-link CLI
|
|
528
|
-
npm test # Run test suite
|
|
529
|
-
```
|
|
530
|
-
|
|
531
522
|
#### 9. Emergency Recovery
|
|
532
523
|
|
|
533
524
|
**If all else fails**:
|
package/commands/integration.js
CHANGED
|
@@ -54,10 +54,6 @@ const commands = {
|
|
|
54
54
|
description: "Show detailed information about an integration",
|
|
55
55
|
action: handleStatus,
|
|
56
56
|
},
|
|
57
|
-
test: {
|
|
58
|
-
description: "Run tests for the integration with coverage",
|
|
59
|
-
action: handleTest,
|
|
60
|
-
},
|
|
61
57
|
help: {
|
|
62
58
|
description: "Show help for integration commands",
|
|
63
59
|
action: showHelp,
|
|
@@ -1064,6 +1060,13 @@ async function handleStatus() {
|
|
|
1064
1060
|
console.log(integration.documentation);
|
|
1065
1061
|
}
|
|
1066
1062
|
} catch (error) {
|
|
1063
|
+
if (
|
|
1064
|
+
error.message &&
|
|
1065
|
+
error.message.includes("User force closed the prompt")
|
|
1066
|
+
) {
|
|
1067
|
+
console.log(chalk.yellow("\n⚠️ Operation cancelled by user"));
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1067
1070
|
console.error(
|
|
1068
1071
|
chalk.red("\n❌ Error fetching integration status:"),
|
|
1069
1072
|
error.message || "Unknown error"
|
|
@@ -1071,258 +1074,6 @@ async function handleStatus() {
|
|
|
1071
1074
|
}
|
|
1072
1075
|
}
|
|
1073
1076
|
|
|
1074
|
-
async function handleTest(args) {
|
|
1075
|
-
// Parse command line arguments
|
|
1076
|
-
let currentDir = process.cwd();
|
|
1077
|
-
let jestConfigFile = null;
|
|
1078
|
-
const pathIndex = args.indexOf("--path");
|
|
1079
|
-
const configIndex = args.indexOf("--config");
|
|
1080
|
-
|
|
1081
|
-
if (pathIndex !== -1 && args[pathIndex + 1]) {
|
|
1082
|
-
currentDir = args[pathIndex + 1];
|
|
1083
|
-
// Validate the provided path
|
|
1084
|
-
if (!fs.existsSync(currentDir)) {
|
|
1085
|
-
console.error(
|
|
1086
|
-
chalk.red(
|
|
1087
|
-
`Error: The specified path does not exist: ${currentDir}`
|
|
1088
|
-
)
|
|
1089
|
-
);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
1095
|
-
jestConfigFile = args[configIndex + 1];
|
|
1096
|
-
// Validate the config file path
|
|
1097
|
-
const configPath = path.isAbsolute(jestConfigFile)
|
|
1098
|
-
? jestConfigFile
|
|
1099
|
-
: path.join(currentDir, jestConfigFile);
|
|
1100
|
-
|
|
1101
|
-
if (!fs.existsSync(configPath)) {
|
|
1102
|
-
console.error(
|
|
1103
|
-
chalk.red(
|
|
1104
|
-
`Error: Jest config file does not exist: ${configPath}`
|
|
1105
|
-
)
|
|
1106
|
-
);
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
const { spawn } = await import("child_process");
|
|
1112
|
-
|
|
1113
|
-
console.log(chalk.cyan.bold("\n🧪 Running integration tests...\n"));
|
|
1114
|
-
|
|
1115
|
-
// Look for test directory and test files
|
|
1116
|
-
const testDirs = ["test", "tests", "__tests__"];
|
|
1117
|
-
let testDir = null;
|
|
1118
|
-
let testFiles = [];
|
|
1119
|
-
|
|
1120
|
-
// Check for test directories
|
|
1121
|
-
for (const dir of testDirs) {
|
|
1122
|
-
const testPath = path.join(currentDir, dir);
|
|
1123
|
-
if (fs.existsSync(testPath)) {
|
|
1124
|
-
testDir = dir;
|
|
1125
|
-
// Find all test files in the directory
|
|
1126
|
-
const files = fs.readdirSync(testPath);
|
|
1127
|
-
testFiles = files.filter(
|
|
1128
|
-
(file) =>
|
|
1129
|
-
file.endsWith(".test.js") ||
|
|
1130
|
-
file.endsWith(".spec.js") ||
|
|
1131
|
-
file.endsWith(".test.ts") ||
|
|
1132
|
-
file.endsWith(".spec.ts")
|
|
1133
|
-
);
|
|
1134
|
-
break;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
// If no test directory found, look for test files in current directory
|
|
1139
|
-
if (!testDir) {
|
|
1140
|
-
const currentDirFiles = fs.readdirSync(currentDir);
|
|
1141
|
-
testFiles = currentDirFiles.filter(
|
|
1142
|
-
(file) =>
|
|
1143
|
-
file.endsWith(".test.js") ||
|
|
1144
|
-
file.endsWith(".spec.js") ||
|
|
1145
|
-
file.endsWith(".test.ts") ||
|
|
1146
|
-
file.endsWith(".spec.ts")
|
|
1147
|
-
);
|
|
1148
|
-
|
|
1149
|
-
if (testFiles.length === 0) {
|
|
1150
|
-
console.log(
|
|
1151
|
-
chalk.yellow(
|
|
1152
|
-
"⚠️ No test files found. Looked for: test, tests, __tests__ directories and *.test.js, *.spec.js, *.test.ts, *.spec.ts files"
|
|
1153
|
-
)
|
|
1154
|
-
);
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
if (testFiles.length === 0 && testDir) {
|
|
1160
|
-
console.log(
|
|
1161
|
-
chalk.yellow(
|
|
1162
|
-
`⚠️ No test files found in ${testDir} directory. Looking for: *.test.js, *.spec.js, *.test.ts, *.spec.ts files`
|
|
1163
|
-
)
|
|
1164
|
-
);
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
console.log(chalk.dim(`📁 Found test directory: ${testDir || currentDir}`));
|
|
1169
|
-
console.log(
|
|
1170
|
-
chalk.dim(
|
|
1171
|
-
`📋 Found ${testFiles.length} test file(s): ${testFiles.join(", ")}`
|
|
1172
|
-
)
|
|
1173
|
-
);
|
|
1174
|
-
|
|
1175
|
-
// Check for Jest configuration
|
|
1176
|
-
let jestConfigPath;
|
|
1177
|
-
if (jestConfigFile) {
|
|
1178
|
-
jestConfigPath = path.isAbsolute(jestConfigFile)
|
|
1179
|
-
? jestConfigFile
|
|
1180
|
-
: path.join(currentDir, jestConfigFile);
|
|
1181
|
-
} else {
|
|
1182
|
-
// Look for default config files
|
|
1183
|
-
const defaultConfigs = [
|
|
1184
|
-
"jest.config.cjs",
|
|
1185
|
-
"jest.config.js",
|
|
1186
|
-
"jest.config.json",
|
|
1187
|
-
];
|
|
1188
|
-
for (const configFile of defaultConfigs) {
|
|
1189
|
-
const configPath = path.join(currentDir, configFile);
|
|
1190
|
-
if (fs.existsSync(configPath)) {
|
|
1191
|
-
jestConfigPath = configPath;
|
|
1192
|
-
break;
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
const jestConfigExists = !!jestConfigPath;
|
|
1198
|
-
const configFileName = jestConfigPath
|
|
1199
|
-
? path.basename(jestConfigPath)
|
|
1200
|
-
: null;
|
|
1201
|
-
|
|
1202
|
-
console.log(
|
|
1203
|
-
chalk.dim(
|
|
1204
|
-
`⚙️ Jest configuration: ${jestConfigExists ? `Found ${configFileName}` : "Using default configuration"}`
|
|
1205
|
-
)
|
|
1206
|
-
);
|
|
1207
|
-
|
|
1208
|
-
// Prepare Jest arguments
|
|
1209
|
-
const jestArgs = ["jest"];
|
|
1210
|
-
|
|
1211
|
-
// Add basic Jest flags
|
|
1212
|
-
jestArgs.push("--coverage");
|
|
1213
|
-
jestArgs.push("--verbose");
|
|
1214
|
-
jestArgs.push("--colors");
|
|
1215
|
-
jestArgs.push("--passWithNoTests");
|
|
1216
|
-
|
|
1217
|
-
// Add config file if exists, otherwise use simple patterns
|
|
1218
|
-
if (jestConfigExists) {
|
|
1219
|
-
jestArgs.push("--config", jestConfigPath);
|
|
1220
|
-
} else {
|
|
1221
|
-
// Use simple test matching patterns
|
|
1222
|
-
if (testDir) {
|
|
1223
|
-
jestArgs.push(
|
|
1224
|
-
"--testMatch",
|
|
1225
|
-
`**/${testDir}/**/*.{test,spec}.{js,ts}`
|
|
1226
|
-
);
|
|
1227
|
-
} else {
|
|
1228
|
-
jestArgs.push("--testMatch", "**/*.{test,spec}.{js,ts}");
|
|
1229
|
-
}
|
|
1230
|
-
jestArgs.push("--collectCoverageFrom", "lib/**/*.{js,ts}");
|
|
1231
|
-
jestArgs.push("--collectCoverageFrom", "src/**/*.{js,ts}");
|
|
1232
|
-
jestArgs.push("--collectCoverageFrom", "*.{js,ts}");
|
|
1233
|
-
jestArgs.push("--coveragePathIgnorePatterns", "/node_modules/");
|
|
1234
|
-
jestArgs.push("--coveragePathIgnorePatterns", "/tests/");
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
// Add any additional Jest arguments passed by user
|
|
1238
|
-
const additionalArgs = args.filter((arg, index) => {
|
|
1239
|
-
// Skip our custom flags and their values
|
|
1240
|
-
if (arg === "--path" || arg === "--config") return false;
|
|
1241
|
-
if (args[index - 1] === "--path" || args[index - 1] === "--config")
|
|
1242
|
-
return false;
|
|
1243
|
-
return true;
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
|
-
if (additionalArgs.length > 0) {
|
|
1247
|
-
jestArgs.push(...additionalArgs);
|
|
1248
|
-
console.log(
|
|
1249
|
-
chalk.dim(
|
|
1250
|
-
`🔧 Additional Jest arguments: ${additionalArgs.join(" ")}`
|
|
1251
|
-
)
|
|
1252
|
-
);
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
console.log(chalk.dim(`🚀 Running command: npx ${jestArgs.join(" ")}`));
|
|
1256
|
-
console.log(chalk.cyan("─".repeat(50)));
|
|
1257
|
-
|
|
1258
|
-
// Run Jest with coverage
|
|
1259
|
-
return new Promise((resolve, reject) => {
|
|
1260
|
-
const jestProcess = spawn("npx", jestArgs, {
|
|
1261
|
-
stdio: "inherit",
|
|
1262
|
-
shell: true,
|
|
1263
|
-
cwd: currentDir,
|
|
1264
|
-
});
|
|
1265
|
-
|
|
1266
|
-
jestProcess.on("close", (code) => {
|
|
1267
|
-
console.log(chalk.cyan("─".repeat(50)));
|
|
1268
|
-
|
|
1269
|
-
if (code === 0) {
|
|
1270
|
-
console.log(chalk.green.bold("\n✅ All tests passed!"));
|
|
1271
|
-
|
|
1272
|
-
// Check for coverage directory
|
|
1273
|
-
const coverageDir = path.join(currentDir, "coverage");
|
|
1274
|
-
if (fs.existsSync(coverageDir)) {
|
|
1275
|
-
console.log(chalk.cyan("\n📊 Coverage Report Generated:"));
|
|
1276
|
-
console.log(chalk.dim(` Directory: ${coverageDir}`));
|
|
1277
|
-
|
|
1278
|
-
// Check for HTML report
|
|
1279
|
-
const htmlReportPath = path.join(
|
|
1280
|
-
coverageDir,
|
|
1281
|
-
"lcov-report",
|
|
1282
|
-
"index.html"
|
|
1283
|
-
);
|
|
1284
|
-
if (fs.existsSync(htmlReportPath)) {
|
|
1285
|
-
console.log(
|
|
1286
|
-
chalk.dim(` HTML Report: ${htmlReportPath}`)
|
|
1287
|
-
);
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
// Check for text report
|
|
1291
|
-
const textReportPath = path.join(coverageDir, "lcov.info");
|
|
1292
|
-
if (fs.existsSync(textReportPath)) {
|
|
1293
|
-
console.log(
|
|
1294
|
-
chalk.dim(` LCOV Report: ${textReportPath}`)
|
|
1295
|
-
);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
// Show test summary
|
|
1300
|
-
console.log(chalk.cyan("\n📋 Test Summary:"));
|
|
1301
|
-
console.log(chalk.dim(` Test files: ${testFiles.length}`));
|
|
1302
|
-
console.log(
|
|
1303
|
-
chalk.dim(` Test directory: ${testDir || currentDir}`)
|
|
1304
|
-
);
|
|
1305
|
-
console.log(chalk.green(" Status: PASSED"));
|
|
1306
|
-
} else {
|
|
1307
|
-
console.log(chalk.red.bold("\n❌ Some tests failed."));
|
|
1308
|
-
console.log(chalk.red(` Exit code: ${code}`));
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
console.log(
|
|
1312
|
-
chalk.cyan(
|
|
1313
|
-
"\n💡 Tip: Check the coverage directory for detailed coverage reports"
|
|
1314
|
-
)
|
|
1315
|
-
);
|
|
1316
|
-
resolve(code);
|
|
1317
|
-
});
|
|
1318
|
-
|
|
1319
|
-
jestProcess.on("error", (error) => {
|
|
1320
|
-
console.error(chalk.red("❌ Error running tests:"), error.message);
|
|
1321
|
-
reject(error);
|
|
1322
|
-
});
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
1077
|
export default {
|
|
1327
1078
|
execute,
|
|
1328
1079
|
};
|
package/helper/validation.js
CHANGED
|
@@ -142,21 +142,42 @@ const validateSchemaKeys = (
|
|
|
142
142
|
schemaName,
|
|
143
143
|
displayType,
|
|
144
144
|
errors,
|
|
145
|
-
prefix = ""
|
|
145
|
+
prefix = "",
|
|
146
|
+
filename = ""
|
|
146
147
|
) => {
|
|
148
|
+
const fileLabel = filename ? ` in "${filename}"` : "";
|
|
149
|
+
|
|
147
150
|
Object.keys(schemaObj).forEach((key) => {
|
|
148
151
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
149
152
|
|
|
153
|
+
// Special handling for config.body - skip validation for select, autocomplete, and multiselect
|
|
154
|
+
if (
|
|
155
|
+
fullKey === "config.body" &&
|
|
156
|
+
(displayType === "select" ||
|
|
157
|
+
displayType === "autocomplete" ||
|
|
158
|
+
displayType === "multiselect")
|
|
159
|
+
) {
|
|
160
|
+
// For these display types, config.body can contain any keys, so we skip validation
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
150
164
|
if (!allowedKeys.has(fullKey)) {
|
|
151
165
|
errors.add(
|
|
152
|
-
`"${schemaName}" has an invalid key "${fullKey}" for displayType "${displayType}".`
|
|
166
|
+
`"${schemaName}" has an invalid key "${fullKey}" for displayType "${displayType}"${fileLabel}.`
|
|
153
167
|
);
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
if (
|
|
157
171
|
typeof schemaObj[key] === "object" &&
|
|
158
172
|
schemaObj[key] !== null &&
|
|
159
|
-
!Array.isArray(schemaObj[key])
|
|
173
|
+
!Array.isArray(schemaObj[key]) &&
|
|
174
|
+
// Skip recursion for config.body of select/autocomplete/multiselect
|
|
175
|
+
!(
|
|
176
|
+
fullKey === "config.body" &&
|
|
177
|
+
(displayType === "select" ||
|
|
178
|
+
displayType === "autocomplete" ||
|
|
179
|
+
displayType === "multiselect")
|
|
180
|
+
)
|
|
160
181
|
) {
|
|
161
182
|
validateSchemaKeys(
|
|
162
183
|
schemaObj[key],
|
|
@@ -164,7 +185,8 @@ const validateSchemaKeys = (
|
|
|
164
185
|
schemaName,
|
|
165
186
|
displayType,
|
|
166
187
|
errors,
|
|
167
|
-
fullKey
|
|
188
|
+
fullKey,
|
|
189
|
+
filename
|
|
168
190
|
);
|
|
169
191
|
}
|
|
170
192
|
});
|
|
@@ -175,21 +197,29 @@ const validateSchemaKeys = (
|
|
|
175
197
|
* @param {Object} schema - The schema to validate
|
|
176
198
|
* @param {string} displayType - The display type to validate against
|
|
177
199
|
* @param {Set} errors - Error collection
|
|
200
|
+
* @param {string} filename - The filename for error messages
|
|
178
201
|
*/
|
|
179
|
-
const validateComponentByType = (
|
|
202
|
+
const validateComponentByType = (
|
|
203
|
+
schema,
|
|
204
|
+
displayType,
|
|
205
|
+
errors,
|
|
206
|
+
filename = ""
|
|
207
|
+
) => {
|
|
208
|
+
const fileLabel = filename ? ` in "${filename}"` : "";
|
|
209
|
+
|
|
180
210
|
// Get the component schema definition for this display type
|
|
181
211
|
const componentSchema = componentSchemas[displayType];
|
|
182
212
|
|
|
183
213
|
if (!componentSchema) {
|
|
184
214
|
errors.add(
|
|
185
|
-
`"${schema.name}" has an unsupported displayType "${displayType}".`
|
|
215
|
+
`"${schema.name}" has an unsupported displayType "${displayType}"${fileLabel}.`
|
|
186
216
|
);
|
|
187
217
|
return;
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
if (!componentSchema.meta) {
|
|
191
221
|
errors.add(
|
|
192
|
-
`Component schema for "${displayType}" is missing meta definition.`
|
|
222
|
+
`Component schema for "${displayType}" is missing meta definition${fileLabel}.`
|
|
193
223
|
);
|
|
194
224
|
return;
|
|
195
225
|
}
|
|
@@ -204,29 +234,39 @@ const validateComponentByType = (schema, displayType, errors) => {
|
|
|
204
234
|
allowedKeys,
|
|
205
235
|
schema.name,
|
|
206
236
|
currentDisplayType,
|
|
207
|
-
errors
|
|
237
|
+
errors,
|
|
238
|
+
"",
|
|
239
|
+
filename
|
|
208
240
|
);
|
|
209
241
|
};
|
|
210
242
|
|
|
211
|
-
const validateComponentSchemas = (schemas, errors) => {
|
|
243
|
+
const validateComponentSchemas = (schemas, errors, filename = "") => {
|
|
244
|
+
const fileLabel = filename ? ` in "${filename}"` : "";
|
|
245
|
+
|
|
212
246
|
schemas.forEach((schema) => {
|
|
213
247
|
// Basic required field validation
|
|
214
248
|
if (!schema.name) {
|
|
215
|
-
errors.add(`Schema is missing a name.`);
|
|
249
|
+
errors.add(`Schema is missing a name${fileLabel}.`);
|
|
216
250
|
return; // Can't continue without a name
|
|
217
251
|
}
|
|
218
252
|
if (!schema.meta) {
|
|
219
|
-
errors.add(
|
|
253
|
+
errors.add(
|
|
254
|
+
`"${schema.name}" is missing a meta object${fileLabel}.`
|
|
255
|
+
);
|
|
220
256
|
return; // Can't continue without meta
|
|
221
257
|
}
|
|
222
258
|
if (!schema.meta.displayType) {
|
|
223
|
-
errors.add(
|
|
259
|
+
errors.add(
|
|
260
|
+
`"${schema.name}" is missing a displayType${fileLabel}.`
|
|
261
|
+
);
|
|
224
262
|
return; // Can't continue without displayType
|
|
225
263
|
}
|
|
226
264
|
|
|
227
265
|
// Optional field validation (these are warnings, not blocking)
|
|
228
266
|
if (!schema.meta.displayName) {
|
|
229
|
-
errors.add(
|
|
267
|
+
errors.add(
|
|
268
|
+
`"${schema.name}" is missing a displayName${fileLabel}.`
|
|
269
|
+
);
|
|
230
270
|
}
|
|
231
271
|
|
|
232
272
|
// Only require placeholder if the component schema defines it
|
|
@@ -237,7 +277,9 @@ const validateComponentSchemas = (schemas, errors) => {
|
|
|
237
277
|
"placeholder" in componentSchema.meta &&
|
|
238
278
|
!schema.meta.placeholder
|
|
239
279
|
) {
|
|
240
|
-
errors.add(
|
|
280
|
+
errors.add(
|
|
281
|
+
`"${schema.name}" is missing a placeholder${fileLabel}.`
|
|
282
|
+
);
|
|
241
283
|
}
|
|
242
284
|
|
|
243
285
|
// Only require description if the component schema defines it
|
|
@@ -247,7 +289,9 @@ const validateComponentSchemas = (schemas, errors) => {
|
|
|
247
289
|
"description" in componentSchema.meta &&
|
|
248
290
|
!schema.meta.description
|
|
249
291
|
) {
|
|
250
|
-
errors.add(
|
|
292
|
+
errors.add(
|
|
293
|
+
`"${schema.name}" is missing a description${fileLabel}.`
|
|
294
|
+
);
|
|
251
295
|
}
|
|
252
296
|
|
|
253
297
|
// 🚨 Validate for duplicate options with same label and value
|
|
@@ -258,7 +302,7 @@ const validateComponentSchemas = (schemas, errors) => {
|
|
|
258
302
|
const key = `${option.label}::${option.value}`;
|
|
259
303
|
if (seen.has(key)) {
|
|
260
304
|
errors.add(
|
|
261
|
-
`"${schema.name}" contains duplicate option at index ${index} with label "${option.label}" and value "${option.value}".`
|
|
305
|
+
`"${schema.name}" contains duplicate option at index ${index} with label "${option.label}" and value "${option.value}"${fileLabel}.`
|
|
262
306
|
);
|
|
263
307
|
} else {
|
|
264
308
|
seen.add(key);
|
|
@@ -268,7 +312,12 @@ const validateComponentSchemas = (schemas, errors) => {
|
|
|
268
312
|
}
|
|
269
313
|
|
|
270
314
|
// Validate against the specific component type schema
|
|
271
|
-
validateComponentByType(
|
|
315
|
+
validateComponentByType(
|
|
316
|
+
schema,
|
|
317
|
+
schema.meta.displayType,
|
|
318
|
+
errors,
|
|
319
|
+
filename
|
|
320
|
+
);
|
|
272
321
|
});
|
|
273
322
|
};
|
|
274
323
|
|
|
@@ -314,7 +363,11 @@ const validateWebhook = (webhookPath, spec, errors) => {
|
|
|
314
363
|
errors
|
|
315
364
|
);
|
|
316
365
|
if (webhookSchema && Array.isArray(webhookSchema.parameters)) {
|
|
317
|
-
validateComponentSchemas(
|
|
366
|
+
validateComponentSchemas(
|
|
367
|
+
webhookSchema.parameters,
|
|
368
|
+
errors,
|
|
369
|
+
"webhook.json"
|
|
370
|
+
);
|
|
318
371
|
}
|
|
319
372
|
}
|
|
320
373
|
};
|
|
@@ -329,7 +382,7 @@ const validateBaseSchema = (baseSchemaPath, errors) => {
|
|
|
329
382
|
|
|
330
383
|
// Validate base schema parameters
|
|
331
384
|
if (baseSchema && Array.isArray(baseSchema.parameters)) {
|
|
332
|
-
validateComponentSchemas(baseSchema.parameters, errors);
|
|
385
|
+
validateComponentSchemas(baseSchema.parameters, errors, "base.json");
|
|
333
386
|
}
|
|
334
387
|
|
|
335
388
|
return baseSchema;
|
|
@@ -346,7 +399,11 @@ const validateAuthentication = (authPath, errors) => {
|
|
|
346
399
|
|
|
347
400
|
// Validate authentication schema parameters
|
|
348
401
|
if (authSchema && Array.isArray(authSchema.parameters)) {
|
|
349
|
-
validateComponentSchemas(
|
|
402
|
+
validateComponentSchemas(
|
|
403
|
+
authSchema.parameters,
|
|
404
|
+
errors,
|
|
405
|
+
"authentication.json"
|
|
406
|
+
);
|
|
350
407
|
}
|
|
351
408
|
|
|
352
409
|
// Validate authentication type-specific parameters (like api_key, oauth, etc.)
|
|
@@ -360,7 +417,8 @@ const validateAuthentication = (authPath, errors) => {
|
|
|
360
417
|
if (Array.isArray(authSchema[key].parameters)) {
|
|
361
418
|
validateComponentSchemas(
|
|
362
419
|
authSchema[key].parameters,
|
|
363
|
-
errors
|
|
420
|
+
errors,
|
|
421
|
+
"authentication.json"
|
|
364
422
|
);
|
|
365
423
|
}
|
|
366
424
|
}
|
|
@@ -399,7 +457,11 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
|
399
457
|
|
|
400
458
|
// Validate resource file parameters
|
|
401
459
|
if (Array.isArray(schema.parameters)) {
|
|
402
|
-
validateComponentSchemas(
|
|
460
|
+
validateComponentSchemas(
|
|
461
|
+
schema.parameters,
|
|
462
|
+
errors,
|
|
463
|
+
`${resourceFile}.json`
|
|
464
|
+
);
|
|
403
465
|
}
|
|
404
466
|
|
|
405
467
|
const operationFields = findOperationFieldsWithOptions(
|
|
@@ -433,7 +495,11 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
|
433
495
|
} else {
|
|
434
496
|
// Validate operation parameters using component schemas
|
|
435
497
|
if (Array.isArray(methodDef.parameters)) {
|
|
436
|
-
validateComponentSchemas(
|
|
498
|
+
validateComponentSchemas(
|
|
499
|
+
methodDef.parameters,
|
|
500
|
+
errors,
|
|
501
|
+
`${resourceFile}.json`
|
|
502
|
+
);
|
|
437
503
|
}
|
|
438
504
|
}
|
|
439
505
|
if (!methodDef.definition) {
|
package/package.json
CHANGED
|
@@ -110,7 +110,12 @@ const select = {
|
|
|
110
110
|
url: "/api/options",
|
|
111
111
|
labelKey: "label",
|
|
112
112
|
valueKey: "value",
|
|
113
|
-
body: {
|
|
113
|
+
body: {
|
|
114
|
+
secret: "secret",
|
|
115
|
+
loadOptionsMethod: "get",
|
|
116
|
+
resource: "resource",
|
|
117
|
+
operation: "operation",
|
|
118
|
+
},
|
|
114
119
|
},
|
|
115
120
|
displayProps: { loading: false },
|
|
116
121
|
validation: {
|
|
@@ -118,6 +123,17 @@ const select = {
|
|
|
118
123
|
requiredDetail: {
|
|
119
124
|
errorMsg: "Selection is required",
|
|
120
125
|
},
|
|
126
|
+
max: 10,
|
|
127
|
+
maxDetail: {
|
|
128
|
+
errorMsg: "Too many options",
|
|
129
|
+
},
|
|
130
|
+
min: 1,
|
|
131
|
+
minDetail: {
|
|
132
|
+
errorMsg: "At least one option is required",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
htmlProps: {
|
|
136
|
+
allowDynamic: false,
|
|
121
137
|
},
|
|
122
138
|
dependencies: {
|
|
123
139
|
logic: "AND",
|
|
@@ -898,6 +914,29 @@ const button = {
|
|
|
898
914
|
},
|
|
899
915
|
};
|
|
900
916
|
|
|
917
|
+
const datetime = {
|
|
918
|
+
name: "datetime-picker",
|
|
919
|
+
meta: {
|
|
920
|
+
displayName: "Enter Date with Start Time",
|
|
921
|
+
description: "Please enter date and start time",
|
|
922
|
+
displayType: "datetime",
|
|
923
|
+
value: "2024-02-14",
|
|
924
|
+
validation: {
|
|
925
|
+
required: false,
|
|
926
|
+
},
|
|
927
|
+
readOnly: false,
|
|
928
|
+
htmlProps: {
|
|
929
|
+
format: "YYYY-MM-DD HH:mm:ss",
|
|
930
|
+
disabled: false,
|
|
931
|
+
timeSteps: {
|
|
932
|
+
hours: 1,
|
|
933
|
+
minutes: 5,
|
|
934
|
+
seconds: 5,
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
};
|
|
939
|
+
|
|
901
940
|
export {
|
|
902
941
|
accordion,
|
|
903
942
|
array,
|
|
@@ -906,6 +945,7 @@ export {
|
|
|
906
945
|
checkbox,
|
|
907
946
|
code,
|
|
908
947
|
date,
|
|
948
|
+
datetime,
|
|
909
949
|
divider,
|
|
910
950
|
email,
|
|
911
951
|
file,
|