Skip to content

Commit 9e17713

Browse files
committed
Implement CLI send flows and refactor plugin event handling
1 parent 6cc659e commit 9e17713

File tree

29 files changed

+1587
-252
lines changed

29 files changed

+1587
-252
lines changed

Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates-cli/yaak-cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ path = "src/main.rs"
1212
clap = { version = "4", features = ["derive"] }
1313
dirs = "6"
1414
env_logger = "0.11"
15+
futures = "0.3"
1516
log = { workspace = true }
17+
schemars = { version = "0.8.22", features = ["chrono"] }
1618
serde = { workspace = true }
1719
serde_json = { workspace = true }
1820
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

crates-cli/yaak-cli/PLAN.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,23 @@ Current branch state:
1212
- Modular CLI structure with command modules and shared `CliContext`
1313
- Resource/action hierarchy in place for:
1414
- `workspace list|show|create|update|delete`
15-
- `request list|show|create|update|send|delete`
15+
- `request list|show|create|update|send|delete|schema`
1616
- `folder list|show|create|update|delete`
1717
- `environment list|show|create|update|delete`
18-
- Top-level `send` exists as a request-send shortcut (not yet flexible request/folder/workspace resolution)
18+
- Top-level `send` resolves request/folder/workspace IDs and supports `--sequential|--parallel` + `--fail-fast`
1919
- Legacy `get` command removed
2020
- JSON create/update flow implemented (`--json` and positional JSON shorthand)
21-
- No `request schema` command yet
21+
- Request schema generation implemented via `schemars`, with plugin auth-field merge into `authentication`
22+
- `request send` is polymorphic via `get_any_request`; HTTP is implemented, gRPC/WebSocket return explicit NYI errors
2223

2324
Progress checklist:
2425

2526
- [x] Phase 1 complete
2627
- [x] Phase 2 complete
2728
- [x] Phase 3 complete
28-
- [ ] Phase 4 complete
29-
- [ ] Phase 5 complete
30-
- [ ] Phase 6 complete
29+
- [x] Phase 4 complete
30+
- [x] Phase 5 complete
31+
- [x] Phase 6 complete
3132

3233
## Command Architecture
3334

@@ -47,7 +48,7 @@ Progress checklist:
4748

