@matimo/bruno 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/README.md +174 -0
- package/definition.yaml +33 -0
- package/package.json +17 -0
- package/tools/bru-utils.ts +51 -0
- package/tools/bruno_add_request/definition.yaml +108 -0
- package/tools/bruno_add_request/index.ts +101 -0
- package/tools/bruno_create_collection/definition.yaml +46 -0
- package/tools/bruno_create_collection/index.ts +55 -0
- package/tools/bruno_get_collection_info/definition.yaml +61 -0
- package/tools/bruno_get_collection_info/index.ts +119 -0
- package/tools/bruno_import_openapi/definition.yaml +75 -0
- package/tools/bruno_import_openapi/index.ts +102 -0
- package/tools/bruno_list_collections/definition.yaml +49 -0
- package/tools/bruno_list_collections/index.ts +101 -0
- package/tools/bruno_run_collection/definition.yaml +131 -0
- package/tools/bruno_run_collection/index.ts +99 -0
- package/tools/bruno_run_request/definition.yaml +69 -0
- package/tools/bruno_run_request/index.ts +104 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: bruno_run_request
|
|
2
|
+
description: Execute a single request from a Bruno collection for targeted debugging and validation. Returns detailed request/response data and assertion results.
|
|
3
|
+
version: '1.0.0'
|
|
4
|
+
status: approved
|
|
5
|
+
|
|
6
|
+
parameters:
|
|
7
|
+
collection_path:
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
description: Path to Bruno collection directory
|
|
11
|
+
|
|
12
|
+
request_name:
|
|
13
|
+
type: string
|
|
14
|
+
required: true
|
|
15
|
+
description: Name of the request to execute (exact match)
|
|
16
|
+
|
|
17
|
+
environment:
|
|
18
|
+
type: string
|
|
19
|
+
required: false
|
|
20
|
+
description: Environment name to use (overrides collection default)
|
|
21
|
+
|
|
22
|
+
env_file:
|
|
23
|
+
type: string
|
|
24
|
+
required: false
|
|
25
|
+
description: Path to environment file to override variables
|
|
26
|
+
|
|
27
|
+
sandbox_mode:
|
|
28
|
+
type: string
|
|
29
|
+
required: false
|
|
30
|
+
default: safe
|
|
31
|
+
description: JavaScript execution mode - 'safe' or 'developer'
|
|
32
|
+
|
|
33
|
+
execution:
|
|
34
|
+
type: function
|
|
35
|
+
code: index.ts
|
|
36
|
+
timeout: 60000
|
|
37
|
+
|
|
38
|
+
output_schema:
|
|
39
|
+
type: object
|
|
40
|
+
properties:
|
|
41
|
+
success:
|
|
42
|
+
type: boolean
|
|
43
|
+
request:
|
|
44
|
+
type: string
|
|
45
|
+
description: Name of the request that was executed
|
|
46
|
+
status:
|
|
47
|
+
type: number
|
|
48
|
+
description: HTTP status code parsed from bru output
|
|
49
|
+
duration_ms:
|
|
50
|
+
type: number
|
|
51
|
+
description: Total execution time in milliseconds
|
|
52
|
+
errors:
|
|
53
|
+
type: array
|
|
54
|
+
items:
|
|
55
|
+
type: string
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
examples:
|
|
59
|
+
- name: "Debug single authentication request"
|
|
60
|
+
params:
|
|
61
|
+
collection_path: "./collections/auth"
|
|
62
|
+
request_name: "Login"
|
|
63
|
+
environment: "staging"
|
|
64
|
+
|
|
65
|
+
- name: "Test specific endpoint with custom environment"
|
|
66
|
+
params:
|
|
67
|
+
collection_path: "./collections/api"
|
|
68
|
+
request_name: "Get User Profile"
|
|
69
|
+
env_file: "./envs/test.json"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { getGlobalMatimoLogger } from '@matimo/core';
|
|
2
|
+
import { execFileSync } from 'child_process';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { checkBruVersion } from '../bru-utils';
|
|
6
|
+
|
|
7
|
+
const logger = getGlobalMatimoLogger();
|
|
8
|
+
|
|
9
|
+
/** Search recursively for a .bru file matching the given slug, return path relative to root. */
|
|
10
|
+
async function findBruFile(root: string, slug: string): Promise<string | null> {
|
|
11
|
+
let entries: string[];
|
|
12
|
+
try {
|
|
13
|
+
entries = await fs.readdir(root);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (entries.includes(`${slug}.bru`)) {
|
|
18
|
+
return `${slug}.bru`;
|
|
19
|
+
}
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const fullPath = path.join(root, entry);
|
|
22
|
+
try {
|
|
23
|
+
const stat = await fs.stat(fullPath);
|
|
24
|
+
if (stat.isDirectory() && entry !== 'node_modules') {
|
|
25
|
+
const found = await findBruFile(fullPath, slug);
|
|
26
|
+
if (found) return path.join(entry, found);
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
// skip
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default async function execute(params: Record<string, unknown>): Promise<unknown> {
|
|
36
|
+
const collectionPath = params.collection_path as string;
|
|
37
|
+
const requestName = params.request_name as string;
|
|
38
|
+
|
|
39
|
+
if (!collectionPath || !requestName) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
request: requestName ?? '',
|
|
43
|
+
status: 0,
|
|
44
|
+
duration_ms: 0,
|
|
45
|
+
errors: ['collection_path and request_name parameters are required'],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
checkBruVersion();
|
|
50
|
+
|
|
51
|
+
const absolutePath = path.resolve(collectionPath);
|
|
52
|
+
const slug = requestName.toLowerCase().replace(/\s+/g, '-');
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
logger.info(`Running request: ${requestName} from ${absolutePath}`);
|
|
56
|
+
|
|
57
|
+
// Locate the .bru file (may be in a requests/ subfolder)
|
|
58
|
+
const bruRelPath = (await findBruFile(absolutePath, slug)) ?? `${slug}.bru`;
|
|
59
|
+
|
|
60
|
+
const args: string[] = ['run', bruRelPath];
|
|
61
|
+
|
|
62
|
+
if (params.environment) args.push('--env', params.environment as string);
|
|
63
|
+
if (params.env_file) args.push('--env-file', params.env_file as string);
|
|
64
|
+
args.push('--sandbox', (params.sandbox_mode as string) || 'safe');
|
|
65
|
+
|
|
66
|
+
logger.debug(`Executing: bru ${args.join(' ')}`);
|
|
67
|
+
|
|
68
|
+
const start = Date.now();
|
|
69
|
+
let success = true;
|
|
70
|
+
let output = '';
|
|
71
|
+
try {
|
|
72
|
+
output = execFileSync('bru', args, { encoding: 'utf-8', stdio: 'pipe', cwd: absolutePath });
|
|
73
|
+
} catch (execError) {
|
|
74
|
+
success = false;
|
|
75
|
+
// Extract stdout/stderr from the error so assertion failures are visible
|
|
76
|
+
const err = execError as NodeJS.ErrnoException & { stdout?: string; stderr?: string };
|
|
77
|
+
output = [err.stdout, err.stderr, err.message].filter(Boolean).join('\n');
|
|
78
|
+
}
|
|
79
|
+
const durationMs = Date.now() - start;
|
|
80
|
+
|
|
81
|
+
// Parse status code from bru run output (e.g. "200 OK")
|
|
82
|
+
const statusMatch = output.match(/\b([1-5]\d{2})\b/);
|
|
83
|
+
const status = statusMatch ? parseInt(statusMatch[1], 10) : (success ? 200 : 0);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
success,
|
|
87
|
+
request: requestName,
|
|
88
|
+
status,
|
|
89
|
+
duration_ms: durationMs,
|
|
90
|
+
errors: success ? [] : [output],
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error(
|
|
94
|
+
`Request execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
95
|
+
);
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
request: requestName,
|
|
99
|
+
status: 0,
|
|
100
|
+
duration_ms: 0,
|
|
101
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|