Skip to content

Commit 49d8dc1

Browse files
committed
Added --dry-run option for write commands
1 parent 2ff3867 commit 49d8dc1

5 files changed

Lines changed: 45 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ webex cc agents list --output csv > agents.csv
145145
| `--output json\|table\|csv\|raw` | Output format (default: json) |
146146
| `--debug` | Show HTTP request/response details |
147147
| `--paginate` | Auto-paginate list results |
148+
| `--dry-run` | Print write requests without executing them |
148149

149150
## Configuration
150151

cmd/root.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67

78
"github.com/Cloverhound/webex-cli/internal/appconfig"
89
"github.com/Cloverhound/webex-cli/internal/auth"
10+
"github.com/Cloverhound/webex-cli/internal/client"
911
"github.com/Cloverhound/webex-cli/internal/config"
1012
"github.com/Cloverhound/webex-cli/internal/output"
1113
"github.com/spf13/cobra"
@@ -21,6 +23,10 @@ var rootCmd = &cobra.Command{
2123
debug, _ := cmd.Flags().GetBool("debug")
2224
config.SetDebug(debug)
2325

26+
// Dry-run mode
27+
dryRun, _ := cmd.Flags().GetBool("dry-run")
28+
config.SetDryRun(dryRun)
29+
2430
// Output format
2531
format, _ := cmd.Flags().GetString("output")
2632
output.SetFormat(format)
@@ -110,7 +116,11 @@ func Execute() error {
110116
stripRequiredOrgID(rootCmd)
111117
// Hide per-command org flags so only --organization is visible in help.
112118
hideOrgFlags(rootCmd)
113-
return rootCmd.Execute()
119+
err := rootCmd.Execute()
120+
if errors.Is(err, client.ErrDryRun) {
121+
return nil
122+
}
123+
return err
114124
}
115125

116126
// stripRequiredOrgID recursively removes the "required" annotation from --orgid and --org-id flags.
@@ -146,6 +156,7 @@ func init() {
146156
rootCmd.PersistentFlags().String("output", "json", "Output format: json, table, csv, raw")
147157
rootCmd.PersistentFlags().Bool("debug", false, "Enable debug logging of HTTP requests")
148158
rootCmd.PersistentFlags().Bool("paginate", false, "Auto-paginate list results")
159+
rootCmd.PersistentFlags().Bool("dry-run", false, "Print write requests without executing them")
149160
rootCmd.PersistentFlags().String("user", "", "Use a specific authenticated user (email)")
150161
rootCmd.PersistentFlags().String("organization", "", "Override organization ID for this command")
151162

internal/client/client.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"errors"
45
"fmt"
56
"io"
67
"net/http"
@@ -10,6 +11,9 @@ import (
1011
"github.com/Cloverhound/webex-cli/internal/config"
1112
)
1213

14+
// ErrDryRun is returned when a write operation is intercepted by --dry-run mode.
15+
var ErrDryRun = errors.New("dry run: no changes made")
16+
1317
// Do executes an HTTP request. On a 401, it attempts to refresh the token and retry once.
1418
func Do(req *Request) ([]byte, int, error) {
1519
body, status, err := doOnce(req)
@@ -83,6 +87,20 @@ func doOnce(req *Request) ([]byte, int, error) {
8387
}
8488
}
8589

90+
// Dry-run: intercept write operations before making the HTTP call
91+
if config.DryRun() && isWriteMethod(req.method) {
92+
fmt.Fprintf(os.Stderr, "[DRY RUN] %s %s\n", req.method, url)
93+
for k, v := range httpReq.Header {
94+
if k != "Authorization" {
95+
fmt.Fprintf(os.Stderr, "[DRY RUN] %s: %s\n", k, strings.Join(v, ", "))
96+
}
97+
}
98+
if req.bodyRaw != "" {
99+
fmt.Fprintf(os.Stderr, "[DRY RUN] Body: %s\n", req.bodyRaw)
100+
}
101+
return nil, 0, ErrDryRun
102+
}
103+
86104
resp, err := http.DefaultClient.Do(httpReq)
87105
if err != nil {
88106
return nil, 0, fmt.Errorf("executing request: %w", err)
@@ -111,3 +129,12 @@ func truncate(s string, n int) string {
111129
}
112130
return s[:n] + "..."
113131
}
132+
133+
// isWriteMethod returns true for HTTP methods that modify data.
134+
func isWriteMethod(method string) bool {
135+
switch method {
136+
case http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch:
137+
return true
138+
}
139+
return false
140+
}

internal/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var (
99
token string
1010
debug bool
1111
paginate bool
12+
dryRun bool
1213
orgID string // UUID format
1314
orgIDBase64 string // base64 Webex format
1415
TokenRefresher func() (string, error)
@@ -23,6 +24,9 @@ func Debug() bool { return debug }
2324
func SetPaginate(p bool) { paginate = p }
2425
func Paginate() bool { return paginate }
2526

27+
func SetDryRun(d bool) { dryRun = d }
28+
func DryRun() bool { return dryRun }
29+
2630
// SetOrgID stores the org ID in both UUID and base64 formats.
2731
// Accepts either format as input and derives the other.
2832
func SetOrgID(id string) {

skill/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Aliases: `devices` for `device`, `meeting` for `meetings`, `msg` for `messaging`
5555
- `--output json|table|csv|raw` — Output format (default: json)
5656
- `--debug` — Show HTTP request/response details
5757
- `--paginate` — Auto-paginate list results
58+
- `--dry-run` — Print write requests (POST/PUT/DELETE/PATCH) without executing them; read operations still run normally
5859

5960
## Contact Center `--orgid` Handling
6061

0 commit comments

Comments
 (0)