adfinem 0.0.0 → 0.1.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/CHANGELOG.md +10 -0
- package/CODE_OF_CONDUCT.md +21 -0
- package/CONTRIBUTING.md +29 -0
- package/LICENSE +21 -0
- package/README.md +86 -2
- package/SECURITY.md +13 -0
- package/catalogs/.gitkeep +0 -0
- package/catalogs/api-operations.yaml +21 -0
- package/catalogs/batches.yaml +74 -0
- package/catalogs/queries.yaml +75 -0
- package/config/environments.yaml +13 -0
- package/dist/actions/assert-db.js +3 -0
- package/dist/actions/run-eod.js +3 -0
- package/dist/adapters/api/api-collections.js +296 -0
- package/dist/adapters/api/body-utils.js +9 -0
- package/dist/adapters/api/rest-client.js +557 -0
- package/dist/adapters/api/soap-client.js +5 -0
- package/dist/adapters/db/assertions.js +87 -0
- package/dist/adapters/db/oracle-client.js +115 -0
- package/dist/adapters/db/query-catalog.js +75 -0
- package/dist/adapters/unix/batch-catalog.js +71 -0
- package/dist/adapters/unix/batch-input-files.js +36 -0
- package/dist/adapters/unix/batch-runner.js +382 -0
- package/dist/adapters/unix/ssh-client.js +228 -0
- package/dist/app/server.js +826 -0
- package/dist/cli.js +465 -0
- package/dist/config/environments.js +138 -0
- package/dist/config/registry.js +18 -0
- package/dist/config/secrets.js +123 -0
- package/dist/dsl/parser.js +20 -0
- package/dist/dsl/schema.js +182 -0
- package/dist/dsl/types.js +1 -0
- package/dist/dsl/validator.js +264 -0
- package/dist/engine/captures.js +68 -0
- package/dist/engine/context.js +69 -0
- package/dist/engine/evidence.js +33 -0
- package/dist/engine/known-errors.js +129 -0
- package/dist/engine/retry.js +13 -0
- package/dist/engine/runner.js +710 -0
- package/dist/engine/step-result.js +58 -0
- package/dist/flows/catalog-normalizer.js +72 -0
- package/dist/flows/compiler.js +237 -0
- package/dist/flows/concat.js +130 -0
- package/dist/flows/parser.js +21 -0
- package/dist/flows/schema.js +142 -0
- package/dist/flows/types.js +1 -0
- package/dist/flows/validator.js +470 -0
- package/dist/reports/html-report.js +112 -0
- package/dist/reports/junit-report.js +48 -0
- package/docs/.gitkeep +0 -0
- package/docs/DB_UNIX_OPERATIONS.md +118 -0
- package/docs/FLOW_BUILDER.md +87 -0
- package/flows/account_processing_cycle.flow.yaml +88 -0
- package/flows/new_flow.flow.yaml +22 -0
- package/package.json +92 -7
- package/scenarios/smoke/account-processing-smoke.yaml +44 -0
- package/scenarios/smoke/api-db-batch-check.yaml +40 -0
- package/src/actions/assert-db.ts +6 -0
- package/src/actions/run-eod.ts +6 -0
- package/src/adapters/api/api-collections.ts +375 -0
- package/src/adapters/api/body-utils.ts +10 -0
- package/src/adapters/api/rest-client.ts +587 -0
- package/src/adapters/api/soap-client.ts +7 -0
- package/src/adapters/db/assertions.ts +83 -0
- package/src/adapters/db/oracle-client.ts +133 -0
- package/src/adapters/db/query-catalog.ts +80 -0
- package/src/adapters/unix/batch-catalog.ts +81 -0
- package/src/adapters/unix/batch-input-files.ts +39 -0
- package/src/adapters/unix/batch-runner.ts +456 -0
- package/src/adapters/unix/ssh-client.ts +248 -0
- package/src/app/server.ts +913 -0
- package/src/cli.ts +466 -0
- package/src/config/environments.ts +193 -0
- package/src/config/registry.ts +23 -0
- package/src/config/secrets.ts +128 -0
- package/src/dsl/parser.ts +24 -0
- package/src/dsl/schema.ts +189 -0
- package/src/dsl/types.ts +371 -0
- package/src/dsl/validator.ts +282 -0
- package/src/engine/captures.ts +66 -0
- package/src/engine/context.ts +76 -0
- package/src/engine/evidence.ts +35 -0
- package/src/engine/known-errors.ts +145 -0
- package/src/engine/retry.ts +11 -0
- package/src/engine/runner.ts +746 -0
- package/src/engine/step-result.ts +64 -0
- package/src/flows/catalog-normalizer.ts +86 -0
- package/src/flows/compiler.ts +247 -0
- package/src/flows/concat.ts +149 -0
- package/src/flows/parser.ts +27 -0
- package/src/flows/schema.ts +154 -0
- package/src/flows/types.ts +130 -0
- package/src/flows/validator.ts +468 -0
- package/src/llm/system-prompt.md +9 -0
- package/src/reports/html-report.ts +113 -0
- package/src/reports/junit-report.ts +55 -0
- package/src/types/oracledb.d.ts +1 -0
- package/templates/.gitkeep +0 -0
- package/templates/api/create-test-case.json +5 -0
- package/templates/api/record-test-activity.json +6 -0
- package/tsconfig.json +15 -0
- package/vite.config.ts +17 -0
- package/web/index.html +12 -0
- package/web/src/App.tsx +6588 -0
- package/web/src/main.tsx +10 -0
- package/web/src/styles.css +3147 -0
- package/index.js +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { EvidenceVisibilityMode } from "../dsl/types.js";
|
|
2
|
+
|
|
3
|
+
const secretKeyFragments = [
|
|
4
|
+
"password",
|
|
5
|
+
"passwd",
|
|
6
|
+
"pwd",
|
|
7
|
+
"token",
|
|
8
|
+
"authorization",
|
|
9
|
+
"apikey",
|
|
10
|
+
"api_key",
|
|
11
|
+
"privatekey",
|
|
12
|
+
"private_key",
|
|
13
|
+
"jwt",
|
|
14
|
+
"secret",
|
|
15
|
+
"credential",
|
|
16
|
+
"bearer",
|
|
17
|
+
"access_token",
|
|
18
|
+
"refreshtoken",
|
|
19
|
+
"refresh_token",
|
|
20
|
+
"servicepassword",
|
|
21
|
+
"apikey",
|
|
22
|
+
"session",
|
|
23
|
+
"cookie",
|
|
24
|
+
"pin",
|
|
25
|
+
"cvv",
|
|
26
|
+
"cvc",
|
|
27
|
+
"cvv2"
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const cardKeyExact = new Set([
|
|
31
|
+
"pan",
|
|
32
|
+
"pans",
|
|
33
|
+
"cardnumber",
|
|
34
|
+
"cardnumbers",
|
|
35
|
+
"cardno",
|
|
36
|
+
"primaryaccountnumber",
|
|
37
|
+
"primaryaccountnumbers"
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const REDACTED = "<redacted>";
|
|
41
|
+
|
|
42
|
+
export function redactSecrets<T>(value: T): T {
|
|
43
|
+
return redact(value) as T;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function evidenceVisibilityMode(): EvidenceVisibilityMode {
|
|
47
|
+
return process.env.ADFINEM_EVIDENCE_VISIBILITY?.toLowerCase() === "redacted" ? "redacted" : "raw";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function applyEvidenceVisibility<T>(value: T, mode: EvidenceVisibilityMode = evidenceVisibilityMode()): T {
|
|
51
|
+
return mode === "redacted" ? redactSecrets(value) : value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Heavier redaction intended for shared evidence artifacts.
|
|
56
|
+
* Combines key-based redaction with inline PAN-pattern scrubbing on every string.
|
|
57
|
+
*/
|
|
58
|
+
export function redactEvidence<T>(value: T): T {
|
|
59
|
+
return redactDeep(value, true) as T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function maskInlinePans(text: string): string {
|
|
63
|
+
if (!text) return text;
|
|
64
|
+
return text.replace(/(?<!\d)(\d[\d\s.-]{10,21}\d)(?!\d)/g, (match) => {
|
|
65
|
+
const digits = match.replace(/\D+/g, "");
|
|
66
|
+
if (digits.length < 12 || digits.length > 19) return match;
|
|
67
|
+
if (!isLuhnValid(digits)) return match;
|
|
68
|
+
return `****${digits.slice(-4)}`;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isLuhnValid(digits: string): boolean {
|
|
73
|
+
if (!/^\d+$/.test(digits)) return false;
|
|
74
|
+
let sum = 0;
|
|
75
|
+
let alt = false;
|
|
76
|
+
for (let index = digits.length - 1; index >= 0; index--) {
|
|
77
|
+
let n = digits.charCodeAt(index) - 48;
|
|
78
|
+
if (alt) {
|
|
79
|
+
n *= 2;
|
|
80
|
+
if (n > 9) n -= 9;
|
|
81
|
+
}
|
|
82
|
+
sum += n;
|
|
83
|
+
alt = !alt;
|
|
84
|
+
}
|
|
85
|
+
return sum > 0 && sum % 10 === 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function redact(value: unknown): unknown {
|
|
89
|
+
return redactDeep(value, false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function redactDeep(value: unknown, scrubStrings: boolean): unknown {
|
|
93
|
+
if (typeof value === "string") return scrubStrings ? maskInlinePans(value) : value;
|
|
94
|
+
if (Array.isArray(value)) return value.map((entry) => redactDeep(entry, scrubStrings));
|
|
95
|
+
if (!value || typeof value !== "object") return value;
|
|
96
|
+
|
|
97
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
98
|
+
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
99
|
+
if (secretKeyFragments.some((fragment) => normalized.includes(fragment))) {
|
|
100
|
+
return [key, REDACTED];
|
|
101
|
+
}
|
|
102
|
+
if (cardKeyExact.has(normalized)) {
|
|
103
|
+
return [key, maskCardLike(entry, scrubStrings)];
|
|
104
|
+
}
|
|
105
|
+
return [key, redactDeep(entry, scrubStrings)];
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function maskCardLike(value: unknown, scrubStrings: boolean): unknown {
|
|
110
|
+
if (Array.isArray(value)) return value.map((entry) => maskCardLike(entry, scrubStrings));
|
|
111
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
112
|
+
return maskPan(String(value));
|
|
113
|
+
}
|
|
114
|
+
if (value && typeof value === "object") {
|
|
115
|
+
return redactDeep(value, scrubStrings);
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function maskPan(value: string): string {
|
|
121
|
+
const digits = value.replace(/\D+/g, "");
|
|
122
|
+
if (digits.length < 12) return value;
|
|
123
|
+
if (digits.length >= 12 && digits.length <= 19) {
|
|
124
|
+
return `${digits.slice(0, 4)}${"*".repeat(Math.max(4, digits.length - 8))}${digits.slice(-4)}`;
|
|
125
|
+
}
|
|
126
|
+
const last4 = digits.slice(-4);
|
|
127
|
+
return `****${last4}`;
|
|
128
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { scenarioSchema, queryCatalogSchema, batchCatalogSchema, apiOperationsCatalogSchema } from "./schema.js";
|
|
4
|
+
import type { Catalogs, QueryCatalogEntry, Scenario } from "./types.js";
|
|
5
|
+
import { importedOperationsFromCollections, loadApiCollections } from "../adapters/api/api-collections.js";
|
|
6
|
+
import { normalizeQueryCatalog } from "../adapters/db/query-catalog.js";
|
|
7
|
+
|
|
8
|
+
export async function loadYamlFile<T>(path: string): Promise<T> {
|
|
9
|
+
const raw = await readFile(path, "utf8");
|
|
10
|
+
return YAML.parse(raw) as T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function loadScenario(path: string): Promise<Scenario> {
|
|
14
|
+
const parsed = await loadYamlFile<unknown>(path);
|
|
15
|
+
return scenarioSchema.parse(parsed) as Scenario;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function loadCatalogs(rootDir: string): Promise<Catalogs> {
|
|
19
|
+
const queries = normalizeQueryCatalog(queryCatalogSchema.parse(await loadYamlFile<unknown>(`${rootDir}/catalogs/queries.yaml`)) as Record<string, QueryCatalogEntry>);
|
|
20
|
+
const batches = batchCatalogSchema.parse(await loadYamlFile<unknown>(`${rootDir}/catalogs/batches.yaml`));
|
|
21
|
+
const apiOperations = apiOperationsCatalogSchema.parse(await loadYamlFile<unknown>(`${rootDir}/catalogs/api-operations.yaml`));
|
|
22
|
+
const importedApiOperations = importedOperationsFromCollections(await loadApiCollections(rootDir));
|
|
23
|
+
return { queries, batches, apiOperations: { ...apiOperations, ...importedApiOperations } } as Catalogs;
|
|
24
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
const value = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
4
|
+
const catalogParamType = z.enum(["string", "number", "boolean", "string[]", "number[]", "boolean[]"]);
|
|
5
|
+
const apiMethod = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
6
|
+
const apiBodyMode = z.enum(["none", "json", "raw", "formdata", "urlencoded"]);
|
|
7
|
+
const apiRequestSpecSchema = z.object({
|
|
8
|
+
method: apiMethod.optional(),
|
|
9
|
+
path: z.string().optional(),
|
|
10
|
+
headers: z.record(z.string()).optional(),
|
|
11
|
+
query: z.record(z.unknown()).optional(),
|
|
12
|
+
body: z.unknown().optional(),
|
|
13
|
+
rawBody: z.string().optional(),
|
|
14
|
+
bodyMode: apiBodyMode.optional(),
|
|
15
|
+
auth: z.unknown().optional(),
|
|
16
|
+
acceptStatuses: z.array(z.number().int().min(100).max(599)).optional()
|
|
17
|
+
}).strict();
|
|
18
|
+
const apiAssertionSchema = z.discriminatedUnion("type", [
|
|
19
|
+
z.object({
|
|
20
|
+
type: z.literal("status"),
|
|
21
|
+
operator: z.enum(["in", "="]).optional(),
|
|
22
|
+
value: z.union([z.number().int().min(100).max(599), z.array(z.number().int().min(100).max(599))])
|
|
23
|
+
}).strict(),
|
|
24
|
+
z.object({ type: z.literal("jsonpath_exists"), path: z.string().min(1) }).strict(),
|
|
25
|
+
z.object({ type: z.literal("jsonpath_equals"), path: z.string().min(1), value: z.unknown() }).strict(),
|
|
26
|
+
z.object({ type: z.literal("jsonpath_contains"), path: z.string().min(1), value: z.unknown() }).strict(),
|
|
27
|
+
z.object({ type: z.literal("header_exists"), header: z.string().min(1) }).strict(),
|
|
28
|
+
z.object({ type: z.literal("header_equals"), header: z.string().min(1), value: z.string() }).strict(),
|
|
29
|
+
z.object({ type: z.literal("body_contains"), value: z.string() }).strict(),
|
|
30
|
+
z.object({ type: z.literal("body_not_contains"), value: z.string() }).strict()
|
|
31
|
+
]);
|
|
32
|
+
const expectedOutcomeSchema = z.enum(["positive", "negative", "setup", "teardown"]);
|
|
33
|
+
const parallelJoinModeSchema = z.enum(["all", "any", "fail_fast"]);
|
|
34
|
+
const loopDateFormatSchema = z.enum(["YYYY-MM-DD", "DD/MM/YYYY", "MM/DD/YYYY"]);
|
|
35
|
+
const loopDateCursorSchema = z.object({
|
|
36
|
+
outputName: z.string().min(1).optional(),
|
|
37
|
+
start: z.string().min(1).optional(),
|
|
38
|
+
inputFormat: loopDateFormatSchema.optional(),
|
|
39
|
+
outputFormat: loopDateFormatSchema.optional(),
|
|
40
|
+
advance: z.object({
|
|
41
|
+
mode: z.enum(["days", "months", "nth_day_of_month", "first_of_month", "end_of_month"]),
|
|
42
|
+
amount: z.number().int().positive().optional(),
|
|
43
|
+
day: z.number().int().min(1).max(31).optional()
|
|
44
|
+
}).strict().optional()
|
|
45
|
+
}).strict();
|
|
46
|
+
const loopSpecSchema = z.object({
|
|
47
|
+
mode: z.enum(["count", "foreach"]),
|
|
48
|
+
count: z.union([z.number().int().nonnegative(), z.string()]).optional(),
|
|
49
|
+
items: z.unknown().optional(),
|
|
50
|
+
itemName: z.string().min(1).optional(),
|
|
51
|
+
maxIterations: z.number().int().positive().optional(),
|
|
52
|
+
dateCursor: loopDateCursorSchema.optional()
|
|
53
|
+
}).strict();
|
|
54
|
+
const catalogParamSchema = z.object({
|
|
55
|
+
required: z.boolean().optional(),
|
|
56
|
+
type: catalogParamType.optional(),
|
|
57
|
+
pattern: z.string().optional(),
|
|
58
|
+
luhn: z.boolean().optional()
|
|
59
|
+
}).strict();
|
|
60
|
+
const batchInputFileSchema = z.object({
|
|
61
|
+
name: z.string().min(1),
|
|
62
|
+
required: z.boolean().optional(),
|
|
63
|
+
remotePath: z.string().min(1).optional(),
|
|
64
|
+
paramName: z.string().min(1).optional(),
|
|
65
|
+
appendAsArg: z.boolean().optional()
|
|
66
|
+
}).strict();
|
|
67
|
+
const batchOutputFileSchema = z.object({
|
|
68
|
+
name: z.string().min(1),
|
|
69
|
+
required: z.boolean().optional(),
|
|
70
|
+
source: z.enum(["stdout", "stderr", "both", "explicit"]).optional(),
|
|
71
|
+
pathPattern: z.string().min(1).optional(),
|
|
72
|
+
remotePath: z.string().min(1).optional(),
|
|
73
|
+
download: z.boolean().optional(),
|
|
74
|
+
decrypt: z.object({
|
|
75
|
+
command: z.string().min(1).optional(),
|
|
76
|
+
outputRemotePath: z.string().min(1).optional(),
|
|
77
|
+
required: z.boolean().optional()
|
|
78
|
+
}).strict().optional()
|
|
79
|
+
}).strict();
|
|
80
|
+
|
|
81
|
+
export const scenarioStepSchema: z.ZodType<unknown> = z.lazy(() => z.object({
|
|
82
|
+
id: z.string().min(1).regex(/^[A-Za-z0-9_-]+$/),
|
|
83
|
+
action: z.string().min(1),
|
|
84
|
+
via: z.string().optional(),
|
|
85
|
+
retry: z.object({
|
|
86
|
+
attempts: z.number().int().positive().optional(),
|
|
87
|
+
delaySeconds: z.number().nonnegative().optional()
|
|
88
|
+
}).strict().optional(),
|
|
89
|
+
input: z.record(z.unknown()).optional(),
|
|
90
|
+
params: z.record(z.unknown()).optional(),
|
|
91
|
+
query: z.string().optional(),
|
|
92
|
+
batch: z.string().optional(),
|
|
93
|
+
request: apiRequestSpecSchema.optional(),
|
|
94
|
+
assertions: z.array(apiAssertionSchema).optional(),
|
|
95
|
+
capture: z.record(z.string()).optional(),
|
|
96
|
+
continueOnFailure: z.boolean().optional(),
|
|
97
|
+
expectedOutcome: expectedOutcomeSchema.optional(),
|
|
98
|
+
captureOnFailure: z.boolean().optional(),
|
|
99
|
+
control: z.enum(["parallel", "loop"]).optional(),
|
|
100
|
+
branches: z.array(z.object({
|
|
101
|
+
id: z.string().min(1).regex(/^[A-Za-z0-9_-]+$/),
|
|
102
|
+
label: z.string().min(1).optional(),
|
|
103
|
+
steps: z.array(scenarioStepSchema)
|
|
104
|
+
}).strict()).optional(),
|
|
105
|
+
steps: z.array(scenarioStepSchema).optional(),
|
|
106
|
+
loop: loopSpecSchema.optional(),
|
|
107
|
+
join: parallelJoinModeSchema.optional()
|
|
108
|
+
}).strict());
|
|
109
|
+
|
|
110
|
+
export const scenarioSchema = z.object({
|
|
111
|
+
id: z.string().min(1).regex(/^[A-Za-z0-9_-]+$/),
|
|
112
|
+
environment: z.string().min(1),
|
|
113
|
+
tenant: z.record(z.string()).optional(),
|
|
114
|
+
variables: z.record(z.union([value, z.array(value), z.record(value)])).optional(),
|
|
115
|
+
steps: z.array(scenarioStepSchema).min(1)
|
|
116
|
+
}).strict();
|
|
117
|
+
|
|
118
|
+
export const queryCatalogSchema = z.record(z.object({
|
|
119
|
+
description: z.string().optional(),
|
|
120
|
+
mode: z.enum(["query", "execute"]).optional(),
|
|
121
|
+
sql: z.string().min(1),
|
|
122
|
+
params: z.record(catalogParamSchema).optional(),
|
|
123
|
+
expect: z.object({
|
|
124
|
+
type: z.enum(["number", "string", "boolean", "rowCount"]),
|
|
125
|
+
column: z.string().optional(),
|
|
126
|
+
operator: z.enum(["=", "!=", ">", ">=", "<", "<=", "contains"]),
|
|
127
|
+
value: z.unknown()
|
|
128
|
+
}).strict()
|
|
129
|
+
.refine((expect) => expect.type === "rowCount" || Boolean(expect.column), {
|
|
130
|
+
message: "expect.column is required when expect.type is not rowCount."
|
|
131
|
+
})
|
|
132
|
+
.optional(),
|
|
133
|
+
captures: z.record(z.string()).optional(),
|
|
134
|
+
maxRows: z.number().int().positive().optional()
|
|
135
|
+
}).strict());
|
|
136
|
+
|
|
137
|
+
export const batchCatalogSchema = z.record(z.object({
|
|
138
|
+
description: z.string().optional(),
|
|
139
|
+
hostRef: z.string().min(1),
|
|
140
|
+
command: z.string().min(1).refine((value) => !/[\r\n]/.test(value), {
|
|
141
|
+
message: "command must be a single executable token; put arguments in fixedArgs."
|
|
142
|
+
}),
|
|
143
|
+
fixedArgs: z.array(z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
144
|
+
workingDirectory: z.string().min(1).optional(),
|
|
145
|
+
useWorkingDirectory: z.boolean().optional(),
|
|
146
|
+
environment: z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
147
|
+
args: z.array(z.object({
|
|
148
|
+
name: z.string().min(1),
|
|
149
|
+
required: z.boolean().optional(),
|
|
150
|
+
type: catalogParamType.optional(),
|
|
151
|
+
pattern: z.string().optional(),
|
|
152
|
+
luhn: z.boolean().optional()
|
|
153
|
+
}).strict()).optional(),
|
|
154
|
+
inputFiles: z.array(batchInputFileSchema).optional(),
|
|
155
|
+
outputFiles: z.array(batchOutputFileSchema).optional(),
|
|
156
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
157
|
+
success: z.object({
|
|
158
|
+
exitCodes: z.array(z.number().int()).optional(),
|
|
159
|
+
requiredOutput: z.array(z.string()).optional()
|
|
160
|
+
}).strict().optional(),
|
|
161
|
+
captures: z.record(z.string()).optional()
|
|
162
|
+
}).strict());
|
|
163
|
+
|
|
164
|
+
export const apiOperationsCatalogSchema = z.record(z.object({
|
|
165
|
+
description: z.string().optional(),
|
|
166
|
+
type: z.enum(["rest", "soap"]),
|
|
167
|
+
method: apiMethod.optional(),
|
|
168
|
+
path: z.string().optional(),
|
|
169
|
+
headers: z.record(z.string()).optional(),
|
|
170
|
+
query: z.record(z.unknown()).optional(),
|
|
171
|
+
body: z.unknown().optional(),
|
|
172
|
+
rawBody: z.string().optional(),
|
|
173
|
+
bodyMode: apiBodyMode.optional(),
|
|
174
|
+
auth: z.unknown().optional(),
|
|
175
|
+
params: z.record(catalogParamSchema).optional(),
|
|
176
|
+
assertions: z.array(apiAssertionSchema).optional(),
|
|
177
|
+
requestTemplate: z.string().optional(),
|
|
178
|
+
captures: z.record(z.string()).optional(),
|
|
179
|
+
acceptStatuses: z.array(z.number().int().min(100).max(599)).optional(),
|
|
180
|
+
idempotent: z.boolean().optional(),
|
|
181
|
+
source: z.object({
|
|
182
|
+
collectionId: z.string().optional(),
|
|
183
|
+
collectionName: z.string().optional(),
|
|
184
|
+
requestId: z.string().optional(),
|
|
185
|
+
folderPath: z.array(z.string()).optional()
|
|
186
|
+
}).strict().optional()
|
|
187
|
+
}).strict());
|
|
188
|
+
|
|
189
|
+
export { apiRequestSpecSchema, apiAssertionSchema };
|