Skip to content

Commit f6c1159

Browse files
committed
feat: add --log-http and --dry-run
Enable the ability to just log http requests, along with a dry run functionality. This allows for crafting HTTP requests with aepcli, as well as auditing what aepcli is doing.
1 parent efefce9 commit f6c1159

6 files changed

Lines changed: 51 additions & 19 deletions

File tree

cmd/aepcli/main.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ func main() {
2525
}
2626

2727
func aepcli(args []string) error {
28+
var dryRun bool
29+
var logHTTP bool
2830
var logLevel string
2931
var fileAliasOrCore string
3032
var additionalArgs []string
3133
var headers []string
3234
var pathPrefix string
3335
var serverURL string
3436
var configFileVar string
35-
var s *service.Service
37+
var s *service.ServiceCommand
3638

3739
rootCmd := &cobra.Command{
3840
Use: "aepcli [host or api alias] [resource or --help]",
@@ -53,6 +55,8 @@ func aepcli(args []string) error {
5355
rootCmd.Flags().SetInterspersed(false) // allow sub parsers to parse subsequent flags after the resource
5456
rootCmd.PersistentFlags().StringArrayVar(&headers, "header", []string{}, "Specify headers in the format key=value")
5557
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Set the logging level (debug, info, warn, error)")
58+
rootCmd.PersistentFlags().BoolVar(&logHTTP, "log-http", false, "Set to true to log HTTP requests. This can be helpful when attempting to write your own code or debug.")
59+
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Set to true to not make any changes. This can be helpful when paired with log-http to just view http requests instead of perform them.")
5660
rootCmd.PersistentFlags().StringVar(&pathPrefix, "path-prefix", "", "Specify a path prefix that is prepended to all paths in the openapi schema. This will strip them when evaluating the resource hierarchy paths.")
5761
rootCmd.PersistentFlags().StringVar(&serverURL, "server-url", "", "Specify a URL to use for the server. If not specified, the first server URL in the OpenAPI definition will be used.")
5862
rootCmd.PersistentFlags().StringVar(&configFileVar, "config", "", "Path to config file")
@@ -109,9 +113,9 @@ func aepcli(args []string) error {
109113
return fmt.Errorf("unable to parse headers: %w", err)
110114
}
111115

112-
s = service.NewService(api, headersMap)
116+
s = service.NewServiceCommand(api, headersMap, dryRun, logHTTP)
113117

114-
result, err := s.ExecuteCommand(additionalArgs)
118+
result, err := s.Execute(additionalArgs)
115119
fmt.Println(result)
116120
if err != nil {
117121
return err

docs/userguide.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,19 @@ lists are specified as a comma-separated list:
178178
aepcli bookstore book-edition create --book "peter-pan" --publisher "consistent-house" --tags "fantasy,childrens"
179179
```
180180

181+
### Logging HTTP requests and Dry Runs
182+
183+
aepcli supports logging http requests and dry runs. To log http requests, use the
184+
`--log-http` flag. To perform a dry run, use the `--dry-run` flag.
185+
186+
Combined, they can be useful for creating HTTP calls without having to craft the
187+
HTTP request by hand:
188+
189+
```bash
190+
aepcli --dry-run --log-http bookstore book --publisher=standard-house get foo
191+
Request: GET http://localhost:8081/publishers/standard-house/books/foo
192+
```
193+
181194
### core commands
182195

183196
See `aepcli core --help` for commands for aepcli (e.g. config)

internal/service/resource_definition.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func ExecuteResourceCommand(r *api.Resource, args []string) (*http.Request, stri
2525
var flagValue string
2626
parents = append(parents, &flagValue)
2727
c.PersistentFlags().StringVar(&flagValue, flagName, "", fmt.Sprintf("The %v of the resource", flagName))
28+
c.MarkPersistentFlagRequired(flagName)
2829
i += 2
2930
}
3031

@@ -47,10 +48,10 @@ func ExecuteResourceCommand(r *api.Resource, args []string) (*http.Request, stri
4748

4849
if r.CreateMethod != nil {
4950
use := "create [id]"
50-
args := cobra.ExactArgs(0)
51+
args := cobra.ExactArgs(1)
5152
if !r.CreateMethod.SupportsUserSettableCreate {
5253
use = "create"
53-
args = cobra.ExactArgs(1)
54+
args = cobra.ExactArgs(0)
5455
}
5556
createArgs := map[string]interface{}{}
5657
createCmd := &cobra.Command{

internal/service/resource_definition_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ var projectResource = api.Resource{
3939
},
4040
Required: []string{"name"},
4141
},
42-
GetMethod: &api.GetMethod{},
43-
ListMethod: &api.ListMethod{},
44-
CreateMethod: &api.CreateMethod{},
42+
GetMethod: &api.GetMethod{},
43+
ListMethod: &api.ListMethod{},
44+
CreateMethod: &api.CreateMethod{
45+
SupportsUserSettableCreate: true,
46+
},
4547
UpdateMethod: &api.UpdateMethod{},
4648
DeleteMethod: &api.DeleteMethod{},
4749
}

internal/service/service.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,25 @@ import (
1414
"github.com/aep-dev/aep-lib-go/pkg/api"
1515
)
1616

17-
type Service struct {
17+
type ServiceCommand struct {
1818
API api.API
1919
Headers map[string]string
20+
DryRun bool
21+
LogHTTP bool
2022
Client *http.Client
2123
}
2224

23-
func NewService(api *api.API, headers map[string]string) *Service {
24-
return &Service{
25+
func NewServiceCommand(api *api.API, headers map[string]string, dryRun bool, logHTTP bool) *ServiceCommand {
26+
return &ServiceCommand{
2527
API: *api,
2628
Headers: headers,
29+
DryRun: dryRun,
30+
LogHTTP: logHTTP,
2731
Client: &http.Client{},
2832
}
2933
}
3034

31-
func (s *Service) ExecuteCommand(args []string) (string, error) {
35+
func (s *ServiceCommand) Execute(args []string) (string, error) {
3236
if len(args) == 0 || args[0] == "--help" {
3337
return s.PrintHelp(), nil
3438
}
@@ -56,7 +60,7 @@ func (s *Service) ExecuteCommand(args []string) (string, error) {
5660
return strings.Join([]string{output, reqOutput}, "\n"), nil
5761
}
5862

59-
func (s *Service) doRequest(r *http.Request) (string, error) {
63+
func (s *ServiceCommand) doRequest(r *http.Request) (string, error) {
6064
r.Header.Set("Content-Type", "application/json")
6165
for k, v := range s.Headers {
6266
r.Header.Set(k, v)
@@ -70,10 +74,18 @@ func (s *Service) doRequest(r *http.Request) (string, error) {
7074
r.Body = io.NopCloser(bytes.NewBuffer(b))
7175
body = string(b)
7276
}
73-
slog.Debug(fmt.Sprintf("Request: %s %s\n%s", r.Method, r.URL.String(), string(body)))
77+
requestLog := fmt.Sprintf("Request: %s %s\n%s", r.Method, r.URL.String(), string(body))
78+
slog.Debug(requestLog)
79+
if s.LogHTTP {
80+
fmt.Println(requestLog)
81+
}
82+
if s.DryRun {
83+
slog.Debug("Dry run: not making request")
84+
return "", nil
85+
}
7486
resp, err := s.Client.Do(r)
7587
if err != nil {
76-
return "", err
88+
return "", fmt.Errorf("unable to execute request: %v", err)
7789
}
7890
defer resp.Body.Close()
7991
respBody, err := io.ReadAll(resp.Body)
@@ -88,7 +100,7 @@ func (s *Service) doRequest(r *http.Request) (string, error) {
88100
return prettyJSON.String(), nil
89101
}
90102

91-
func (s *Service) PrintHelp() string {
103+
func (s *ServiceCommand) PrintHelp() string {
92104
var resources []string
93105
for singular := range s.API.Resources {
94106
resources = append(resources, singular)

internal/service/service_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import (
99

1010
func TestService_ExecuteCommand_ListResources(t *testing.T) {
1111
// Test setup
12-
svc := NewService(&api.API{
12+
svc := NewServiceCommand(&api.API{
1313
ServerURL: "http://test.com",
1414
Resources: map[string]*api.Resource{
1515
"project": &projectResource,
1616
"user": {},
1717
"post": {},
1818
"comment": {},
1919
},
20-
}, nil)
20+
}, nil, false, false)
2121

2222
tests := []struct {
2323
name string
@@ -55,7 +55,7 @@ func TestService_ExecuteCommand_ListResources(t *testing.T) {
5555

5656
for _, tt := range tests {
5757
t.Run(tt.name, func(t *testing.T) {
58-
result, err := svc.ExecuteCommand(tt.args)
58+
result, err := svc.Execute(tt.args)
5959
if err != nil {
6060
if !tt.expectAsError {
6161
t.Errorf("ExecuteCommand() error = %v, expected no error", err)

0 commit comments

Comments
 (0)