4849
```
4950
# Top-level shortcut
50-
yaakcli send <id> [-e <env_id>] # id can be a request, folder, or workspace
51+
yaakcli send <id> [--sequential|--parallel] [--fail-fast] [-e <env_id>]
5152
5253
# Resource commands
5354
yaakcli workspace list
@@ -102,8 +103,8 @@ is purely by DB lookup.
102103
`send` means "execute this request" regardless of protocol:
103104

104105
- **HTTP**: send request, print response, exit
105-
- **gRPC**: invoke the method; for streaming, stream output to stdout until done/Ctrl+C
106-
- **WebSocket**: connect, stream messages to stdout until closed/Ctrl+C
106+
- **gRPC**: currently returns explicit "not implemented yet in yaak-cli"
107+
- **WebSocket**: currently returns explicit "not implemented yet in yaak-cli"
107108

108109
### `request schema` — Runtime JSON Schema
109110

crates-cli/yaak-cli/src/cli.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clap::{Args, Parser, Subcommand};
1+
use clap::{Args, Parser, Subcommand, ValueEnum};
22
use std::path::PathBuf;
33

44
#[derive(Parser)]
@@ -23,7 +23,7 @@ pub struct Cli {
2323

2424
#[derive(Subcommand)]
2525
pub enum Commands {
26-
/// Send an HTTP request by ID
26+
/// Send a request, folder, or workspace by ID
2727
Send(SendArgs),
2828

2929
/// Workspace commands
@@ -41,8 +41,20 @@ pub enum Commands {
4141

4242
#[derive(Args)]
4343
pub struct SendArgs {
44-
/// Request ID
45-
pub request_id: String,
44+
/// Request, folder, or workspace ID
45+
pub id: String,
46+
47+
/// Execute requests sequentially (default)
48+
#[arg(long, conflicts_with = "parallel")]
49+
pub sequential: bool,
50+
51+
/// Execute requests in parallel
52+
#[arg(long, conflicts_with = "sequential")]
53+
pub parallel: bool,
54+
55+
/// Stop on first request failure when sending folders/workspaces
56+
#[arg(long, conflicts_with = "parallel")]
57+
pub fail_fast: bool,
4658
}
4759

4860
#[derive(Args)]
@@ -119,12 +131,18 @@ pub enum RequestCommands {
119131
request_id: String,
120132
},
121133

122-
/// Send an HTTP request by ID
134+
/// Send a request by ID
123135
Send {
124136
/// Request ID
125137
request_id: String,
126138
},
127139

140+
/// Output JSON schema for request create/update payloads
141+
Schema {
142+
#[arg(value_enum)]
143+
request_type: RequestSchemaType,
144+
},
145+
128146
/// Create a new HTTP request
129147
Create {
130148
/// Workspace ID (or positional JSON payload shorthand)
@@ -169,6 +187,13 @@ pub enum RequestCommands {
169187
},
170188
}
171189

190+
#[derive(Clone, Copy, Debug, ValueEnum)]
191+
pub enum RequestSchemaType {
192+
Http,
193+
Grpc,
194+
Websocket,
195+
}
196+
172197
#[derive(Args)]
173198
pub struct FolderArgs {
174199
#[command(subcommand)]

crates-cli/yaak-cli/src/commands/environment.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ fn show(ctx: &CliContext, environment_id: &str) -> CommandResult {
5151
.db()
5252
.get_environment(environment_id)
5353
.map_err(|e| format!("Failed to get environment: {e}"))?;
54-
let output =
55-
serde_json::to_string_pretty(&environment).map_err(|e| format!("Failed to serialize environment: {e}"))?;
54+
let output = serde_json::to_string_pretty(&environment)
55+
.map_err(|e| format!("Failed to serialize environment: {e}"))?;
5656
println!("{output}");
5757
Ok(())
5858
}
@@ -81,9 +81,8 @@ fn create(
8181
}
8282

8383
validate_create_id(&payload, "environment")?;
84-
let mut environment: Environment =
85-
serde_json::from_value(payload)
86-
.map_err(|e| format!("Failed to parse environment create JSON: {e}"))?;
84+
let mut environment: Environment = serde_json::from_value(payload)
85+
.map_err(|e| format!("Failed to parse environment create JSON: {e}"))?;
8786

8887
if environment.workspace_id.is_empty() {
8988
return Err("environment create JSON requires non-empty \"workspaceId\"".to_string());
@@ -105,8 +104,9 @@ fn create(
105104
let workspace_id = workspace_id.ok_or_else(|| {
106105
"environment create requires workspace_id unless JSON payload is provided".to_string()
107106
})?;
108-
let name = name
109-
.ok_or_else(|| "environment create requires --name unless JSON payload is provided".to_string())?;
107+
let name = name.ok_or_else(|| {
108+
"environment create requires --name unless JSON payload is provided".to_string()
109+
})?;
110110

111111
let environment = Environment {
112112
workspace_id,

crates-cli/yaak-cli/src/commands/folder.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ pub fn run(ctx: &CliContext, args: FolderArgs) -> i32 {
3131
}
3232

3333
fn list(ctx: &CliContext, workspace_id: &str) -> CommandResult {
34-
let folders = ctx.db().list_folders(workspace_id).map_err(|e| format!("Failed to list folders: {e}"))?;
34+
let folders =
35+
ctx.db().list_folders(workspace_id).map_err(|e| format!("Failed to list folders: {e}"))?;
3536
if folders.is_empty() {
3637
println!("No folders found in workspace {}", workspace_id);
3738
} else {
@@ -43,9 +44,10 @@ fn list(ctx: &CliContext, workspace_id: &str) -> CommandResult {
4344
}
4445

4546
fn show(ctx: &CliContext, folder_id: &str) -> CommandResult {
46-
let folder = ctx.db().get_folder(folder_id).map_err(|e| format!("Failed to get folder: {e}"))?;
47-
let output =
48-
serde_json::to_string_pretty(&folder).map_err(|e| format!("Failed to serialize folder: {e}"))?;
47+
let folder =
48+
ctx.db().get_folder(folder_id).map_err(|e| format!("Failed to get folder: {e}"))?;
49+
let output = serde_json::to_string_pretty(&folder)
50+
.map_err(|e| format!("Failed to serialize folder: {e}"))?;
4951
println!("{output}");
5052
Ok(())
5153
}
@@ -72,8 +74,8 @@ fn create(
7274
}
7375

7476
validate_create_id(&payload, "folder")?;
75-
let folder: Folder =
76-
serde_json::from_value(payload).map_err(|e| format!("Failed to parse folder create JSON: {e}"))?;
77+
let folder: Folder = serde_json::from_value(payload)
78+
.map_err(|e| format!("Failed to parse folder create JSON: {e}"))?;
7779

7880
if folder.workspace_id.is_empty() {
7981
return Err("folder create JSON requires non-empty \"workspaceId\"".to_string());
@@ -88,10 +90,12 @@ fn create(
8890
return Ok(());
8991
}
9092

91-
let workspace_id = workspace_id
92-
.ok_or_else(|| "folder create requires workspace_id unless JSON payload is provided".to_string())?;
93-
let name =
94-
name.ok_or_else(|| "folder create requires --name unless JSON payload is provided".to_string())?;
93+
let workspace_id = workspace_id.ok_or_else(|| {
94+
"folder create requires workspace_id unless JSON payload is provided".to_string()
95+
})?;
96+
let name = name.ok_or_else(|| {
97+
"folder create requires --name unless JSON payload is provided".to_string()
98+
})?;
9599

96100
let folder = Folder { workspace_id, name, ..Default::default() };
97101

@@ -108,10 +112,8 @@ fn update(ctx: &CliContext, json: Option<String>, json_input: Option<String>) ->
108112
let patch = parse_required_json(json, json_input, "folder update")?;
109113
let id = require_id(&patch, "folder update")?;
110114

111-
let existing = ctx
112-
.db()
113-
.get_folder(&id)
114-
.map_err(|e| format!("Failed to get folder for update: {e}"))?;
115+
let existing =
116+
ctx.db().get_folder(&id).map_err(|e| format!("Failed to get folder for update: {e}"))?;
115117
let updated = apply_merge_patch(&existing, &patch, &id, "folder update")?;
116118

117119
let saved = ctx

0 commit comments

Comments
 (0)