From d5c79fcfd965b0b7859eec38781fecb87ff6e9ae Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Wed, 21 Aug 2024 12:23:39 -0700 Subject: [PATCH] WIP #458 --- cmd/create.go | 6 +- cmd/delete-all.go | 14 +- cmd/delete.go | 4 +- cmd/get-all.go | 289 ++++++++++++++++++ cmd/get.go | 6 +- cmd/helper.go | 4 + cmd/login.go | 4 +- cmd/reset-store.go | 2 +- cmd/root.go | 3 + cmd/test-json.go | 2 +- cmd/update.go | 6 +- external/apihelper/get_all_ids.go | 9 +- .../map_collection_response_to_ids.go | 13 +- external/id/idable_attributes.go | 8 +- external/json/encoder.go | 35 +-- external/json/print_json.go | 39 ++- .../runbooks/commerce-extensions.epcc.yml | 22 ++ go.mod | 2 + go.sum | 6 + 19 files changed, 410 insertions(+), 64 deletions(-) create mode 100644 cmd/get-all.go create mode 100644 external/runbooks/commerce-extensions.epcc.yml diff --git a/cmd/create.go b/cmd/create.go index 3d404fa..4ed631d 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -125,7 +125,7 @@ func NewCreateCommand(parentCmd *cobra.Command) func() { return err } - err = json.PrintJson(string(outputJson)) + err = json.PrintJsonToStdout(string(outputJson)) if err != nil { return err @@ -146,7 +146,7 @@ func NewCreateCommand(parentCmd *cobra.Command) func() { } } - return json.PrintJson(body) + return json.PrintJsonToStdout(body) } } @@ -343,7 +343,7 @@ func createInternal(ctx context.Context, overrides *httpclient.HttpParameterOver // Check if error response if resp.StatusCode >= 400 && resp.StatusCode <= 600 { - json.PrintJson(string(resBody)) + json.PrintJsonToStdout(string(resBody)) return "", fmt.Errorf(resp.Status) } diff --git a/cmd/delete-all.go b/cmd/delete-all.go index f7d72ff..e6592f2 100644 --- a/cmd/delete-all.go +++ b/cmd/delete-all.go @@ -103,7 +103,19 @@ func deleteAllInternal(ctx context.Context, args []string) error { return err } - ids, totalCount, err := apihelper.GetResourceIdsFromHttpResponse(resp) + if resp.StatusCode >= 400 { + log.Warnf("Could not retrieve page of data, aborting") + break + } + + bodyTxt, err := io.ReadAll(resp.Body) + + if err != nil { + return err + } + + ids, totalCount, err := apihelper.GetResourceIdsFromHttpResponse(bodyTxt) + resp.Body.Close() allIds := make([][]id.IdableAttributes, 0) diff --git a/cmd/delete.go b/cmd/delete.go index eaafd5e..04bd531 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -99,7 +99,7 @@ func NewDeleteCommand(parentCmd *cobra.Command) func() { if err != nil { if body != "" { if !noBodyPrint { - json.PrintJson(body) + json.PrintJsonToStdout(body) } } return err @@ -108,7 +108,7 @@ func NewDeleteCommand(parentCmd *cobra.Command) func() { if noBodyPrint { return nil } else { - return json.PrintJson(body) + return json.PrintJsonToStdout(body) } } diff --git a/cmd/get-all.go b/cmd/get-all.go new file mode 100644 index 0000000..7e74a5b --- /dev/null +++ b/cmd/get-all.go @@ -0,0 +1,289 @@ +package cmd + +import ( + "context" + gojson "encoding/json" + "fmt" + "github.com/elasticpath/epcc-cli/external/apihelper" + "github.com/elasticpath/epcc-cli/external/httpclient" + "github.com/elasticpath/epcc-cli/external/id" + "github.com/elasticpath/epcc-cli/external/json" + "os" + "sync" + + "github.com/elasticpath/epcc-cli/external/resources" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/thediveo/enumflag" + "io" + "net/url" + "reflect" + "strconv" +) + +type OutputFormat enumflag.Flag + +const ( + Jsonl OutputFormat = iota + Csv + EpccCli +) + +var OutputFormatIds = map[OutputFormat][]string{ + Jsonl: {"jsonl"}, + Csv: {"csv"}, + EpccCli: {"epcc-cli"}, +} + +func NewGetAllCommand(parentCmd *cobra.Command) func() { + + var getAll = &cobra.Command{ + Use: "get-all", + Short: "Get all of a resource", + SilenceUsage: false, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return fmt.Errorf("please specify a resource, epcc get-all [RESOURCE], see epcc delete-all --help") + } else { + return fmt.Errorf("invalid resource [%s] specified, see all with epcc delete-all --help", args[0]) + } + }, + } + + for _, resource := range resources.GetPluralResources() { + if resource.GetCollectionInfo == nil { + continue + } + + resourceName := resource.PluralName + + var outputFile string + var outputFormat OutputFormat + + var getAllResourceCmd = &cobra.Command{ + Use: resourceName, + Short: GetGetAllShort(resource), + Hidden: false, + RunE: func(cmd *cobra.Command, args []string) error { + return getAllInternal(context.Background(), outputFormat, outputFile, append([]string{resourceName}, args...)) + }, + } + + getAllResourceCmd.Flags().StringVarP(&outputFile, "output-file", "", "", "The file to output results to") + + getAllResourceCmd.Flags().VarP( + enumflag.New(&outputFormat, "output-format", OutputFormatIds, enumflag.EnumCaseInsensitive), + "output-format", "", + "sets output format; can be 'jsonl', 'csv', 'epcc-cli'") + + getAll.AddCommand(getAllResourceCmd) + } + + parentCmd.AddCommand(getAll) + return func() {} + +} + +func getAllInternal(ctx context.Context, outputFormat OutputFormat, outputFile string, args []string) error { + // Find Resource + resource, ok := resources.GetResourceByName(args[0]) + if !ok { + return fmt.Errorf("could not find resource %s", args[0]) + } + + if resource.GetCollectionInfo == nil { + return fmt.Errorf("resource %s doesn't support GET collection", args[0]) + } + + allParentEntityIds, err := getParentIds(ctx, resource) + + if err != nil { + return fmt.Errorf("could not retrieve parent ids for for resource %s, error: %w", resource.PluralName, err) + } + + if len(allParentEntityIds) == 1 { + log.Debugf("Resource %s is a top level resource need to scan only one path to delete all resources", resource.PluralName) + } else { + log.Debugf("Resource %s is not a top level resource, need to scan %d paths to delete all resources", resource.PluralName, len(allParentEntityIds)) + } + + var syncGroup = sync.WaitGroup{} + + syncGroup.Add(1) + + type idableAttributesWithType struct { + id.IdableAttributes + Type string `yaml:"type,omitempty" json:"type,omitempty"` + EpccCliType string `yaml:"epcc_cli_type,omitempty" json:"epcc_cli_type,omitempty"` + } + + type msg struct { + txt []byte + id []idableAttributesWithType + } + var sendChannel = make(chan msg, 0) + + var writer io.Writer + if outputFile == "" { + writer = os.Stdout + } else { + file, err := os.Create(outputFile) + if err != nil { + panic(err) + } + defer file.Close() + writer = file + } + + outputWriter := func() { + defer syncGroup.Done() + + for msgs := 0; ; msgs++ { + select { + case result, ok := <-sendChannel: + + if !ok { + log.Debugf("Channel closed, we are done.") + return + } + var obj interface{} + err = gojson.Unmarshal(result.txt, &obj) + + if err != nil { + log.Errorf("Couldn't unmarshal JSON response %s due to error: %v", result, err) + continue + } + + newObjs, err := json.RunJQWithArray(".data[]", obj) + + if err != nil { + log.Errorf("Couldn't process response %s due to error: %v", result, err) + continue + } + + for _, newObj := range newObjs { + + wrappedObj := map[string]interface{}{ + "data": newObj, + "meta": map[string]interface{}{ + "_epcc_cli_parent_resources": result.id, + }, + } + + line, err := gojson.Marshal(&wrappedObj) + + if err != nil { + log.Errorf("Could not create JSON for %s, error: %v", line, err) + continue + } + + _, err = writer.Write(line) + + if err != nil { + log.Errorf("Could not save line %s, error: %v", line, err) + continue + } + + _, err = writer.Write([]byte{10}) + + if err != nil { + log.Errorf("Could not save line %s, error: %v", line, err) + continue + } + + } + + } + } + } + + go outputWriter() + + for _, parentEntityIds := range allParentEntityIds { + lastIds := make([][]id.IdableAttributes, 1) + for offset := 0; offset <= 10000; offset += 100 { + resourceURL, err := resources.GenerateUrlViaIdableAttributes(resource.GetCollectionInfo, parentEntityIds) + + if err != nil { + return err + } + + types, err := resources.GetSingularTypesOfVariablesNeeded(resource.GetCollectionInfo.Url) + + if err != nil { + return err + } + + params := url.Values{} + params.Add("page[limit]", "100") + params.Add("page[offset]", strconv.Itoa(offset)) + + resp, err := httpclient.DoRequest(ctx, "GET", resourceURL, params.Encode(), nil) + + if err != nil { + return err + } + + if resp.StatusCode >= 400 { + log.Warnf("Could not retrieve page of data, aborting") + + break + } + + bodyTxt, err := io.ReadAll(resp.Body) + + if err != nil { + + return err + } + + ids, totalCount, err := apihelper.GetResourceIdsFromHttpResponse(bodyTxt) + resp.Body.Close() + + allIds := make([][]id.IdableAttributes, 0) + for _, id := range ids { + allIds = append(allIds, append(parentEntityIds, id)) + } + + if reflect.DeepEqual(allIds, lastIds) { + log.Warnf("Data on the previous two pages did not change. Does this resource support pagination? Aborting export", resource.PluralName, len(allIds)) + + break + } else { + lastIds = allIds + } + + idsWithType := make([]idableAttributesWithType, len(types)) + + for i, t := range types { + idsWithType[i].IdableAttributes = parentEntityIds[i] + idsWithType[i].EpccCliType = t + idsWithType[i].Type = resources.MustGetResourceByName(t).JsonApiType + } + + sendChannel <- msg{ + bodyTxt, + idsWithType, + } + + if len(allIds) == 0 { + log.Infof("Total ids retrieved for %s in %s is %d, we are done", resource.PluralName, resourceURL, len(allIds)) + + break + } else { + if totalCount >= 0 { + log.Infof("Total number of %s in %s is %d", resource.PluralName, resourceURL, totalCount) + } else { + log.Infof("Total number %s in %s is unknown", resource.PluralName, resourceURL) + } + } + + } + } + + close(sendChannel) + + syncGroup.Wait() + + return nil +} diff --git a/cmd/get.go b/cmd/get.go index d39d9e8..2c67f08 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -193,7 +193,7 @@ func NewGetCommand(parentCmd *cobra.Command) func() { return err } - err = json.PrintJson(string(outputJson)) + err = json.PrintJsonToStdout(string(outputJson)) if err != nil { return err @@ -214,7 +214,7 @@ func NewGetCommand(parentCmd *cobra.Command) func() { } } - printError := json.PrintJson(body) + printError := json.PrintJsonToStdout(body) if retriesFailedError != nil { return retriesFailedError @@ -326,7 +326,7 @@ func getInternal(ctx context.Context, overrides *httpclient.HttpParameterOverrid // Check if error response if resp.StatusCode >= 400 && resp.StatusCode <= 600 { - json.PrintJson(string(body)) + json.PrintJsonToStdout(string(body)) return "", fmt.Errorf(resp.Status) } diff --git a/cmd/helper.go b/cmd/helper.go index 2c05dd4..f2292c9 100644 --- a/cmd/helper.go +++ b/cmd/helper.go @@ -243,6 +243,10 @@ func GetDeleteAllShort(resource resources.Resource) string { return fmt.Sprintf("Calls DELETE %s for every resource in GET %s", GetHelpResourceUrls(resource.DeleteEntityInfo.Url), GetHelpResourceUrls(resource.GetCollectionInfo.Url)) } +func GetGetAllShort(resource resources.Resource) string { + return fmt.Sprintf("Calls GET %s and iterates over all pages and parent resources (if applicable)", GetHelpResourceUrls(resource.GetCollectionInfo.Url)) +} + func GetGetLong(resourceName string, resourceUrl string, usageGetType string, completionVerb int, urlInfo *resources.CrudEntityInfo, resource resources.Resource) string { if DisableLongOutput { diff --git a/cmd/login.go b/cmd/login.go index 4dd30f4..3da550e 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -387,7 +387,7 @@ var loginCustomer = &cobra.Command{ authentication.SaveCustomerToken(*customerTokenResponse) - return json.PrintJson(body) + return json.PrintJsonToStdout(body) }, } @@ -609,6 +609,6 @@ var loginAccountManagement = &cobra.Command{ } jsonBody, _ := gojson.Marshal(selectedAccount) - return json.PrintJson(string(jsonBody)) + return json.PrintJsonToStdout(string(jsonBody)) }, } diff --git a/cmd/reset-store.go b/cmd/reset-store.go index 1cfcecf..a375ccf 100644 --- a/cmd/reset-store.go +++ b/cmd/reset-store.go @@ -206,7 +206,7 @@ func resetResourcesUndeletableResources(ctx context.Context, overrides *httpclie errors = append(errors, fmt.Errorf("error resetting %s: %v", resetCmd[0], err).Error()) } - err = json.PrintJson(body) + err = json.PrintJsonToStdout(body) if err != nil { errors = append(errors, fmt.Errorf("error resetting %s: %v", resetCmd[0], err).Error()) diff --git a/cmd/root.go b/cmd/root.go index e029948..e273b48 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -110,6 +110,9 @@ func InitializeCmd() { log.Tracef("Building Delete All Commands") NewDeleteAllCommand(RootCmd) + log.Tracef("Building Get All Commands") + NewGetAllCommand(RootCmd) + Logs.AddCommand(LogsList, LogsShow, LogsClear) testJson.ResetFlags() diff --git a/cmd/test-json.go b/cmd/test-json.go index da423a1..eec267e 100644 --- a/cmd/test-json.go +++ b/cmd/test-json.go @@ -15,7 +15,7 @@ var testJson = &cobra.Command{ res, err := json.ToJson(args, noWrapping, compliant, map[string]*resources.CrudEntityAttribute{}, true) if res != "" { - json.PrintJson(res) + json.PrintJsonToStdout(res) } return err diff --git a/cmd/update.go b/cmd/update.go index f09e6ca..1052d05 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -115,7 +115,7 @@ func NewUpdateCommand(parentCmd *cobra.Command) func() { return err } - err = json.PrintJson(string(outputJson)) + err = json.PrintJsonToStdout(string(outputJson)) if err != nil { return err @@ -136,7 +136,7 @@ func NewUpdateCommand(parentCmd *cobra.Command) func() { } } - return json.PrintJson(body) + return json.PrintJsonToStdout(body) } } @@ -282,7 +282,7 @@ func updateInternal(ctx context.Context, overrides *httpclient.HttpParameterOver // Check if error response if resp.StatusCode >= 400 && resp.StatusCode <= 600 { - json.PrintJson(string(resBody)) + json.PrintJsonToStdout(string(resBody)) return "", fmt.Errorf(resp.Status) } diff --git a/external/apihelper/get_all_ids.go b/external/apihelper/get_all_ids.go index 3fb4cb3..b1fdbc9 100644 --- a/external/apihelper/get_all_ids.go +++ b/external/apihelper/get_all_ids.go @@ -7,6 +7,7 @@ import ( "github.com/elasticpath/epcc-cli/external/id" "github.com/elasticpath/epcc-cli/external/resources" log "github.com/sirupsen/logrus" + "io" "net/url" "reflect" ) @@ -79,7 +80,13 @@ func GetAllIds(ctx context.Context, resource *resources.Resource) ([][]id.Idable return myEntityIds, err } - ids, _, err := GetResourceIdsFromHttpResponse(resp) + bodyTxt, err := io.ReadAll(resp.Body) + + if err != nil { + return nil, err + } + + ids, _, err := GetResourceIdsFromHttpResponse(bodyTxt) if reflect.DeepEqual(ids, lastPageIds) { log.Debugf("Resource %s does not seem to support pagination as we got the exact same set of ids back as the last page... breaking. This might happen if exactly a paginated number of records is returned", resource.PluralName) diff --git a/external/apihelper/map_collection_response_to_ids.go b/external/apihelper/map_collection_response_to_ids.go index d8f07be..3bde437 100644 --- a/external/apihelper/map_collection_response_to_ids.go +++ b/external/apihelper/map_collection_response_to_ids.go @@ -4,21 +4,12 @@ import ( json2 "encoding/json" "fmt" "github.com/elasticpath/epcc-cli/external/id" - log "github.com/sirupsen/logrus" - "io" - "net/http" ) -func GetResourceIdsFromHttpResponse(resp *http.Response) ([]id.IdableAttributes, int, error) { - // Read the body - body, err := io.ReadAll(resp.Body) - - if err != nil { - log.Fatal(err) - } +func GetResourceIdsFromHttpResponse(bodyTxt []byte) ([]id.IdableAttributes, int, error) { var jsonStruct = map[string]interface{}{} - err = json2.Unmarshal(body, &jsonStruct) + err := json2.Unmarshal(bodyTxt, &jsonStruct) if err != nil { return nil, 0, fmt.Errorf("response for get was not JSON: %w", err) } diff --git a/external/id/idable_attributes.go b/external/id/idable_attributes.go index 57e0d12..5c48ca9 100644 --- a/external/id/idable_attributes.go +++ b/external/id/idable_attributes.go @@ -1,8 +1,8 @@ package id type IdableAttributes struct { - Id string `yaml:"id"` - Slug string `yaml:"slug,omitempty"` - Sku string `yaml:"sku,omitempty"` - Code string `yaml:"code,omitempty"` + Id string `yaml:"id" json:"id"` + Slug string `yaml:"slug,omitempty" json:"slug,omitempty"` + Sku string `yaml:"sku,omitempty" json:"sku,omitempty"` + Code string `yaml:"code,omitempty" json:"code,omitempty"` } diff --git a/external/json/encoder.go b/external/json/encoder.go index 962bdf9..31c13cf 100644 --- a/external/json/encoder.go +++ b/external/json/encoder.go @@ -17,13 +17,14 @@ import ( // https://github.com/itchyny/gojq/blob/main/cli/color.go type encoder struct { - out io.Writer - w *bytes.Buffer - tab bool - indent int - depth int - buf [64]byte - keyStack []string + out io.Writer + w *bytes.Buffer + tab bool + monoOutput bool + indent int + depth int + buf [64]byte + keyStack []string } type colorInfo struct { @@ -31,8 +32,8 @@ type colorInfo struct { colorString string } -func setColor(buf *bytes.Buffer, color colorInfo) { - if !MonochromeOutput { +func (e *encoder) setColor(buf *bytes.Buffer, color colorInfo) { + if !e.monoOutput { buf.Write([]byte(color.colorString)) } } @@ -107,9 +108,9 @@ var ( objectColor = newColor("", "") // No color ) -func NewEncoder(tab bool, indent int) *encoder { +func NewEncoder(tab bool, indent int, monoOutput bool) *encoder { // reuse the buffer in multiple calls of marshal - return &encoder{w: new(bytes.Buffer), tab: tab, indent: indent} + return &encoder{w: new(bytes.Buffer), tab: tab, indent: indent, monoOutput: monoOutput} } func (e *encoder) Marshal(v interface{}, w io.Writer) error { @@ -182,7 +183,7 @@ func (e *encoder) encodeFloat64(f float64) { // ref: encodeState#string in encoding/json func (e *encoder) encodeString(s string, color *colorInfo) { if color != nil { - setColor(e.w, *color) + e.setColor(e.w, *color) } e.w.WriteByte('"') start := 0 @@ -236,7 +237,7 @@ func (e *encoder) encodeString(s string, color *colorInfo) { } e.w.WriteByte('"') if color != nil { - setColor(e.w, resetColor) + e.setColor(e.w, resetColor) } } @@ -378,9 +379,9 @@ func (e *encoder) writeByte(b byte, color *colorInfo) { if color == nil || color.colorString == "" { e.w.WriteByte(b) } else { - setColor(e.w, *color) + e.setColor(e.w, *color) e.w.WriteByte(b) - setColor(e.w, resetColor) + e.setColor(e.w, resetColor) } } @@ -388,8 +389,8 @@ func (e *encoder) write(bs []byte, color *colorInfo) { if color == nil || color.colorString == "" { e.w.Write(bs) } else { - setColor(e.w, *color) + e.setColor(e.w, *color) e.w.Write(bs) - setColor(e.w, resetColor) + e.setColor(e.w, resetColor) } } diff --git a/external/json/print_json.go b/external/json/print_json.go index 0b5a8f8..3eec0bc 100644 --- a/external/json/print_json.go +++ b/external/json/print_json.go @@ -12,15 +12,33 @@ import ( var MonochromeOutput = false -func PrintJson(json string) error { +func PrintJsonToStdout(json string) error { defer os.Stdout.Sync() - return printJsonToWriter(json, os.Stdout) + return printJsonToWriter(json, shouldPrintMonochrome(), os.Stdout) +} + +func shouldPrintMonochrome() bool { + m := MonochromeOutput + // Adapted from gojq + if !m && os.Getenv("TERM") == "dumb" { + m = true + } else { + colorCapableTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) + if !colorCapableTerminal { + m = true + } + } + return m +} + +func PrintJsonToWriter(json string, w io.Writer) error { + return printJsonToWriter(json, true, w) } func PrintJsonToStderr(json string) error { defer os.Stderr.Sync() - return printJsonToWriter(json, os.Stderr) + return printJsonToWriter(json, shouldPrintMonochrome(), os.Stderr) } func PrettyPrint(in string) string { @@ -32,28 +50,19 @@ func PrettyPrint(in string) string { return out.String() } -func printJsonToWriter(json string, w io.Writer) error { - // Adapted from gojq - if os.Getenv("TERM") == "dumb" { - MonochromeOutput = true - } else { - colorCapableTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) - if !colorCapableTerminal { - MonochromeOutput = true - } - } +func printJsonToWriter(json string, monoOutput bool, w io.Writer) error { var v interface{} err := gojson.Unmarshal([]byte(json), &v) - e := NewEncoder(false, 2) + e := NewEncoder(false, 0, monoOutput) done := make(chan bool, 1) defer close(done) - if !MonochromeOutput { + if !monoOutput { go func() { select { case <-done: diff --git a/external/runbooks/commerce-extensions.epcc.yml b/external/runbooks/commerce-extensions.epcc.yml new file mode 100644 index 0000000..4568b1f --- /dev/null +++ b/external/runbooks/commerce-extensions.epcc.yml @@ -0,0 +1,22 @@ +name: "commerce-extensions" +description: + long: "Examples for using Commerce Extensions" + short: "Examples for using Commerce Extensions" +actions: + multi-location-inventory: + description: + short: "An example of multi location inventory" + commands: + - | + epcc create custom-api name Location Description Location api_type location_ext slug location + epcc create custom-api name Inventory Description Inventory api_type inventory_ext slug inventory + + reset-multi-location-inventory: + description: + short: "Reset the store configuration" + ignore_errors: true + commands: + - | + epcc delete custom-api slug=location + epcc delete custom-api slug=inventory + diff --git a/go.mod b/go.mod index 90aeff9..cf58fcb 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,8 @@ require ( github.com/jolestar/go-commons-pool/v2 v2.1.2 ) +require github.com/yukithm/json2csv v0.1.2 // indirect + require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect diff --git a/go.sum b/go.sum index 4da37b4..bd180df 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,7 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -94,6 +95,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -159,6 +162,7 @@ github.com/thediveo/enumflag v0.10.1 h1:DB3Ag69VZ7BCv6jzKECrZ0ebZrHLzFRMIFYt96s4 github.com/thediveo/enumflag v0.10.1/go.mod h1:KyVhQUPzreSw85oJi2uSjFM0ODLKXBH0rPod7zc2pmI= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= @@ -166,6 +170,8 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yukithm/json2csv v0.1.2 h1:b2aIY9+TOY5Wss9lCku4wjqnQrENv5Ix1G0ZHN1FE2Q= +github.com/yukithm/json2csv v0.1.2/go.mod h1:Ul6ZenFV94YeUm08AqppOd+/hB9JsmiU4KXPs9ZvgwQ= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=