diff --git a/.gitignore b/.gitignore index 60e1354b..bba1f9e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ bin .env dist/ epcc-cli -epcc \ No newline at end of file +epcc +profiles \ No newline at end of file diff --git a/cmd/commercemanager.go b/cmd/commercemanager.go index 0bfc0007..95f4edeb 100644 --- a/cmd/commercemanager.go +++ b/cmd/commercemanager.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/elasticpath/epcc-cli/config" + "github.com/elasticpath/epcc-cli/shared" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "net/url" @@ -28,7 +29,7 @@ var cmCommand = &cobra.Command{ if cmUrl == "" { return fmt.Errorf("Don't know where Commerce Manager is for $EPCC_API_BASE_URL=%s \n", u) } - err = OpenUrl(cmUrl) + err = shared.OpenUrl(cmUrl) if err != nil { return err } diff --git a/cmd/create.go b/cmd/create.go index d43d4b02..072831db 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -76,7 +76,6 @@ var create = &cobra.Command{ } aliases.SaveAliasesForResources(string(resBody)) - return json.PrintJson(string(resBody)) }, diff --git a/cmd/docs.go b/cmd/docs.go index eab184ea..f5612615 100644 --- a/cmd/docs.go +++ b/cmd/docs.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/elasticpath/epcc-cli/external/completion" "github.com/elasticpath/epcc-cli/external/resources" + "github.com/elasticpath/epcc-cli/shared" "github.com/spf13/cobra" ) @@ -51,32 +52,32 @@ func openDoc(resourceDoc resources.Resource, verb string) error { if len(resourceDoc.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.Docs) + err = shared.OpenUrl(resourceDoc.Docs) case "get-collection": if resourceDoc.GetCollectionInfo != nil && len(resourceDoc.GetCollectionInfo.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.GetCollectionInfo.Docs) + err = shared.OpenUrl(resourceDoc.GetCollectionInfo.Docs) case "get": if resourceDoc.GetEntityInfo != nil && len(resourceDoc.GetEntityInfo.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.GetEntityInfo.Docs) + err = shared.OpenUrl(resourceDoc.GetEntityInfo.Docs) case "update": if resourceDoc.UpdateEntityInfo != nil && len(resourceDoc.UpdateEntityInfo.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.UpdateEntityInfo.Docs) + err = shared.OpenUrl(resourceDoc.UpdateEntityInfo.Docs) case "delete": if resourceDoc.DeleteEntityInfo != nil && len(resourceDoc.DeleteEntityInfo.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.DeleteEntityInfo.Docs) + err = shared.OpenUrl(resourceDoc.DeleteEntityInfo.Docs) case "create": if resourceDoc.CreateEntityInfo != nil && len(resourceDoc.CreateEntityInfo.Docs) < 1 { err = doDefault() } - err = OpenUrl(resourceDoc.CreateEntityInfo.Docs) + err = shared.OpenUrl(resourceDoc.CreateEntityInfo.Docs) default: return fmt.Errorf("Could not find verb %s", verb) diff --git a/cmd/logs.go b/cmd/logs.go new file mode 100644 index 00000000..b8683d60 --- /dev/null +++ b/cmd/logs.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "fmt" + "github.com/elasticpath/epcc-cli/shared" + "github.com/spf13/cobra" + "os" + "strings" +) + +var logsClear = &cobra.Command{ + Use: "clear", + Short: "Clears all Http logs", + RunE: func(cmd *cobra.Command, args []string) error { + os.RemoveAll(shared.LogDirectory) + return nil + }, +} + +var logsList = &cobra.Command{ + Use: "list", + Short: "List All Http logs", + RunE: func(cmd *cobra.Command, args []string) error { + files := shared.AllFilesSortedByDate(shared.LogDirectory) + for i := 0; i < len(files); i++ { + name, _ := shared.Base64DecodeStripped(files[i].Name()) + fmt.Println(name) + } + return nil + }, +} + +var logsShow = &cobra.Command{ + Use: "show ", + Short: "Show Http logs for specific number", + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + files := shared.AllFilesSortedByDate(shared.LogDirectory) + for i := 0; i < len(files); i++ { + name, _ := shared.Base64DecodeStripped(files[i].Name()) + segments := strings.Split(name, " ") + if segments[0] == args[0] { + fmt.Println(name) + break + } + } + + return nil + }, +} + +var logs = &cobra.Command{Use: "logs"} diff --git a/cmd/root.go b/cmd/root.go index cc6c6759..0db4367d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,9 +30,11 @@ func init() { create, delete, update, + logs, resourceListCommand, aliasesCmd, ) + logs.AddCommand(logsList, logsShow, logsClear) testJson.Flags().BoolVarP(&noWrapping, "no-wrapping", "", false, "if set, we won't wrap the output the json in a data tag") testJson.Flags().BoolVarP(&compliant, "compliant", "", false, "if set, we wrap most keys in an attributes tage automatically.") diff --git a/cmd/share.go b/cmd/share.go deleted file mode 100644 index 52a39308..00000000 --- a/cmd/share.go +++ /dev/null @@ -1,23 +0,0 @@ -package cmd - -import ( - "fmt" - "os/exec" - "runtime" -) - -func OpenUrl(cmUrl string) error { - - switch runtime.GOOS { - case "linux": - exec.Command("xdg-open", cmUrl).Start() - case "windows": - exec.Command("rundll32", "url.dll,FileProtocolHandler", cmUrl).Start() - case "darwin": - exec.Command("open", cmUrl).Start() - default: - return fmt.Errorf("unsupported platform") - } - - return nil -} diff --git a/external/httpclient/httpclient.go b/external/httpclient/httpclient.go index 6a40861b..70460b7d 100644 --- a/external/httpclient/httpclient.go +++ b/external/httpclient/httpclient.go @@ -9,12 +9,17 @@ import ( "github.com/elasticpath/epcc-cli/external/json" "github.com/elasticpath/epcc-cli/external/version" "github.com/elasticpath/epcc-cli/globals" + "github.com/elasticpath/epcc-cli/shared" log "github.com/sirupsen/logrus" "io" + "io/fs" "io/ioutil" "mime/multipart" "net/http" + "net/http/httputil" "net/url" + "os" + "strconv" "strings" "time" ) @@ -92,6 +97,17 @@ func doRequestInternal(ctx context.Context, method string, contentType string, p } else if resp.StatusCode >= 200 && resp.StatusCode <= 399 { log.Infof("%s %s ==> %s %s", method, reqURL.String(), resp.Proto, resp.Status) } + dumpReq, err := httputil.DumpRequestOut(req, true) + if err != nil { + log.Error(err) + } + + dumpRes, err := httputil.DumpResponse(resp, true) + if err != nil { + log.Error(err) + } + + logToDisk(method, path, dumpReq, dumpRes, resp.StatusCode) return resp, err } @@ -135,3 +151,38 @@ func AddHeaderByFlag(r *http.Request) error { } return nil } + +func logToDisk(requestMethod string, requestPath string, requestBytes []byte, responseBytes []byte, responseCode int) error { + os.Mkdir(shared.LogDirectory, os.ModePerm) + var logNumber = 1 + lastFile := getLastFile(shared.LogDirectory) + if lastFile != nil { + decodedFileNAme, err := shared.Base64DecodeStripped((*lastFile).Name()) + if err != nil { + return err + } + + fileNameParts := strings.Split(decodedFileNAme, " ") + logNumber, _ = strconv.Atoi(fileNameParts[0]) + logNumber++ + } + + filename := shared.Base64EncodeStripped(fmt.Sprintf("%d %s %s ==> %d", logNumber, requestMethod, requestPath, responseCode)) + f, err := os.Create(fmt.Sprintf("%s/%s", shared.LogDirectory, filename)) + if err != nil { + return err + } + defer f.Close() + f.Write(requestBytes) + f.Write([]byte("\n")) + f.Write(responseBytes) + return nil +} + +func getLastFile(logDirectory string) *fs.FileInfo { + all := shared.AllFilesSortedByDate(logDirectory) + if len(all) >= 1 { + return &all[len(all)-1] + } + return nil +} diff --git a/shared/share.go b/shared/share.go new file mode 100644 index 00000000..06a90cf0 --- /dev/null +++ b/shared/share.go @@ -0,0 +1,51 @@ +package shared + +import ( + b64 "encoding/base64" + "fmt" + "io/fs" + "io/ioutil" + "os/exec" + "runtime" + "sort" + "strings" +) + +const LogDirectory = "profiles" + +func OpenUrl(cmUrl string) error { + switch runtime.GOOS { + case "linux": + exec.Command("xdg-open", cmUrl).Start() + case "windows": + exec.Command("rundll32", "url.dll,FileProtocolHandler", cmUrl).Start() + case "darwin": + exec.Command("open", cmUrl).Start() + default: + return fmt.Errorf("unsupported platform") + } + + return nil +} + +func AllFilesSortedByDate(logDirectory string) []fs.FileInfo { + files, _ := ioutil.ReadDir(logDirectory) + sort.Slice(files, func(i, j int) bool { + return files[i].ModTime().Before(files[j].ModTime()) + }) + + return files +} + +func Base64EncodeStripped(s string) string { + encoded := b64.StdEncoding.EncodeToString([]byte(s)) + return strings.TrimRight(encoded, "=") +} + +func Base64DecodeStripped(s string) (string, error) { + if i := len(s) % 4; i != 0 { + s += strings.Repeat("=", 4-i) + } + decoded, err := b64.StdEncoding.DecodeString(s) + return string(decoded), err +}