|
diff --git a/main.go b/main.go
|
| ... |
| 79 |
reqNames := flag.String("req", "", "Comma-separated list of request names to execute") |
79 |
reqNames := flag.String("req", "", "Comma-separated list of request names to execute") |
| 80 |
groupName := flag.String("group", "", "Group to execute") |
80 |
groupName := flag.String("group", "", "Group to execute") |
| 81 |
showHeaders := flag.Bool("headers", false, "Display response headers") |
81 |
showHeaders := flag.Bool("headers", false, "Display response headers") |
|
|
82 |
timeout := flag.Duration("timeout", 10*time.Second, "Request timeout duration") |
| 82 |
flag.Parse() |
83 |
flag.Parse() |
| 83 |
|
84 |
|
| 84 |
if filePath == "" { |
85 |
if filePath == "" { |
| ... |
| 87 |
os.Exit(1) |
88 |
os.Exit(1) |
| 88 |
} |
89 |
} |
| 89 |
|
90 |
|
| 90 |
runner, err := NewRunner(filePath, envName, statePath) |
91 |
runner, err := NewRunner(filePath, envName, statePath, *timeout) |
| 91 |
if err != nil { |
92 |
if err != nil { |
| 92 |
log.Fatalf("Error: %v", err) |
93 |
log.Fatalf("Error: %v", err) |
| 93 |
} |
94 |
} |
| ... |
| 112 |
} |
113 |
} |
| 113 |
|
114 |
|
| 114 |
// NewRunner initializes a new Hepi runner. |
115 |
// NewRunner initializes a new Hepi runner. |
| 115 |
func NewRunner(filePath, envName, stateFile string) (*Runner, error) { |
116 |
func NewRunner(filePath, envName, stateFile string, timeout time.Duration) (*Runner, error) { |
| 116 |
data, err := os.ReadFile(filePath) |
117 |
data, err := os.ReadFile(filePath) |
| 117 |
if err != nil { |
118 |
if err != nil { |
| 118 |
return nil, fmt.Errorf("failed to read file: %w", err) |
119 |
return nil, fmt.Errorf("%sfailed to read file: %w%s", colorRed, err, colorReset) |
| 119 |
} |
120 |
} |
| 120 |
|
121 |
|
| 121 |
var config Config |
122 |
var config Config |
| 122 |
if err := yaml.Unmarshal(data, &config); err != nil { |
123 |
if err := yaml.Unmarshal(data, &config); err != nil { |
| 123 |
return nil, fmt.Errorf("failed to parse YAML: %w", err) |
124 |
return nil, fmt.Errorf("%sfailed to parse YAML: %w%s", colorRed, err, colorReset) |
| 124 |
} |
125 |
} |
| 125 |
|
126 |
|
| 126 |
if config.Environments.Kind != yaml.MappingNode { |
127 |
if config.Environments.Kind != yaml.MappingNode { |
| 127 |
return nil, fmt.Errorf("environments must be a mapping") |
128 |
return nil, fmt.Errorf("%senvironments must be a mapping%s", colorRed, colorReset) |
| 128 |
} |
129 |
} |
| 129 |
|
130 |
|
| 130 |
selectedEnvName := envName |
131 |
selectedEnvName := envName |
| ... |
| 138 |
availableEnvs = append(availableEnvs, name) |
139 |
availableEnvs = append(availableEnvs, name) |
| 139 |
if name == envName { |
140 |
if name == envName { |
| 140 |
if err := config.Environments.Content[i+1].Decode(&selectedEnv); err != nil { |
141 |
if err := config.Environments.Content[i+1].Decode(&selectedEnv); err != nil { |
| 141 |
return nil, fmt.Errorf("failed to decode environment %q: %w", envName, err) |
142 |
return nil, fmt.Errorf("%sfailed to decode environment %q: %w%s", colorRed, envName, err, colorReset) |
| 142 |
} |
143 |
} |
| 143 |
found = true |
144 |
found = true |
| 144 |
break |
145 |
break |
| 145 |
} |
146 |
} |
| 146 |
} |
147 |
} |
| 147 |
if !found { |
148 |
if !found { |
| 148 |
return nil, fmt.Errorf("environment %q not found\nAvailable environments:\n- %s", envName, strings.Join(availableEnvs, "\n- ")) |
149 |
return nil, fmt.Errorf("%senvironment %q not found\nAvailable environments:\n- %s%s", colorRed, envName, strings.Join(availableEnvs, "\n- "), colorReset) |
| 149 |
} |
150 |
} |
| 150 |
} |
151 |
} |
| 151 |
|
152 |
|
| ... |
| 155 |
Environment: selectedEnv, |
156 |
Environment: selectedEnv, |
| 156 |
State: loadState(selectedEnvName, stateFile), |
157 |
State: loadState(selectedEnvName, stateFile), |
| 157 |
StateFile: stateFile, |
158 |
StateFile: stateFile, |
| 158 |
HTTPClient: &http.Client{Timeout: 10 * time.Second}, |
159 |
HTTPClient: &http.Client{Timeout: timeout}, |
| 159 |
}, nil |
160 |
}, nil |
| 160 |
} |
161 |
} |
| 161 |
|
162 |
|
| ... |
| 163 |
func (r *Runner) ExecuteGroup(groupName string) error { |
164 |
func (r *Runner) ExecuteGroup(groupName string) error { |
| 164 |
group, ok := r.Config.Groups[groupName] |
165 |
group, ok := r.Config.Groups[groupName] |
| 165 |
if !ok { |
166 |
if !ok { |
| 166 |
return fmt.Errorf("group %q not found", groupName) |
167 |
return fmt.Errorf("%sgroup %q not found%s", colorRed, groupName, colorReset) |
| 167 |
} |
168 |
} |
| 168 |
|
169 |
|
| 169 |
for _, reqName := range group { |
170 |
for _, reqName := range group { |
| ... |
| 187 |
|
188 |
|
| 188 |
requestsNode := r.Config.Requests |
189 |
requestsNode := r.Config.Requests |
| 189 |
if requestsNode.Kind != yaml.MappingNode { |
190 |
if requestsNode.Kind != yaml.MappingNode { |
| 190 |
return fmt.Errorf("requests must be a mapping") |
191 |
return fmt.Errorf("%srequests must be a mapping%s", colorRed, colorReset) |
| 191 |
} |
192 |
} |
| 192 |
|
193 |
|
| 193 |
for i := 0; i < len(requestsNode.Content); i += 2 { |
194 |
for i := 0; i < len(requestsNode.Content); i += 2 { |
| ... |
| 202 |
|
203 |
|
| 203 |
var req Request |
204 |
var req Request |
| 204 |
if err := valNode.Decode(&req); err != nil { |
205 |
if err := valNode.Decode(&req); err != nil { |
| 205 |
return fmt.Errorf("failed to decode request %q: %w", name, err) |
206 |
return fmt.Errorf("%sfailed to decode request %q: %w%s", colorRed, name, err, colorReset) |
| 206 |
} |
207 |
} |
| 207 |
|
208 |
|
| 208 |
fmt.Printf("\n%s--- %s[%s]%s %s ---%s\n", colorBold, colorCyan, name, colorReset, req.Description, colorReset) |
209 |
fmt.Printf("\n%s--- %s[%s]%s %s ---%s\n", colorBold, colorCyan, name, colorReset, req.Description, colorReset) |
| 209 |
if err := r.executeRequest(name, req); err != nil { |
210 |
if err := r.executeRequest(name, req); err != nil { |
| 210 |
log.Printf("Warning: request %q failed: %v", name, err) |
211 |
return err |
| 211 |
} |
212 |
} |
| 212 |
} |
213 |
} |
| 213 |
|
214 |
|
| ... |
| 218 |
} |
219 |
} |
| 219 |
} |
220 |
} |
| 220 |
if len(missing) > 0 { |
221 |
if len(missing) > 0 { |
| 221 |
return fmt.Errorf("requests not found: %s", strings.Join(missing, ", ")) |
222 |
return fmt.Errorf("%srequests not found: %s%s", colorRed, strings.Join(missing, ", "), colorReset) |
| 222 |
} |
223 |
} |
| 223 |
|
224 |
|
| 224 |
return nil |
225 |
return nil |
| ... |
| 231 |
if req.Params != nil { |
232 |
if req.Params != nil { |
| 232 |
u, err := url.Parse(rawURL) |
233 |
u, err := url.Parse(rawURL) |
| 233 |
if err != nil { |
234 |
if err != nil { |
| 234 |
return fmt.Errorf("failed to parse URL %q: %w", rawURL, err) |
235 |
return fmt.Errorf("%sfailed to parse URL %q: %w%s", colorRed, rawURL, err, colorReset) |
| 235 |
} |
236 |
} |
| 236 |
q := u.Query() |
237 |
q := u.Query() |
| 237 |
params := r.substituteMap(req.Params) |
238 |
params := r.substituteMap(req.Params) |
| ... |
| 281 |
substitutedPath := r.substitute(path) |
282 |
substitutedPath := r.substitute(path) |
| 282 |
file, err := os.Open(substitutedPath) |
283 |
file, err := os.Open(substitutedPath) |
| 283 |
if err != nil { |
284 |
if err != nil { |
| 284 |
return fmt.Errorf("failed to open file %q: %w", substitutedPath, err) |
285 |
return fmt.Errorf("%sfailed to open file %q: %w%s", colorRed, substitutedPath, err, colorReset) |
| 285 |
} |
286 |
} |
| 286 |
defer file.Close() |
287 |
defer file.Close() |
| 287 |
|
288 |
|
| 288 |
part, err := writer.CreateFormFile(field, substitutedPath) |
289 |
part, err := writer.CreateFormFile(field, substitutedPath) |
| 289 |
if err != nil { |
290 |
if err != nil { |
| 290 |
return fmt.Errorf("failed to create form file for %q: %w", field, err) |
291 |
return fmt.Errorf("%sfailed to create form file for %q: %w%s", colorRed, field, err, colorReset) |
| 291 |
} |
292 |
} |
| 292 |
_, _ = io.Copy(part, file) |
293 |
_, _ = io.Copy(part, file) |
| 293 |
} |
294 |
} |
| ... |
| 307 |
|
308 |
|
| 308 |
httpReq, err := http.NewRequest(req.Method, rawURL, bodyReader) |
309 |
httpReq, err := http.NewRequest(req.Method, rawURL, bodyReader) |
| 309 |
if err != nil { |
310 |
if err != nil { |
| 310 |
return fmt.Errorf("failed to create HTTP request: %w", err) |
311 |
return fmt.Errorf("%sfailed to create HTTP request: %w%s", colorRed, err, colorReset) |
| 311 |
} |
312 |
} |
| 312 |
|
313 |
|
| 313 |
if contentType != "" { |
314 |
if contentType != "" { |
| ... |
| 321 |
startTime := time.Now() |
322 |
startTime := time.Now() |
| 322 |
resp, err := r.HTTPClient.Do(httpReq) |
323 |
resp, err := r.HTTPClient.Do(httpReq) |
| 323 |
if err != nil { |
324 |
if err != nil { |
| 324 |
return fmt.Errorf("request failed: %w", err) |
325 |
if os.IsTimeout(err) { |
|
|
326 |
return fmt.Errorf("%srequest timed out after %v%s", colorRed, r.HTTPClient.Timeout, colorReset) |
|
|
327 |
} |
|
|
328 |
return fmt.Errorf("%srequest failed: %w%s", colorRed, err, colorReset) |
| 325 |
} |
329 |
} |
| 326 |
duration := time.Since(startTime) |
330 |
duration := time.Since(startTime) |
| 327 |
defer resp.Body.Close() |
331 |
defer resp.Body.Close() |
| ... |
| 344 |
|
348 |
|
| 345 |
respData, err := io.ReadAll(resp.Body) |
349 |
respData, err := io.ReadAll(resp.Body) |
| 346 |
if err != nil { |
350 |
if err != nil { |
| 347 |
return fmt.Errorf("failed to read response body: %w", err) |
351 |
return fmt.Errorf("%sfailed to read response body: %w%s", colorRed, err, colorReset) |
| 348 |
} |
352 |
} |
| 349 |
|
353 |
|
| 350 |
if len(respData) > 0 { |
354 |
if len(respData) > 0 { |
| ... |