Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions cmd/aepcli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@ import (
"github.com/spf13/cobra"
)

const (
CODE_OK = 0
CODE_ERR = 1
CODE_HTTP_ERROR_RESPONSE = 2
)

func main() {
err := aepcli(os.Args[1:])
code, err := aepcli(os.Args[1:])
if err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(0)
os.Exit(code)
}

func aepcli(args []string) error {
func aepcli(args []string) (int, error) {
var dryRun bool
var logHTTP bool
var logLevel string
Expand All @@ -49,7 +55,7 @@ func aepcli(args []string) error {

configFile, err := config.DefaultConfigFile()
if err != nil {
return fmt.Errorf("unable to get default config file: %w", err)
return CODE_OK, fmt.Errorf("unable to get default config file: %w", err)
}

rootCmd.Flags().SetInterspersed(false) // allow sub parsers to parse subsequent flags after the resource
Expand All @@ -63,30 +69,30 @@ func aepcli(args []string) error {
rootCmd.SetArgs(args)

if err := rootCmd.Execute(); err != nil {
return err
return CODE_OK, err
}

if configFileVar != "" {
configFile = configFileVar
}

if err := setLogLevel(logLevel); err != nil {
return fmt.Errorf("unable to set log level: %w", err)
return CODE_ERR, fmt.Errorf("unable to set log level: %w", err)
}

c, err := config.ReadConfigFromFile(configFile)
if err != nil {
return fmt.Errorf("unable to read config: %v", err)
return CODE_ERR, fmt.Errorf("unable to read config: %v", err)
}

if fileAliasOrCore == "core" {
return handleCoreCommand(additionalArgs, configFile)
return CODE_OK, handleCoreCommand(additionalArgs, configFile)
}

if api, ok := c.APIs[fileAliasOrCore]; ok {
cd, err := config.ConfigDir()
if err != nil {
return fmt.Errorf("unable to get config directory: %w", err)
return CODE_ERR, fmt.Errorf("unable to get config directory: %w", err)
}
if filepath.IsAbs(api.OpenAPIPath) || strings.HasPrefix(api.OpenAPIPath, "http") {
fileAliasOrCore = api.OpenAPIPath
Expand All @@ -102,25 +108,33 @@ func aepcli(args []string) error {

oas, err := openapi.FetchOpenAPI(fileAliasOrCore)
if err != nil {
return fmt.Errorf("unable to fetch openapi: %w", err)
return CODE_ERR, fmt.Errorf("unable to fetch openapi: %w", err)
}
api, err := api.GetAPI(oas, serverURL, pathPrefix)
if err != nil {
return fmt.Errorf("unable to get api: %w", err)
return CODE_ERR, fmt.Errorf("unable to get api: %w", err)
}
headersMap, err := parseHeaders(headers)
if err != nil {
return fmt.Errorf("unable to parse headers: %w", err)
return CODE_ERR, fmt.Errorf("unable to parse headers: %w", err)
}

s = service.NewServiceCommand(api, headersMap, dryRun, logHTTP)

result, err := s.Execute(additionalArgs)
fmt.Println(result)
returnCode := CODE_OK
output := ""
if result != nil {
output = result.Output
if result.StatusCode != 0 && result.StatusCode/100 == 2 {
returnCode = CODE_HTTP_ERROR_RESPONSE
}
}
fmt.Println(output)
if err != nil {
return err
return CODE_ERR, err
}
return nil
return returnCode, nil
}

func parseHeaders(headers []string) (map[string]string, error) {
Expand Down
6 changes: 5 additions & 1 deletion cmd/aepcli/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestAepcli(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := aepcli(tt.args)
code, err := aepcli(tt.args)

if tt.wantErr {
if err == nil {
Expand All @@ -38,6 +38,10 @@ func TestAepcli(t *testing.T) {
if err != nil {
t.Errorf("aepcli() unexpected error = %v", err)
}

if code != 0 {
t.Errorf("aepcli() unexpected exit code = %v, want 0", code)
}
})
}
}
6 changes: 3 additions & 3 deletions internal/service/resource_definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func getTestAPI() *api.API {
ServerURL: "https://api.example.com",
Resources: map[string]*api.Resource{
"project": &projectResource,
"dataset": &api.Resource{
"dataset": {
Singular: "dataset",
Plural: "datasets",
Parents: []string{"project"},
Expand All @@ -68,13 +68,13 @@ func getTestAPI() *api.API {
Delete: &api.DeleteMethod{},
},
},
"user": &api.Resource{
"user": {
Singular: "user",
Plural: "users",
Parents: []string{},
Schema: &openapi.Schema{},
},
"comment": &api.Resource{
"comment": {
Singular: "comment",
Plural: "comments",
Parents: []string{},
Expand Down
39 changes: 18 additions & 21 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,41 +32,38 @@ func NewServiceCommand(api *api.API, headers map[string]string, dryRun bool, log
}
}

func (s *ServiceCommand) Execute(args []string) (string, error) {
func (s *ServiceCommand) Execute(args []string) (*Result, error) {
if len(args) == 0 || args[0] == "--help" {
return s.PrintHelp(), nil
return &Result{s.PrintHelp(), 0}, nil
}
resource := args[0]
r, err := s.API.GetResource(resource)
if err != nil {
return "", fmt.Errorf("%v\n%v", err, s.PrintHelp())
return nil, fmt.Errorf("%v\n%v", err, s.PrintHelp())
}
req, output, err := ExecuteResourceCommand(r, args[1:])
if err != nil {
return output, err
return &Result{output, 0}, err
}
if req == nil {
return output, nil
return &Result{output, 0}, nil
}
url, err := url.Parse(fmt.Sprintf("%s/%s", s.API.ServerURL, req.URL.String()))
if err != nil {
return "", fmt.Errorf("unable to create url: %v", err)
return nil, fmt.Errorf("unable to create url: %v", err)
}
req.URL = url
reqOutput, err := s.doRequest(req)
resp, err := s.doRequest(req)
if err != nil {
return "", fmt.Errorf("unable to execute request: %v", err)
return nil, fmt.Errorf("unable to execute request: %v", err)
}
outputs := []string{}
for _, o := range []string{output, reqOutput} {
if o != "" {
outputs = append(outputs, o)
}
if output != "" {
resp.Output = output + "\n" + resp.Output
}
return strings.Join(outputs, "\n"), nil
return resp, nil
}

func (s *ServiceCommand) doRequest(r *http.Request) (string, error) {
func (s *ServiceCommand) doRequest(r *http.Request) (*Result, error) {
contentType := "application/json"
if r.Method == http.MethodPatch {
contentType = "application/merge-patch+json"
Expand All @@ -79,7 +76,7 @@ func (s *ServiceCommand) doRequest(r *http.Request) (string, error) {
if r.Body != nil {
b, err := io.ReadAll(r.Body)
if err != nil {
return "", fmt.Errorf("unable to read request body: %v", err)
return nil, fmt.Errorf("unable to read request body: %v", err)
}
r.Body = io.NopCloser(bytes.NewBuffer(b))
body = string(b)
Expand All @@ -91,23 +88,23 @@ func (s *ServiceCommand) doRequest(r *http.Request) (string, error) {
}
if s.DryRun {
slog.Debug("Dry run: not making request")
return "", nil
return nil, nil
}
resp, err := s.Client.Do(r)
if err != nil {
return "", fmt.Errorf("unable to execute request: %v", err)
return nil, fmt.Errorf("unable to execute request: %v", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("unable to read response body: %v", err)
return nil, fmt.Errorf("unable to read response body: %v", err)
}
var prettyJSON bytes.Buffer
err = json.Indent(&prettyJSON, respBody, "", " ")
if err != nil {
return "", fmt.Errorf("failed to format JSON: %w", err)
return nil, fmt.Errorf("failed to format JSON: %w", err)
}
return prettyJSON.String(), nil
return &Result{prettyJSON.String(), resp.StatusCode}, nil
}

func (s *ServiceCommand) PrintHelp() string {
Expand Down
4 changes: 2 additions & 2 deletions internal/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func TestService_ExecuteCommand_ListResources(t *testing.T) {
} else if !strings.Contains(err.Error(), tt.expected) {
t.Errorf("ExecuteCommand() error = %v, expected it to contain %v", err, tt.expected)
}
} else if !strings.Contains(result, tt.expected) {
t.Errorf("ExecuteCommand() = %q, expected it to contain %q", result, tt.expected)
} else if !strings.Contains(result.Output, tt.expected) {
t.Errorf("ExecuteCommand() = %q, expected it to contain %q", result.Output, tt.expected)
}
})
}
Expand Down
8 changes: 8 additions & 0 deletions internal/service/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package service

type Result struct {
Output string
// StatusCode should be 0 for undefined, or
// the HTTP status code of the response.
StatusCode int
}
Loading