From 6c32c960018748ba391b3afbd08d3628312808bb Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 12:55:35 +0500 Subject: [PATCH 01/11] Exploring --- ipinfo/cmd_bulk_ip_asn.go | 14 ++++++++++++++ ipinfo/main.go | 2 ++ lib/cmd_bulk_ip_asn.go | 35 +++++++++++++++++++++++++++++++++++ lib/utils.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 ipinfo/cmd_bulk_ip_asn.go create mode 100644 lib/cmd_bulk_ip_asn.go create mode 100644 lib/utils.go diff --git a/ipinfo/cmd_bulk_ip_asn.go b/ipinfo/cmd_bulk_ip_asn.go new file mode 100644 index 00000000..3234a52c --- /dev/null +++ b/ipinfo/cmd_bulk_ip_asn.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/ipinfo/cli/lib" + "github.com/spf13/pflag" + "os" +) + +func cmdBulkIpAsn() error { + f := lib.CmdBulkIpAsnFlags{} + f.Init() + pflag.Parse() + return lib.CmdBulkIpAsn(f, os.Args[1:]) +} diff --git a/ipinfo/main.go b/ipinfo/main.go index c8426d59..2b5250a3 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -34,6 +34,8 @@ func main() { } switch { + case lib.StrArrIsCombinationOfIPsAndASNs(os.Args[1:]): + err = cmdBulkIpAsn() case lib.StrIsIPStr(cmd): err = cmdIP(cmd) case lib.StrIsASNStr(cmd): diff --git a/lib/cmd_bulk_ip_asn.go b/lib/cmd_bulk_ip_asn.go new file mode 100644 index 00000000..4fcf72c0 --- /dev/null +++ b/lib/cmd_bulk_ip_asn.go @@ -0,0 +1,35 @@ +package lib + +import ( + "github.com/spf13/pflag" +) + +// CmdBulkIpAsnFlags are flags expected by CmdBulkIpAsn. +type CmdBulkIpAsnFlags struct { + token string +} + +// Init initializes the common flags available to CmdBulkIpAsn with sensible +// defaults. +// +// pflag.Parse() must be called to actually use the final flag values. +func (f *CmdBulkIpAsnFlags) Init() { + _h := "see description in --help" + pflag.StringVarP( + &f.token, + "token", "t", "", + _h, + ) +} + +func CmdBulkIpAsn(f CmdBulkIpAsnFlags, args []string) error { + // 1) Separate the args into two groups + // a) args that are ip addresses + // b) args that are ASNs + ii = main.prepareIpinfoClient(fTok) + ips := li.GetIPInfoBatch(args) + + // 2) For each ip address, do a lookup and print the results + // 3) For each ASN, do a lookup and print the results + return nil +} diff --git a/lib/utils.go b/lib/utils.go new file mode 100644 index 00000000..c0eddb8f --- /dev/null +++ b/lib/utils.go @@ -0,0 +1,29 @@ +package lib + +type ValidatorFunc func(string) bool + +func ValidateWithAnyValidator(strings []string, validators []ValidatorFunc) bool { + if len(strings) == 0 || len(validators) == 0 { + return false // Return false if either the input strings or validators are empty. + } + + for _, str := range strings { + valid := false // Flag to keep track of successful validation for the current string. + for _, validator := range validators { + if validator(str) { + valid = true // At least one validator returned true for the current string. + break // No need to check further validators for this string. + } + } + if !valid { + return false // Return false if no validator returned true for the current string. + } + } + + return true // All strings passed at least one validation function. +} + +func StrArrIsCombinationOfIPsAndASNs(strArr []string) bool { + return len(strArr) >= 2 && + ValidateWithAnyValidator(strArr, []ValidatorFunc{StrIsIPStr, StrIsASNStr}) +} From e34255cf41481b5074a7b06a90fbf895f348e68b Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 17:07:55 +0500 Subject: [PATCH 02/11] testing and exploring --- ipinfo/cmd_bulk_ip_asn.go | 5 ++- ipinfo/main.go | 2 +- lib/cmd_bulk_ip_asn.go | 35 ++++++++++++++----- .../github.com/ipinfo/go/v2/ipinfo/batch.go | 15 +++++++- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/ipinfo/cmd_bulk_ip_asn.go b/ipinfo/cmd_bulk_ip_asn.go index 3234a52c..453de55b 100644 --- a/ipinfo/cmd_bulk_ip_asn.go +++ b/ipinfo/cmd_bulk_ip_asn.go @@ -10,5 +10,8 @@ func cmdBulkIpAsn() error { f := lib.CmdBulkIpAsnFlags{} f.Init() pflag.Parse() - return lib.CmdBulkIpAsn(f, os.Args[1:]) + + ii = prepareIpinfoClient(f.Token) + + return lib.CmdBulkIpAsn(f, ii, os.Args[1:]) } diff --git a/ipinfo/main.go b/ipinfo/main.go index 2b5250a3..8a0a01ca 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -34,7 +34,7 @@ func main() { } switch { - case lib.StrArrIsCombinationOfIPsAndASNs(os.Args[1:]): + case lib.StrIsASNStr(cmd) || lib.StrIsIPStr(cmd): err = cmdBulkIpAsn() case lib.StrIsIPStr(cmd): err = cmdIP(cmd) diff --git a/lib/cmd_bulk_ip_asn.go b/lib/cmd_bulk_ip_asn.go index 4fcf72c0..edd3f0ff 100644 --- a/lib/cmd_bulk_ip_asn.go +++ b/lib/cmd_bulk_ip_asn.go @@ -1,12 +1,15 @@ package lib import ( + "fmt" + "github.com/ipinfo/go/v2/ipinfo" "github.com/spf13/pflag" + "net" ) // CmdBulkIpAsnFlags are flags expected by CmdBulkIpAsn. type CmdBulkIpAsnFlags struct { - token string + Token string } // Init initializes the common flags available to CmdBulkIpAsn with sensible @@ -16,20 +19,34 @@ type CmdBulkIpAsnFlags struct { func (f *CmdBulkIpAsnFlags) Init() { _h := "see description in --help" pflag.StringVarP( - &f.token, + &f.Token, "token", "t", "", _h, ) } -func CmdBulkIpAsn(f CmdBulkIpAsnFlags, args []string) error { - // 1) Separate the args into two groups - // a) args that are ip addresses - // b) args that are ASNs - ii = main.prepareIpinfoClient(fTok) - ips := li.GetIPInfoBatch(args) +func CmdBulkIpAsn(f CmdBulkIpAsnFlags, ii *ipinfo.Client, args []string) error { + var ips []net.IP + var asns []string + var err error + + for _, arg := range args { + if StrIsIPStr(arg) { + ips = append(ips, net.ParseIP(arg)) + } else if StrIsASNStr(arg) { + asns = append(asns, arg) + } + } + + fmt.Println(asns) + _, err = ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ + TimeoutPerBatch: 60 * 30, // 30min + ConcurrentBatchRequestsLimit: 20, + }) + + //fmt.Println(data) // 2) For each ip address, do a lookup and print the results // 3) For each ASN, do a lookup and print the results - return nil + return err } diff --git a/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go b/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go index ad82cf1a..11bfc604 100644 --- a/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go +++ b/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go @@ -335,17 +335,30 @@ func (c *Client) GetASNDetailsBatch( asns []string, opts BatchReqOpts, ) (BatchASNDetails, error) { + fmt.Println("GetASNDetailsBatch") intermediateRes, err := c.GetBatch(asns, opts) + // if we have items in the result, don't throw them away; we'll convert // below and return the error together if it existed. if err != nil && len(intermediateRes) == 0 { + fmt.Println("Error: line 345") return nil, err } res := make(BatchASNDetails, len(intermediateRes)) + fmt.Println("res: ", res) for k, v := range intermediateRes { - res[k] = v.(*ASNDetails) + fmt.Println() + switch asn := v.(type) { + case *ASNDetails: + res[k] = asn + default: + // Handle the case when the type is not *ipinfo.ASNDetails. + fmt.Printf("Error: Unexpected type for key %T: %T\n", k, v) + } } + + fmt.Println("-->>Res: ", res) return res, err } From 55de7b745726fcfe085a20255b9b5361075f0737 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Wed, 2 Aug 2023 17:36:41 +0500 Subject: [PATCH 03/11] Added a bunch of things --- ipinfo/cmd_bulk_asn.go | 32 ++++++++ ipinfo/cmd_bulk_ip.go | 32 ++++++++ ipinfo/cmd_bulk_ip_asn.go | 17 ---- ipinfo/main.go | 6 +- lib/cmd_bulk_asn.go | 72 ++++++++++++++++ lib/cmd_bulk_ip.go | 82 +++++++++++++++++++ lib/cmd_bulk_ip_asn.go | 52 ------------ lib/utils.go | 59 ++++++++----- .../github.com/ipinfo/go/v2/ipinfo/batch.go | 15 +--- 9 files changed, 263 insertions(+), 104 deletions(-) create mode 100644 ipinfo/cmd_bulk_asn.go create mode 100644 ipinfo/cmd_bulk_ip.go delete mode 100644 ipinfo/cmd_bulk_ip_asn.go create mode 100644 lib/cmd_bulk_asn.go create mode 100644 lib/cmd_bulk_ip.go delete mode 100644 lib/cmd_bulk_ip_asn.go diff --git a/ipinfo/cmd_bulk_asn.go b/ipinfo/cmd_bulk_asn.go new file mode 100644 index 00000000..b21237ea --- /dev/null +++ b/ipinfo/cmd_bulk_asn.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/ipinfo/cli/lib" + "github.com/spf13/pflag" +) + +// TODO: +// Figure out help messages + +func cmdBulkASN() error { + f := lib.CmdBulkASNFlags{} + f.Init() + pflag.Parse() + + ii = prepareIpinfoClient(f.Token) + + data, err := lib.CmdBulkASN(ii, pflag.Args()) + if err != nil { + return err + } + + if len(f.Field) > 0 { + return outputFieldBatchASNDetails(data, f.Field, true, true) + } + + if f.Yaml { + return outputYAML(data) + } + + return outputJSON(data) +} diff --git a/ipinfo/cmd_bulk_ip.go b/ipinfo/cmd_bulk_ip.go new file mode 100644 index 00000000..0eb1c8cb --- /dev/null +++ b/ipinfo/cmd_bulk_ip.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/ipinfo/cli/lib" + "github.com/spf13/pflag" +) + +func cmdBulkIP() error { + f := lib.CmdBulkIPFlags{} + f.Init() + pflag.Parse() + + ii = prepareIpinfoClient(f.Token) + + data, err := lib.CmdBulkIP(ii, pflag.Args()) + if err != nil { + return err + } + + if len(f.Field) > 0 { + return outputFieldBatchCore(data, f.Field, true, true) + } + + if f.Csv { + return outputCSVBatchCore(data) + } + if f.Yaml { + return outputYAML(data) + } + + return outputJSON(data) +} diff --git a/ipinfo/cmd_bulk_ip_asn.go b/ipinfo/cmd_bulk_ip_asn.go deleted file mode 100644 index 453de55b..00000000 --- a/ipinfo/cmd_bulk_ip_asn.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "github.com/ipinfo/cli/lib" - "github.com/spf13/pflag" - "os" -) - -func cmdBulkIpAsn() error { - f := lib.CmdBulkIpAsnFlags{} - f.Init() - pflag.Parse() - - ii = prepareIpinfoClient(f.Token) - - return lib.CmdBulkIpAsn(f, ii, os.Args[1:]) -} diff --git a/ipinfo/main.go b/ipinfo/main.go index 8a0a01ca..5bf73a1d 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -34,8 +34,10 @@ func main() { } switch { - case lib.StrIsASNStr(cmd) || lib.StrIsIPStr(cmd): - err = cmdBulkIpAsn() + case lib.StrContainsMultipleIPs(os.Args[1:]): + err = cmdBulkIP() + case lib.StrContainsMultipleASNs(os.Args[1:]): + err = cmdBulkASN() case lib.StrIsIPStr(cmd): err = cmdIP(cmd) case lib.StrIsASNStr(cmd): diff --git a/lib/cmd_bulk_asn.go b/lib/cmd_bulk_asn.go new file mode 100644 index 00000000..887ef2db --- /dev/null +++ b/lib/cmd_bulk_asn.go @@ -0,0 +1,72 @@ +package lib + +import ( + "github.com/ipinfo/go/v2/ipinfo" + "github.com/spf13/pflag" + "strings" +) + +// CmdBulkASNFlags are flags expected by CmdBulkASN +type CmdBulkASNFlags struct { + Token string + nocache bool + help bool + Field []string + json bool + Yaml bool +} + +// Init initializes the common flags available to CmdBulkASN with sensible +// defaults. +// pflag.Parse() must be called to actually use the final flag values. +func (f *CmdBulkASNFlags) Init() { + _h := "see description in --help" + pflag.StringVarP( + &f.Token, + "token", "t", "", + _h, + ) + pflag.BoolVarP( + &f.nocache, + "nocache", "", false, + _h, + ) + pflag.BoolVarP( + &f.help, + "help", "h", false, + _h, + ) + pflag.StringSliceVarP( + &f.Field, + "field", "f", []string{}, + _h, + ) + pflag.BoolVarP( + &f.json, + "json", "j", false, + _h, + ) + pflag.BoolVarP( + &f.Yaml, + "yaml", "y", false, + _h, + ) +} + +func CmdBulkASN(ii *ipinfo.Client, args []string) (ipinfo.BatchASNDetails, error) { + var asns []string + + if !validateWithFunctions(args, []func(string) bool{StrIsASNStr}) { + return nil, ErrInvalidInput + } + + for _, arg := range args { + asns = append(asns, strings.ToUpper(arg)) + } + + return ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ + TimeoutPerBatch: 60 * 30, // 30min + ConcurrentBatchRequestsLimit: 20, + }) + +} diff --git a/lib/cmd_bulk_ip.go b/lib/cmd_bulk_ip.go new file mode 100644 index 00000000..095dc634 --- /dev/null +++ b/lib/cmd_bulk_ip.go @@ -0,0 +1,82 @@ +package lib + +import ( + "errors" + "github.com/ipinfo/go/v2/ipinfo" + "github.com/spf13/pflag" + "net" + "strings" +) + +// CmdBulkIPFlags are flags expected by CmdBulkIp. +type CmdBulkIPFlags struct { + Token string + nocache bool + help bool + Field []string + json bool + Csv bool + Yaml bool +} + +// Init initializes the common flags available to CmdBulkIP with sensible +// defaults. +// pflag.Parse() must be called to actually use the final flag values. +func (f *CmdBulkIPFlags) Init() { + _h := "see description in --help" + pflag.StringVarP( + &f.Token, + "token", "t", "", + _h, + ) + pflag.BoolVarP( + &f.nocache, + "nocache", "", false, + _h, + ) + pflag.BoolVarP( + &f.help, + "help", "h", false, + _h, + ) + pflag.StringSliceVarP( + &f.Field, + "field", "f", []string{}, + _h, + ) + pflag.BoolVarP( + &f.json, + "json", "j", false, + _h, + ) + pflag.BoolVarP( + &f.Csv, + "csv", "c", false, + _h, + ) + pflag.BoolVarP( + &f.Yaml, + "yaml", "y", false, + _h, + ) +} + +func CmdBulkIP(ii *ipinfo.Client, args []string) (ipinfo.BatchCore, error) { + var ips []net.IP + if !validateWithFunctions(args, []func(string) bool{StrIsIPStr}) { + return nil, ErrInvalidInput + } + + if strings.TrimSpace(ii.Token) == "" { + return nil, errors.New("bulk lookups require a token; login via `ipinfo init`") + } + + for _, ipstr := range args { + ips = append(ips, net.ParseIP(ipstr)) + } + + return ii.GetIPInfoBatch(ips, ipinfo.BatchReqOpts{ + TimeoutPerBatch: 60 * 30, // 30min + ConcurrentBatchRequestsLimit: 20, + }) +} diff --git a/lib/cmd_bulk_ip_asn.go b/lib/cmd_bulk_ip_asn.go deleted file mode 100644 index edd3f0ff..00000000 --- a/lib/cmd_bulk_ip_asn.go +++ /dev/null @@ -1,52 +0,0 @@ -package lib - -import ( - "fmt" - "github.com/ipinfo/go/v2/ipinfo" - "github.com/spf13/pflag" - "net" -) - -// CmdBulkIpAsnFlags are flags expected by CmdBulkIpAsn. -type CmdBulkIpAsnFlags struct { - Token string -} - -// Init initializes the common flags available to CmdBulkIpAsn with sensible -// defaults. -// -// pflag.Parse() must be called to actually use the final flag values. -func (f *CmdBulkIpAsnFlags) Init() { - _h := "see description in --help" - pflag.StringVarP( - &f.Token, - "token", "t", "", - _h, - ) -} - -func CmdBulkIpAsn(f CmdBulkIpAsnFlags, ii *ipinfo.Client, args []string) error { - var ips []net.IP - var asns []string - var err error - - for _, arg := range args { - if StrIsIPStr(arg) { - ips = append(ips, net.ParseIP(arg)) - } else if StrIsASNStr(arg) { - asns = append(asns, arg) - } - } - - fmt.Println(asns) - _, err = ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ - TimeoutPerBatch: 60 * 30, // 30min - ConcurrentBatchRequestsLimit: 20, - }) - - //fmt.Println(data) - - // 2) For each ip address, do a lookup and print the results - // 3) For each ASN, do a lookup and print the results - return err -} diff --git a/lib/utils.go b/lib/utils.go index c0eddb8f..bb2719c2 100644 --- a/lib/utils.go +++ b/lib/utils.go @@ -1,29 +1,50 @@ package lib -type ValidatorFunc func(string) bool - -func ValidateWithAnyValidator(strings []string, validators []ValidatorFunc) bool { - if len(strings) == 0 || len(validators) == 0 { - return false // Return false if either the input strings or validators are empty. +// validateWithFunctions validates a slice of strings using a set of custom validation functions. +// It checks whether all elements in the input slice satisfy all the given validation functions. +// If any element fails validation for any of the functions, it returns false. +// Otherwise, it returns true. +// +// Parameters: +// s: A slice of strings to be validated. +// fns: A slice of functions that take a string argument and return a boolean value. Each function represents a validation rule. +// +// Returns: +// true: If all elements in the input slice satisfy all the given validation functions. +// false: If any element in the input slice fails validation for any of the functions. +func validateWithFunctions(s []string, fns []func(string) bool) bool { + for _, fn := range fns { + for _, str := range s { + if !fn(str) { + return false + } + } } + return true +} - for _, str := range strings { - valid := false // Flag to keep track of successful validation for the current string. - for _, validator := range validators { - if validator(str) { - valid = true // At least one validator returned true for the current string. - break // No need to check further validators for this string. - } +func StrContainsMultipleIPs(ipsStr []string) bool { + numberOfIPs := 0 + for _, ipStr := range ipsStr { + if StrIsIPStr(ipStr) { + numberOfIPs++ } - if !valid { - return false // Return false if no validator returned true for the current string. + if numberOfIPs > 1 { + return true } } - - return true // All strings passed at least one validation function. + return false } -func StrArrIsCombinationOfIPsAndASNs(strArr []string) bool { - return len(strArr) >= 2 && - ValidateWithAnyValidator(strArr, []ValidatorFunc{StrIsIPStr, StrIsASNStr}) +func StrContainsMultipleASNs(asnsStr []string) bool { + numberOfASNs := 0 + for _, asnStr := range asnsStr { + if StrIsASNStr(asnStr) { + numberOfASNs++ + } + if numberOfASNs > 1 { + return true + } + } + return false } diff --git a/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go b/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go index 11bfc604..ad82cf1a 100644 --- a/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go +++ b/vendor/github.com/ipinfo/go/v2/ipinfo/batch.go @@ -335,30 +335,17 @@ func (c *Client) GetASNDetailsBatch( asns []string, opts BatchReqOpts, ) (BatchASNDetails, error) { - fmt.Println("GetASNDetailsBatch") intermediateRes, err := c.GetBatch(asns, opts) - // if we have items in the result, don't throw them away; we'll convert // below and return the error together if it existed. if err != nil && len(intermediateRes) == 0 { - fmt.Println("Error: line 345") return nil, err } res := make(BatchASNDetails, len(intermediateRes)) - fmt.Println("res: ", res) for k, v := range intermediateRes { - fmt.Println() - switch asn := v.(type) { - case *ASNDetails: - res[k] = asn - default: - // Handle the case when the type is not *ipinfo.ASNDetails. - fmt.Printf("Error: Unexpected type for key %T: %T\n", k, v) - } + res[k] = v.(*ASNDetails) } - - fmt.Println("-->>Res: ", res) return res, err } From 039e117aeffd13b9ab003fced69b8f45616ccdc8 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 3 Aug 2023 15:14:12 +0500 Subject: [PATCH 04/11] Bulk lookup for ASN --- ipinfo/cmd_asn.go | 134 ++++++++++++++-------------------- ipinfo/cmd_asn_bulk.go | 90 +++++++++++++++++++++++ ipinfo/cmd_asn_single.go | 111 ++++++++++++++++++++++++++++ ipinfo/cmd_bulk_asn.go | 32 -------- ipinfo/cmd_bulk_ip.go | 32 -------- ipinfo/cmd_default.go | 1 + ipinfo/completions.go | 3 +- ipinfo/main.go | 8 +- lib/cmd_asn_bulk.go | 153 +++++++++++++++++++++++++++++++++++++++ lib/cmd_bulk_asn.go | 72 ------------------ lib/cmd_bulk_ip.go | 82 --------------------- lib/utils.go | 50 ------------- 12 files changed, 414 insertions(+), 354 deletions(-) create mode 100644 ipinfo/cmd_asn_bulk.go create mode 100644 ipinfo/cmd_asn_single.go delete mode 100644 ipinfo/cmd_bulk_asn.go delete mode 100644 ipinfo/cmd_bulk_ip.go create mode 100644 lib/cmd_asn_bulk.go delete mode 100644 lib/cmd_bulk_asn.go delete mode 100644 lib/cmd_bulk_ip.go delete mode 100644 lib/utils.go diff --git a/ipinfo/cmd_asn.go b/ipinfo/cmd_asn.go index 6a478277..721d9dbb 100644 --- a/ipinfo/cmd_asn.go +++ b/ipinfo/cmd_asn.go @@ -1,113 +1,87 @@ package main import ( - "errors" "fmt" - "net/http" + "github.com/ipinfo/cli/lib" + "github.com/spf13/pflag" + "os" - "github.com/fatih/color" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" - "github.com/ipinfo/go/v2/ipinfo" - "github.com/spf13/pflag" ) var completionsASN = &complete.Command{ + Sub: map[string]*complete.Command{ + "bulk": completionsASNBulk, + }, Flags: map[string]complete.Predictor{ - "-t": predict.Nothing, - "--token": predict.Nothing, - "--nocache": predict.Nothing, - "-h": predict.Nothing, - "--help": predict.Nothing, - "-f": predict.Set(asnFields), - "--field": predict.Set(asnFields), - "--nocolor": predict.Nothing, - "-p": predict.Nothing, - "--pretty": predict.Nothing, - "-j": predict.Nothing, - "--json": predict.Nothing, - "-c": predict.Nothing, - "--csv": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, }, } -func printHelpASN(asn string) { +func printHelpASN() { + fmt.Printf( - `Usage: %s %s [] + `Usage: %s asn [] + +Commands: + bulk lookup ASNs in bulk Options: General: - --token , -t - use as API token. - --nocache - do not use the cache. --help, -h show help. - - Outputs: - --field , -f - lookup only specific fields in the output. - field names correspond to JSON keys, e.g. 'registry' or 'allocated'. - multiple field names must be separated by commas. - --nocolor - disable colored output. - - Formats: - --json, -j - output JSON format. (default) - --yaml, -y - output YAML format. -`, progBase, asn) +`, progBase) } -func cmdASN(asn string) error { - var fTok string - var fField []string - var fJSON bool - var fYAML bool - - pflag.StringVarP(&fTok, "token", "t", "", "the token to use.") - pflag.BoolVar(&fNoCache, "nocache", false, "disable the cache.") - pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") - pflag.StringSliceVarP(&fField, "field", "f", nil, "specific field to lookup.") - pflag.BoolVarP(&fJSON, "json", "j", true, "output JSON format. (default)") - pflag.BoolVarP(&fYAML, "yaml", "y", false, "output YAML format.") - pflag.BoolVar(&fNoColor, "nocolor", false, "disable color output.") - pflag.Parse() - - if fNoColor { - color.NoColor = true +// cmdASN is the handler for the "asn" command. +func cmdASN() error { + var err error + cmd := "" + if len(os.Args) > 2 { + cmd = os.Args[2] } - if fHelp { - printHelpASN(asn) - return nil - } + switch { + case cmd == "bulk": + err = cmdASNBulk() + default: + // check whether the standard input (stdin) is being piped + // or redirected from another source or whether it's being read from the terminal (interactive mode). + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) != 0 { + printHelpASN() + return nil + } - ii = prepareIpinfoClient(fTok) + f := lib.CmdASNBulkFlags{} + f.Init() + pflag.Parse() - // require token for ASN API. - if ii.Token == "" { - return errors.New("ASN lookups require a token; login via `ipinfo init`.") - } + ii = prepareIpinfoClient(f.Token) + data, err := lib.CmdASNBulk(f, ii, lib.ReadStringsFromStdin(), printHelpASNBulk) + if err != nil { + return err + } + if (data) == nil { + return nil + } - data, err := ii.GetASNDetails(asn) - if err != nil { - iiErr, ok := err.(*ipinfo.ErrorResponse) - if ok && (iiErr.Response.StatusCode == http.StatusUnauthorized) { - return errors.New("Token does not have access to ASN API") + if len(f.Field) > 0 { + return outputFieldBatchASNDetails(data, f.Field, false, false) } - return err - } - if len(fField) > 0 { - d := make(ipinfo.BatchASNDetails, 1) - d[data.ASN] = data - return outputFieldBatchASNDetails(d, fField, false, false) + if f.Yaml { + return outputYAML(data) + } + + return outputJSON(data) } - if fYAML { - return outputYAML(data) + + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) } - return outputJSON(data) + return nil } diff --git a/ipinfo/cmd_asn_bulk.go b/ipinfo/cmd_asn_bulk.go new file mode 100644 index 00000000..7c15b529 --- /dev/null +++ b/ipinfo/cmd_asn_bulk.go @@ -0,0 +1,90 @@ +package main + +import ( + "fmt" + "github.com/ipinfo/cli/lib" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var completionsASNBulk = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-t": predict.Nothing, + "--token": predict.Nothing, + "--nocache": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, + "-f": predict.Set(asnFields), + "--field": predict.Set(asnFields), + "-j": predict.Nothing, + "--json": predict.Nothing, + }, +} + +// printHelpASNBulk prints the help message for the asn bulk command. +func printHelpASNBulk() { + fmt.Printf( + `Usage: %s asn bulk [] + +Description: + Accepts ASNs and file paths. + +Examples: + # Lookup all ASNs in multiple files. + $ %[1]s asn bulk /path/to/asnlist1.txt /path/to/asnlist2.txt + + # Lookup multiple ASNs. + $ %[1]s asn bulk AS123 AS456 AS789 + + # Lookup ASNs from multiple sources simultaneously. + $ %[1]s asn bulk AS123 AS456 AS789 asns.txt + +Options: + General: + --token , -t + use as API token. + --nocache + do not use the cache. + --help, -h + show help. + + Outputs: + --field , -f + lookup only specific fields in the output. + field names correspond to JSON keys, e.g. 'name' or 'registry'. + multiple field names must be separated by commas. + + Formats: + --json, -j + output JSON format. (default) + --yaml, -y + output YAML format. +`, progBase) +} + +// cmdASNBulk is the asn bulk command. +func cmdASNBulk() error { + f := lib.CmdASNBulkFlags{} + f.Init() + pflag.Parse() + + ii = prepareIpinfoClient(f.Token) + data, err := lib.CmdASNBulk(f, ii, pflag.Args()[2:], printHelpASNBulk) + if err != nil { + return err + } + if (data) == nil { + return nil + } + + if len(f.Field) > 0 { + return outputFieldBatchASNDetails(data, f.Field, false, false) + } + + if f.Yaml { + return outputYAML(data) + } + + return outputJSON(data) +} diff --git a/ipinfo/cmd_asn_single.go b/ipinfo/cmd_asn_single.go new file mode 100644 index 00000000..8c62a8f2 --- /dev/null +++ b/ipinfo/cmd_asn_single.go @@ -0,0 +1,111 @@ +package main + +import ( + "errors" + "fmt" + "net/http" + + "github.com/fatih/color" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/ipinfo/go/v2/ipinfo" + "github.com/spf13/pflag" +) + +var completionsASNSingle = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-t": predict.Nothing, + "--token": predict.Nothing, + "--nocache": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, + "-f": predict.Set(asnFields), + "--field": predict.Set(asnFields), + "--nocolor": predict.Nothing, + "-p": predict.Nothing, + "--pretty": predict.Nothing, + "-j": predict.Nothing, + "--json": predict.Nothing, + }, +} + +func printHelpASNSingle(asn string) { + fmt.Printf( + `Usage: %s %s [] + +Options: + General: + --token , -t + use as API token. + --nocache + do not use the cache. + --help, -h + show help. + + Outputs: + --field , -f + lookup only specific fields in the output. + field names correspond to JSON keys, e.g. 'registry' or 'allocated'. + multiple field names must be separated by commas. + --nocolor + disable colored output. + + Formats: + --json, -j + output JSON format. (default) + --yaml, -y + output YAML format. +`, progBase, asn) +} + +func cmdASNSingle(asn string) error { + var fTok string + var fField []string + var fJSON bool + var fYAML bool + + pflag.StringVarP(&fTok, "token", "t", "", "the token to use.") + pflag.BoolVar(&fNoCache, "nocache", false, "disable the cache.") + pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") + pflag.StringSliceVarP(&fField, "field", "f", nil, "specific field to lookup.") + pflag.BoolVarP(&fJSON, "json", "j", true, "output JSON format. (default)") + pflag.BoolVarP(&fYAML, "yaml", "y", false, "output YAML format.") + pflag.BoolVar(&fNoColor, "nocolor", false, "disable color output.") + pflag.Parse() + + if fNoColor { + color.NoColor = true + } + + if fHelp { + printHelpASNSingle(asn) + return nil + } + + ii = prepareIpinfoClient(fTok) + + // require token for ASN API. + if ii.Token == "" { + return errors.New("ASN lookups require a token; login via `ipinfo init`.") + } + + data, err := ii.GetASNDetails(asn) + if err != nil { + iiErr, ok := err.(*ipinfo.ErrorResponse) + if ok && (iiErr.Response.StatusCode == http.StatusUnauthorized) { + return errors.New("Token does not have access to ASN API") + } + return err + } + + if len(fField) > 0 { + d := make(ipinfo.BatchASNDetails, 1) + d[data.ASN] = data + return outputFieldBatchASNDetails(d, fField, false, false) + } + if fYAML { + return outputYAML(data) + } + + return outputJSON(data) +} diff --git a/ipinfo/cmd_bulk_asn.go b/ipinfo/cmd_bulk_asn.go deleted file mode 100644 index b21237ea..00000000 --- a/ipinfo/cmd_bulk_asn.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "github.com/ipinfo/cli/lib" - "github.com/spf13/pflag" -) - -// TODO: -// Figure out help messages - -func cmdBulkASN() error { - f := lib.CmdBulkASNFlags{} - f.Init() - pflag.Parse() - - ii = prepareIpinfoClient(f.Token) - - data, err := lib.CmdBulkASN(ii, pflag.Args()) - if err != nil { - return err - } - - if len(f.Field) > 0 { - return outputFieldBatchASNDetails(data, f.Field, true, true) - } - - if f.Yaml { - return outputYAML(data) - } - - return outputJSON(data) -} diff --git a/ipinfo/cmd_bulk_ip.go b/ipinfo/cmd_bulk_ip.go deleted file mode 100644 index 0eb1c8cb..00000000 --- a/ipinfo/cmd_bulk_ip.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "github.com/ipinfo/cli/lib" - "github.com/spf13/pflag" -) - -func cmdBulkIP() error { - f := lib.CmdBulkIPFlags{} - f.Init() - pflag.Parse() - - ii = prepareIpinfoClient(f.Token) - - data, err := lib.CmdBulkIP(ii, pflag.Args()) - if err != nil { - return err - } - - if len(f.Field) > 0 { - return outputFieldBatchCore(data, f.Field, true, true) - } - - if f.Csv { - return outputCSVBatchCore(data) - } - if f.Yaml { - return outputYAML(data) - } - - return outputJSON(data) -} diff --git a/ipinfo/cmd_default.go b/ipinfo/cmd_default.go index 9926dc50..7c266977 100644 --- a/ipinfo/cmd_default.go +++ b/ipinfo/cmd_default.go @@ -21,6 +21,7 @@ Commands: look up details for an ASN, e.g. AS123 or as123. myip get details for your IP. bulk get details for multiple IPs in bulk. + asn tools related to ASNs. summarize get summarized data for a group of IPs. map open a URL to a map showing the locations of a group of IPs. prips print IP list from CIDR or range. diff --git a/ipinfo/completions.go b/ipinfo/completions.go index 0287d836..79163586 100644 --- a/ipinfo/completions.go +++ b/ipinfo/completions.go @@ -12,6 +12,7 @@ var completions = &complete.Command{ Sub: map[string]*complete.Command{ "myip": completionsMyIP, "bulk": completionsBulk, + "asn": completionsASN, "summarize": completionsSummarize, "map": completionsMap, "prips": completionsPrips, @@ -49,7 +50,7 @@ func handleCompletions() { if lib.StrIsIPStr(cmdSecondArg) { completions.Sub[cmdSecondArg] = completionsIP } else if lib.StrIsASNStr(cmdSecondArg) { - completions.Sub[cmdSecondArg] = completionsASN + completions.Sub[cmdSecondArg] = completionsASNSingle } } diff --git a/ipinfo/main.go b/ipinfo/main.go index 5bf73a1d..396436fd 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -34,21 +34,19 @@ func main() { } switch { - case lib.StrContainsMultipleIPs(os.Args[1:]): - err = cmdBulkIP() - case lib.StrContainsMultipleASNs(os.Args[1:]): - err = cmdBulkASN() case lib.StrIsIPStr(cmd): err = cmdIP(cmd) case lib.StrIsASNStr(cmd): asn := strings.ToUpper(cmd) - err = cmdASN(asn) + err = cmdASNSingle(asn) case len(cmd) >= 3 && strings.IndexByte(cmd, '.') != -1: err = cmdDomain(cmd) case cmd == "myip": err = cmdMyIP() case cmd == "bulk": err = cmdBulk() + case cmd == "asn": + err = cmdASN() case cmd == "summarize" || cmd == "sum": err = cmdSum() case cmd == "map": diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go new file mode 100644 index 00000000..fa7aeed3 --- /dev/null +++ b/lib/cmd_asn_bulk.go @@ -0,0 +1,153 @@ +package lib + +import ( + "bufio" + "errors" + "fmt" + "github.com/ipinfo/go/v2/ipinfo" + "github.com/spf13/pflag" + "os" + "strings" +) + +// CmdASNBulkFlags are flags expected by CmdASNBulk +type CmdASNBulkFlags struct { + Token string + nocache bool + help bool + Field []string + json bool + Yaml bool +} + +// Init initializes the common flags available to CmdASNBulk with sensible +// defaults. +// pflag.Parse() must be called to actually use the final flag values. +func (f *CmdASNBulkFlags) Init() { + _h := "see description in --help" + pflag.StringVarP( + &f.Token, + "token", "t", "", + _h, + ) + pflag.BoolVarP( + &f.nocache, + "nocache", "", false, + _h, + ) + pflag.BoolVarP( + &f.help, + "help", "h", false, + _h, + ) + pflag.StringSliceVarP( + &f.Field, + "field", "f", []string{}, + _h, + ) + pflag.BoolVarP( + &f.json, + "json", "j", false, + _h, + ) + pflag.BoolVarP( + &f.Yaml, + "yaml", "y", false, + _h, + ) +} + +// CmdASNBulk is the entrypoint for the `ipinfo asn-bulk` command. +func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp func()) (ipinfo.BatchASNDetails, error) { + if f.help { + printHelp() + return nil, nil + } + + var asns []string + + if len(args) == 0 { + fmt.Println("** manual input mode **\nEnter all ASNs, one per line:") + args = ReadStringsFromStdin() + if len(args) == 0 { + return nil, errors.New("no input ASNs") + } + } + + for i := 0; i < len(args); i++ { + if strings.HasSuffix(args[i], ".txt") { + lines, err := readStringsFromFile(args[i]) + if err != nil { + return nil, err + } + // Remove arg from args + args = append(args[:i], args[i+1:]...) + i-- // Adjust the index as the slice length changes + args = append(args, lines...) + } + } + + if ii.Token == "" { + return nil, errors.New("bulk lookups require a token; login via `ipinfo init`.") + } + + for _, arg := range args { + // Convert to uppercase + asnUpperCase := strings.ToUpper(arg) + // Validate ASN + if !StrIsASNStr(asnUpperCase) { + return nil, ErrInvalidInput + } + asns = append(asns, asnUpperCase) + } + + return ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ + TimeoutPerBatch: 60 * 30, // 30min + ConcurrentBatchRequestsLimit: 20, + }) +} + +// ReadStringsFromStdin reads strings from stdin until an empty line is entered. +func ReadStringsFromStdin() []string { + var inputLines []string + scanner := bufio.NewScanner(os.Stdin) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + break + } + inputLines = append(inputLines, line) + } + return inputLines +} + +// readStringsFromFile reads strings from a file, one per line. +func readStringsFromFile(filename string) ([]string, error) { + var lines []string + + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + return // ignore errors on close + } + }(file) + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanWords) // Set the scanner to split on spaces and newlines + + for scanner.Scan() { + word := scanner.Text() + lines = append(lines, word) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return lines, nil +} diff --git a/lib/cmd_bulk_asn.go b/lib/cmd_bulk_asn.go deleted file mode 100644 index 887ef2db..00000000 --- a/lib/cmd_bulk_asn.go +++ /dev/null @@ -1,72 +0,0 @@ -package lib - -import ( - "github.com/ipinfo/go/v2/ipinfo" - "github.com/spf13/pflag" - "strings" -) - -// CmdBulkASNFlags are flags expected by CmdBulkASN -type CmdBulkASNFlags struct { - Token string - nocache bool - help bool - Field []string - json bool - Yaml bool -} - -// Init initializes the common flags available to CmdBulkASN with sensible -// defaults. -// pflag.Parse() must be called to actually use the final flag values. -func (f *CmdBulkASNFlags) Init() { - _h := "see description in --help" - pflag.StringVarP( - &f.Token, - "token", "t", "", - _h, - ) - pflag.BoolVarP( - &f.nocache, - "nocache", "", false, - _h, - ) - pflag.BoolVarP( - &f.help, - "help", "h", false, - _h, - ) - pflag.StringSliceVarP( - &f.Field, - "field", "f", []string{}, - _h, - ) - pflag.BoolVarP( - &f.json, - "json", "j", false, - _h, - ) - pflag.BoolVarP( - &f.Yaml, - "yaml", "y", false, - _h, - ) -} - -func CmdBulkASN(ii *ipinfo.Client, args []string) (ipinfo.BatchASNDetails, error) { - var asns []string - - if !validateWithFunctions(args, []func(string) bool{StrIsASNStr}) { - return nil, ErrInvalidInput - } - - for _, arg := range args { - asns = append(asns, strings.ToUpper(arg)) - } - - return ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ - TimeoutPerBatch: 60 * 30, // 30min - ConcurrentBatchRequestsLimit: 20, - }) - -} diff --git a/lib/cmd_bulk_ip.go b/lib/cmd_bulk_ip.go deleted file mode 100644 index 095dc634..00000000 --- a/lib/cmd_bulk_ip.go +++ /dev/null @@ -1,82 +0,0 @@ -package lib - -import ( - "errors" - "github.com/ipinfo/go/v2/ipinfo" - "github.com/spf13/pflag" - "net" - "strings" -) - -// CmdBulkIPFlags are flags expected by CmdBulkIp. -type CmdBulkIPFlags struct { - Token string - nocache bool - help bool - Field []string - json bool - Csv bool - Yaml bool -} - -// Init initializes the common flags available to CmdBulkIP with sensible -// defaults. -// pflag.Parse() must be called to actually use the final flag values. -func (f *CmdBulkIPFlags) Init() { - _h := "see description in --help" - pflag.StringVarP( - &f.Token, - "token", "t", "", - _h, - ) - pflag.BoolVarP( - &f.nocache, - "nocache", "", false, - _h, - ) - pflag.BoolVarP( - &f.help, - "help", "h", false, - _h, - ) - pflag.StringSliceVarP( - &f.Field, - "field", "f", []string{}, - _h, - ) - pflag.BoolVarP( - &f.json, - "json", "j", false, - _h, - ) - pflag.BoolVarP( - &f.Csv, - "csv", "c", false, - _h, - ) - pflag.BoolVarP( - &f.Yaml, - "yaml", "y", false, - _h, - ) -} - -func CmdBulkIP(ii *ipinfo.Client, args []string) (ipinfo.BatchCore, error) { - var ips []net.IP - if !validateWithFunctions(args, []func(string) bool{StrIsIPStr}) { - return nil, ErrInvalidInput - } - - if strings.TrimSpace(ii.Token) == "" { - return nil, errors.New("bulk lookups require a token; login via `ipinfo init`") - } - - for _, ipstr := range args { - ips = append(ips, net.ParseIP(ipstr)) - } - - return ii.GetIPInfoBatch(ips, ipinfo.BatchReqOpts{ - TimeoutPerBatch: 60 * 30, // 30min - ConcurrentBatchRequestsLimit: 20, - }) -} diff --git a/lib/utils.go b/lib/utils.go deleted file mode 100644 index bb2719c2..00000000 --- a/lib/utils.go +++ /dev/null @@ -1,50 +0,0 @@ -package lib - -// validateWithFunctions validates a slice of strings using a set of custom validation functions. -// It checks whether all elements in the input slice satisfy all the given validation functions. -// If any element fails validation for any of the functions, it returns false. -// Otherwise, it returns true. -// -// Parameters: -// s: A slice of strings to be validated. -// fns: A slice of functions that take a string argument and return a boolean value. Each function represents a validation rule. -// -// Returns: -// true: If all elements in the input slice satisfy all the given validation functions. -// false: If any element in the input slice fails validation for any of the functions. -func validateWithFunctions(s []string, fns []func(string) bool) bool { - for _, fn := range fns { - for _, str := range s { - if !fn(str) { - return false - } - } - } - return true -} - -func StrContainsMultipleIPs(ipsStr []string) bool { - numberOfIPs := 0 - for _, ipStr := range ipsStr { - if StrIsIPStr(ipStr) { - numberOfIPs++ - } - if numberOfIPs > 1 { - return true - } - } - return false -} - -func StrContainsMultipleASNs(asnsStr []string) bool { - numberOfASNs := 0 - for _, asnStr := range asnsStr { - if StrIsASNStr(asnStr) { - numberOfASNs++ - } - if numberOfASNs > 1 { - return true - } - } - return false -} From ee80d942777e1a29b49b20d044c5e160a40e22e8 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 3 Aug 2023 17:07:48 +0500 Subject: [PATCH 05/11] Fixes not reading token from saved config --- ipinfo/cmd_asn_bulk.go | 5 +++++ ipinfo/cmd_init.go | 2 +- lib/cmd_asn_bulk.go | 4 ---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ipinfo/cmd_asn_bulk.go b/ipinfo/cmd_asn_bulk.go index 7c15b529..80b4daf2 100644 --- a/ipinfo/cmd_asn_bulk.go +++ b/ipinfo/cmd_asn_bulk.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" @@ -70,6 +71,10 @@ func cmdASNBulk() error { pflag.Parse() ii = prepareIpinfoClient(f.Token) + if ii.Token == "" && gConfig.Token == "" { + return errors.New("bulk lookups require a token; login via `ipinfo init`.") + } + data, err := lib.CmdASNBulk(f, ii, pflag.Args()[2:], printHelpASNBulk) if err != nil { return err diff --git a/ipinfo/cmd_init.go b/ipinfo/cmd_init.go index 0043f33a..ebdf796e 100644 --- a/ipinfo/cmd_init.go +++ b/ipinfo/cmd_init.go @@ -134,7 +134,7 @@ func cmdInit() error { } // save token to file. - gConfig.Token = tok + gConfig.Token = newtoken if err := SaveConfig(gConfig); err != nil { return err } diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index fa7aeed3..43f7dd46 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -87,10 +87,6 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f } } - if ii.Token == "" { - return nil, errors.New("bulk lookups require a token; login via `ipinfo init`.") - } - for _, arg := range args { // Convert to uppercase asnUpperCase := strings.ToUpper(arg) From 345cbcede38671fb09a0bf86b9da008b72a29ca7 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 3 Aug 2023 17:12:25 +0500 Subject: [PATCH 06/11] minor fixes --- ipinfo/cmd_asn_bulk.go | 5 ----- lib/cmd_asn_bulk.go | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ipinfo/cmd_asn_bulk.go b/ipinfo/cmd_asn_bulk.go index 80b4daf2..7c15b529 100644 --- a/ipinfo/cmd_asn_bulk.go +++ b/ipinfo/cmd_asn_bulk.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" @@ -71,10 +70,6 @@ func cmdASNBulk() error { pflag.Parse() ii = prepareIpinfoClient(f.Token) - if ii.Token == "" && gConfig.Token == "" { - return errors.New("bulk lookups require a token; login via `ipinfo init`.") - } - data, err := lib.CmdASNBulk(f, ii, pflag.Args()[2:], printHelpASNBulk) if err != nil { return err diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index 43f7dd46..fa7aeed3 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -87,6 +87,10 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f } } + if ii.Token == "" { + return nil, errors.New("bulk lookups require a token; login via `ipinfo init`.") + } + for _, arg := range args { // Convert to uppercase asnUpperCase := strings.ToUpper(arg) From 91986b199465bf3be44a4d4be8d86234f43f7148 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 10 Aug 2023 17:59:21 +0500 Subject: [PATCH 07/11] Refactor: Generalize Input Source Handling Restructure the codebase to enhance input handling from various sources --- lib/cmd_asn_bulk.go | 92 +++++++------------------------ lib/ip_list.go | 95 ++++++++++---------------------- lib/utils_input.go | 129 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 140 deletions(-) create mode 100644 lib/utils_input.go diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index fa7aeed3..6a6095bb 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -1,12 +1,9 @@ package lib import ( - "bufio" "errors" - "fmt" "github.com/ipinfo/go/v2/ipinfo" "github.com/spf13/pflag" - "os" "strings" ) @@ -66,39 +63,31 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f var asns []string - if len(args) == 0 { - fmt.Println("** manual input mode **\nEnter all ASNs, one per line:") - args = ReadStringsFromStdin() - if len(args) == 0 { - return nil, errors.New("no input ASNs") - } - } - - for i := 0; i < len(args); i++ { - if strings.HasSuffix(args[i], ".txt") { - lines, err := readStringsFromFile(args[i]) + op := func(string string, inputType INPUT_TYPE) error { + switch inputType { + case INPUT_TYPE_ASN: + asns = append(asns, strings.ToUpper(string)) + case INPUT_TYPE_FILE: + lines, err := readStringsFromFile(string) if err != nil { - return nil, err + return err + } + for _, line := range lines { + asns = append(asns, strings.ToUpper(line)) } - // Remove arg from args - args = append(args[:i], args[i+1:]...) - i-- // Adjust the index as the slice length changes - args = append(args, lines...) + default: + return ErrInvalidInput } + return nil } - if ii.Token == "" { - return nil, errors.New("bulk lookups require a token; login via `ipinfo init`.") + err := getInputFrom(args, true, op) + if err != nil { + return nil, err } - for _, arg := range args { - // Convert to uppercase - asnUpperCase := strings.ToUpper(arg) - // Validate ASN - if !StrIsASNStr(asnUpperCase) { - return nil, ErrInvalidInput - } - asns = append(asns, asnUpperCase) + if ii.Token == "" { + return nil, errors.New("bulk lookups require a token; login via `ipinfo init`.") } return ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ @@ -106,48 +95,3 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f ConcurrentBatchRequestsLimit: 20, }) } - -// ReadStringsFromStdin reads strings from stdin until an empty line is entered. -func ReadStringsFromStdin() []string { - var inputLines []string - scanner := bufio.NewScanner(os.Stdin) - - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" { - break - } - inputLines = append(inputLines, line) - } - return inputLines -} - -// readStringsFromFile reads strings from a file, one per line. -func readStringsFromFile(filename string) ([]string, error) { - var lines []string - - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer func(file *os.File) { - err := file.Close() - if err != nil { - return // ignore errors on close - } - }(file) - - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanWords) // Set the scanner to split on spaces and newlines - - for scanner.Scan() { - word := scanner.Text() - lines = append(lines, word) - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return lines, nil -} diff --git a/lib/ip_list.go b/lib/ip_list.go index c83249a7..1bbe4f64 100644 --- a/lib/ip_list.go +++ b/lib/ip_list.go @@ -3,89 +3,52 @@ package lib import ( "bufio" "encoding/binary" - "fmt" "io" "net" "os" "strings" ) -// IPListFrom returns a list of IPs from stdin and a list of inputs which is -// interpreted to contain IPs, IP ranges, IP CIDRs and files with IPs in them, -// all depending upon which flags are set. -func IPListFrom( - inputs []string, - stdin bool, - ip bool, - iprange bool, - cidr bool, - file bool, -) ([]net.IP, error) { - ips := make([]net.IP, 0, len(inputs)) - - // prevent edge cases with all flags turned off. - if !stdin && !ip && !iprange && !cidr && !file { - return ips, nil - } - - // start with stdin. - if stdin { - stat, _ := os.Stdin.Stat() - - isPiped := (stat.Mode() & os.ModeNamedPipe) != 0 - isTyping := (stat.Mode()&os.ModeCharDevice) != 0 && len(inputs) == 0 - - if isTyping { - fmt.Println("** manual input mode **") - fmt.Println("Enter all IPs, one per line:") - } - - if isPiped || isTyping || stat.Size() > 0 { - ips = append(ips, IPListFromStdin()...) - } - } - - // parse `inputs`. - for _, input := range inputs { - if iprange { - _ips, err := IPListFromRangeStr(input) - if err == nil { - ips = append(ips, _ips...) - continue - } - } +// IPListFromAllSrcs is the same as IPListFrom with all flags turned on. +func IPListFromAllSrcs(inputs []string) ([]net.IP, error) { + var ips []net.IP - if ip && StrIsIPStr(input) { + op := func(input string, inputType INPUT_TYPE) error { + switch inputType { + case INPUT_TYPE_IP: ips = append(ips, net.ParseIP(input)) - continue - } - - if cidr && StrIsCIDRStr(input) { - _ips, _ := IPListFromCIDR(input) - ips = append(ips, _ips...) - continue - } - - if file && FileExists(input) { - _ips, err := IPListFromFile(input) + case INPUT_TYPE_IP_RANGE: + r, err := IPListFromRangeStr(input) if err != nil { - return nil, err + return err } - ips = append(ips, _ips...) - continue + ips = append(ips, r...) + case INPUT_TYPE_CIDR: + r, err := IPListFromCIDR(input) + if err != nil { + return err + } + ips = append(ips, r...) + case INPUT_TYPE_FILE: + r, err := IPListFromFile(input) + if err != nil { + return err + } + ips = append(ips, r...) + default: + return ErrNotIP } + return nil + } - return nil, ErrInvalidInput + err := getInputFrom(inputs, true, op) + if err != nil { + return nil, err } return ips, nil } -// IPListFromAllSrcs is the same as IPListFrom with all flags turned on. -func IPListFromAllSrcs(inputs []string) ([]net.IP, error) { - return IPListFrom(inputs, true, true, true, true, true) -} - // IPListFromCIDR returns a list of IPs from a CIDR string. func IPListFromCIDR(cidrStr string) ([]net.IP, error) { _, ipnet, err := net.ParseCIDR(cidrStr) diff --git a/lib/utils_input.go b/lib/utils_input.go new file mode 100644 index 00000000..4c999444 --- /dev/null +++ b/lib/utils_input.go @@ -0,0 +1,129 @@ +package lib + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +type INPUT_TYPE string + +const ( + INPUT_TYPE_IP INPUT_TYPE = "IP" + INPUT_TYPE_IP_RANGE INPUT_TYPE = "IPRange" + INPUT_TYPE_CIDR INPUT_TYPE = "CIDR" + INPUT_TYPE_FILE INPUT_TYPE = "File" + INPUT_TYPE_ASN INPUT_TYPE = "ASN" + INPUT_TYPE_UNKNOWN INPUT_TYPE = "Unknown" +) + +// getInputFrom retrieves input data from various sources and processes it using the provided operation. +// The operation is called for each input string with input type. +// +// Usage: +// err := getInputFrom(inputs, +// true, +// func(input string, inputType INPUT_TYPE) error { +// switch inputType { +// case INPUT_TYPE_IP: +// // Process IP here +// default: +// return ErrNotIP +// } +// return nil +// }) +func getInputFrom( + inputs []string, + stdin bool, + op func(input string, inputType INPUT_TYPE) error, +) error { + if !stdin && len(inputs) == 0 { + return nil + } + + // start with stdin. + if stdin { + stat, _ := os.Stdin.Stat() + + isPiped := (stat.Mode() & os.ModeNamedPipe) != 0 + isTyping := (stat.Mode()&os.ModeCharDevice) != 0 && len(inputs) == 0 + + if isTyping { + fmt.Println("** manual input mode **") + fmt.Println("one input per line:") + } + + if isPiped || isTyping || stat.Size() > 0 { + inputs = append(inputs, ReadStringsFromStdin()...) + } + } + + // parse `inputs`. + for _, input := range inputs { + var err error + switch { + case StrIsIPStr(input): + err = op(input, INPUT_TYPE_IP) + case StrIsIPRangeStr(input): + err = op(input, INPUT_TYPE_IP_RANGE) + case StrIsCIDRStr(input): + err = op(input, INPUT_TYPE_CIDR) + case FileExists(input): + err = op(input, INPUT_TYPE_FILE) + case StrIsASNStr(input): + err = op(input, INPUT_TYPE_ASN) + default: + err = op(input, INPUT_TYPE_UNKNOWN) + } + if err != nil { + return err + } + } + return nil +} + +// readStringsFromFile reads strings from a file, one per line. +func readStringsFromFile(filename string) ([]string, error) { + var lines []string + + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + return // ignore errors on close + } + }(file) + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanWords) // Set the scanner to split on spaces and newlines + + for scanner.Scan() { + word := scanner.Text() + lines = append(lines, word) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return lines, nil +} + +// ReadStringsFromStdin reads strings from stdin until an empty line is entered. +func ReadStringsFromStdin() []string { + var inputLines []string + scanner := bufio.NewScanner(os.Stdin) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + break + } + inputLines = append(inputLines, line) + } + return inputLines +} From 20ed7eef66b873fa9a7774105219c80546ac77e7 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Wed, 16 Aug 2023 18:45:06 +0500 Subject: [PATCH 08/11] Incomplete. small fixes need to be made --- ipinfo/cmd_asn.go | 4 +++- ipinfo/cmd_init.go | 2 +- lib/cmd_asn_bulk.go | 10 +-------- lib/utils_input.go | 54 +++++++++++++++++++++++++++++---------------- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/ipinfo/cmd_asn.go b/ipinfo/cmd_asn.go index 721d9dbb..8edd324b 100644 --- a/ipinfo/cmd_asn.go +++ b/ipinfo/cmd_asn.go @@ -35,6 +35,8 @@ Options: `, progBase) } +func cmdASNDefaultHandler + // cmdASN is the handler for the "asn" command. func cmdASN() error { var err error @@ -60,7 +62,7 @@ func cmdASN() error { pflag.Parse() ii = prepareIpinfoClient(f.Token) - data, err := lib.CmdASNBulk(f, ii, lib.ReadStringsFromStdin(), printHelpASNBulk) + data, err := lib.CmdASNBulk(f, ii, lib.ProcessStringsFromStdin(), printHelpASNBulk) if err != nil { return err } diff --git a/ipinfo/cmd_init.go b/ipinfo/cmd_init.go index ebdf796e..0043f33a 100644 --- a/ipinfo/cmd_init.go +++ b/ipinfo/cmd_init.go @@ -134,7 +134,7 @@ func cmdInit() error { } // save token to file. - gConfig.Token = newtoken + gConfig.Token = tok if err := SaveConfig(gConfig); err != nil { return err } diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index 6a6095bb..701df3f8 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -67,21 +67,13 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f switch inputType { case INPUT_TYPE_ASN: asns = append(asns, strings.ToUpper(string)) - case INPUT_TYPE_FILE: - lines, err := readStringsFromFile(string) - if err != nil { - return err - } - for _, line := range lines { - asns = append(asns, strings.ToUpper(line)) - } default: return ErrInvalidInput } return nil } - err := getInputFrom(args, true, op) + err := getInputFrom(args, true, true, op) if err != nil { return nil, err } diff --git a/lib/utils_input.go b/lib/utils_input.go index 4c999444..7ef79fc3 100644 --- a/lib/utils_input.go +++ b/lib/utils_input.go @@ -13,11 +13,25 @@ const ( INPUT_TYPE_IP INPUT_TYPE = "IP" INPUT_TYPE_IP_RANGE INPUT_TYPE = "IPRange" INPUT_TYPE_CIDR INPUT_TYPE = "CIDR" - INPUT_TYPE_FILE INPUT_TYPE = "File" INPUT_TYPE_ASN INPUT_TYPE = "ASN" INPUT_TYPE_UNKNOWN INPUT_TYPE = "Unknown" ) +func inputHelper(str string, op func(string, INPUT_TYPE) error) error { + switch { + case StrIsIPStr(str): + return op(str, INPUT_TYPE_IP) + case StrIsIPRangeStr(str): + return op(str, INPUT_TYPE_IP_RANGE) + case StrIsCIDRStr(str): + return op(str, INPUT_TYPE_CIDR) + case StrIsASNStr(str): + return op(str, INPUT_TYPE_ASN) + default: + return op(str, INPUT_TYPE_UNKNOWN) + } +} + // getInputFrom retrieves input data from various sources and processes it using the provided operation. // The operation is called for each input string with input type. // @@ -36,6 +50,7 @@ const ( func getInputFrom( inputs []string, stdin bool, + file bool, op func(input string, inputType INPUT_TYPE) error, ) error { if !stdin && len(inputs) == 0 { @@ -55,7 +70,10 @@ func getInputFrom( } if isPiped || isTyping || stat.Size() > 0 { - inputs = append(inputs, ReadStringsFromStdin()...) + err := ProcessStringsFromStdin(op) + if err != nil { + return err + } } } @@ -69,8 +87,8 @@ func getInputFrom( err = op(input, INPUT_TYPE_IP_RANGE) case StrIsCIDRStr(input): err = op(input, INPUT_TYPE_CIDR) - case FileExists(input): - err = op(input, INPUT_TYPE_FILE) + case file && FileExists(input): + err = ProcessStringsFromFile(input, op) case StrIsASNStr(input): err = op(input, INPUT_TYPE_ASN) default: @@ -83,13 +101,11 @@ func getInputFrom( return nil } -// readStringsFromFile reads strings from a file, one per line. -func readStringsFromFile(filename string) ([]string, error) { - var lines []string - +// ProcessStringsFromFile reads strings from a file and passes it to op, one per line. +func ProcessStringsFromFile(filename string, op func(input string, inputType INPUT_TYPE) error) error { file, err := os.Open(filename) if err != nil { - return nil, err + return err } defer func(file *os.File) { err := file.Close() @@ -102,28 +118,28 @@ func readStringsFromFile(filename string) ([]string, error) { scanner.Split(bufio.ScanWords) // Set the scanner to split on spaces and newlines for scanner.Scan() { - word := scanner.Text() - lines = append(lines, word) + err = inputHelper(scanner.Text(), op) } if err := scanner.Err(); err != nil { - return nil, err + return err } - return lines, nil + return nil } -// ReadStringsFromStdin reads strings from stdin until an empty line is entered. -func ReadStringsFromStdin() []string { - var inputLines []string +// ProcessStringsFromStdin reads strings from stdin until an empty line is entered. +func ProcessStringsFromStdin(op func(input string, inputType INPUT_TYPE) error) error { scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { break } - inputLines = append(inputLines, line) + err := inputHelper(line, op) + if err != nil { + return err + } } - return inputLines + return nil } From 83d89fdf2aa87b1dc8ae3a51920a940e64db8807 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 17 Aug 2023 12:23:04 +0500 Subject: [PATCH 09/11] Making `op` work per input instead of creating arrays --- ipinfo/cmd_asn.go | 93 ++++++++++++++++++++++++++++----------------- lib/cmd_asn_bulk.go | 2 +- lib/ip_list.go | 8 +--- lib/utils_input.go | 4 +- 4 files changed, 63 insertions(+), 44 deletions(-) diff --git a/ipinfo/cmd_asn.go b/ipinfo/cmd_asn.go index 8edd324b..c0ace1b0 100644 --- a/ipinfo/cmd_asn.go +++ b/ipinfo/cmd_asn.go @@ -1,13 +1,15 @@ package main import ( + "errors" "fmt" "github.com/ipinfo/cli/lib" - "github.com/spf13/pflag" - "os" - "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" + "github.com/ipinfo/go/v2/ipinfo" + "github.com/spf13/pflag" + "os" + "strings" ) var completionsASN = &complete.Command{ @@ -35,7 +37,59 @@ Options: `, progBase) } -func cmdASNDefaultHandler +func cmdASNDefault() error { + // check whether the standard input (stdin) is being piped + // or redirected from another source or whether it's being read from the terminal (interactive mode). + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) != 0 { + printHelpASN() + return nil + } + + f := lib.CmdASNBulkFlags{} + f.Init() + pflag.Parse() + + var asns []string + + op := func(string string, inputType lib.INPUT_TYPE) error { + switch inputType { + case lib.INPUT_TYPE_ASN: + asns = append(asns, strings.ToUpper(string)) + default: + return lib.ErrInvalidInput + } + return nil + } + + err := lib.ProcessStringsFromStdin(op) + + ii = prepareIpinfoClient(f.Token) + if ii.Token == "" { + return errors.New("bulk lookups require a token; login via `ipinfo init`.") + } + + data, err := ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ + TimeoutPerBatch: 60 * 30, // 30min + ConcurrentBatchRequestsLimit: 20, + }) + if err != nil { + return err + } + if (data) == nil { + return nil + } + + if len(f.Field) > 0 { + return outputFieldBatchASNDetails(data, f.Field, false, false) + } + + if f.Yaml { + return outputYAML(data) + } + + return outputJSON(data) +} // cmdASN is the handler for the "asn" command. func cmdASN() error { @@ -49,36 +103,7 @@ func cmdASN() error { case cmd == "bulk": err = cmdASNBulk() default: - // check whether the standard input (stdin) is being piped - // or redirected from another source or whether it's being read from the terminal (interactive mode). - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) != 0 { - printHelpASN() - return nil - } - - f := lib.CmdASNBulkFlags{} - f.Init() - pflag.Parse() - - ii = prepareIpinfoClient(f.Token) - data, err := lib.CmdASNBulk(f, ii, lib.ProcessStringsFromStdin(), printHelpASNBulk) - if err != nil { - return err - } - if (data) == nil { - return nil - } - - if len(f.Field) > 0 { - return outputFieldBatchASNDetails(data, f.Field, false, false) - } - - if f.Yaml { - return outputYAML(data) - } - - return outputJSON(data) + err = cmdASNDefault() } if err != nil { diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index 701df3f8..578d1fb7 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -73,7 +73,7 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f return nil } - err := getInputFrom(args, true, true, op) + err := GetInputFrom(args, true, true, op) if err != nil { return nil, err } diff --git a/lib/ip_list.go b/lib/ip_list.go index 1bbe4f64..7fb332f2 100644 --- a/lib/ip_list.go +++ b/lib/ip_list.go @@ -29,19 +29,13 @@ func IPListFromAllSrcs(inputs []string) ([]net.IP, error) { return err } ips = append(ips, r...) - case INPUT_TYPE_FILE: - r, err := IPListFromFile(input) - if err != nil { - return err - } - ips = append(ips, r...) default: return ErrNotIP } return nil } - err := getInputFrom(inputs, true, op) + err := GetInputFrom(inputs, true, true, op) if err != nil { return nil, err } diff --git a/lib/utils_input.go b/lib/utils_input.go index 7ef79fc3..fb55bbaf 100644 --- a/lib/utils_input.go +++ b/lib/utils_input.go @@ -32,7 +32,7 @@ func inputHelper(str string, op func(string, INPUT_TYPE) error) error { } } -// getInputFrom retrieves input data from various sources and processes it using the provided operation. +// GetInputFrom retrieves input data from various sources and processes it using the provided operation. // The operation is called for each input string with input type. // // Usage: @@ -47,7 +47,7 @@ func inputHelper(str string, op func(string, INPUT_TYPE) error) error { // } // return nil // }) -func getInputFrom( +func GetInputFrom( inputs []string, stdin bool, file bool, From bbcbdb4cd62c762c5203754af54761f83693ac87 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 17 Aug 2023 12:48:14 +0500 Subject: [PATCH 10/11] Refactored the default `piped` case --- ipinfo/cmd_asn.go | 53 ++---------------------------------------- ipinfo/cmd_asn_bulk.go | 9 +++++-- lib/cmd_asn_bulk.go | 1 - 3 files changed, 9 insertions(+), 54 deletions(-) diff --git a/ipinfo/cmd_asn.go b/ipinfo/cmd_asn.go index c0ace1b0..81196801 100644 --- a/ipinfo/cmd_asn.go +++ b/ipinfo/cmd_asn.go @@ -1,15 +1,10 @@ package main import ( - "errors" "fmt" - "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" - "github.com/ipinfo/go/v2/ipinfo" - "github.com/spf13/pflag" "os" - "strings" ) var completionsASN = &complete.Command{ @@ -23,7 +18,6 @@ var completionsASN = &complete.Command{ } func printHelpASN() { - fmt.Printf( `Usage: %s asn [] @@ -45,50 +39,7 @@ func cmdASNDefault() error { printHelpASN() return nil } - - f := lib.CmdASNBulkFlags{} - f.Init() - pflag.Parse() - - var asns []string - - op := func(string string, inputType lib.INPUT_TYPE) error { - switch inputType { - case lib.INPUT_TYPE_ASN: - asns = append(asns, strings.ToUpper(string)) - default: - return lib.ErrInvalidInput - } - return nil - } - - err := lib.ProcessStringsFromStdin(op) - - ii = prepareIpinfoClient(f.Token) - if ii.Token == "" { - return errors.New("bulk lookups require a token; login via `ipinfo init`.") - } - - data, err := ii.GetASNDetailsBatch(asns, ipinfo.BatchReqOpts{ - TimeoutPerBatch: 60 * 30, // 30min - ConcurrentBatchRequestsLimit: 20, - }) - if err != nil { - return err - } - if (data) == nil { - return nil - } - - if len(f.Field) > 0 { - return outputFieldBatchASNDetails(data, f.Field, false, false) - } - - if f.Yaml { - return outputYAML(data) - } - - return outputJSON(data) + return cmdASNBulk(true) } // cmdASN is the handler for the "asn" command. @@ -101,7 +52,7 @@ func cmdASN() error { switch { case cmd == "bulk": - err = cmdASNBulk() + err = cmdASNBulk(false) default: err = cmdASNDefault() } diff --git a/ipinfo/cmd_asn_bulk.go b/ipinfo/cmd_asn_bulk.go index 7c15b529..3ee55547 100644 --- a/ipinfo/cmd_asn_bulk.go +++ b/ipinfo/cmd_asn_bulk.go @@ -64,13 +64,18 @@ Options: } // cmdASNBulk is the asn bulk command. -func cmdASNBulk() error { +func cmdASNBulk(piped bool) error { f := lib.CmdASNBulkFlags{} f.Init() pflag.Parse() ii = prepareIpinfoClient(f.Token) - data, err := lib.CmdASNBulk(f, ii, pflag.Args()[2:], printHelpASNBulk) + var args []string + if !piped { + args = pflag.Args()[2:] + } + + data, err := lib.CmdASNBulk(f, ii, args, printHelpASNBulk) if err != nil { return err } diff --git a/lib/cmd_asn_bulk.go b/lib/cmd_asn_bulk.go index 578d1fb7..5915db9b 100644 --- a/lib/cmd_asn_bulk.go +++ b/lib/cmd_asn_bulk.go @@ -72,7 +72,6 @@ func CmdASNBulk(f CmdASNBulkFlags, ii *ipinfo.Client, args []string, printHelp f } return nil } - err := GetInputFrom(args, true, true, op) if err != nil { return nil, err From 10fd25810d2eef73c502d4ed2a5ef58e83693f03 Mon Sep 17 00:00:00 2001 From: Uman Shahzad Date: Fri, 18 Aug 2023 09:15:18 +0500 Subject: [PATCH 11/11] Apply suggestions from code review --- lib/utils_input.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/utils_input.go b/lib/utils_input.go index fb55bbaf..1144b50a 100644 --- a/lib/utils_input.go +++ b/lib/utils_input.go @@ -36,7 +36,8 @@ func inputHelper(str string, op func(string, INPUT_TYPE) error) error { // The operation is called for each input string with input type. // // Usage: -// err := getInputFrom(inputs, +// err := GetInputFrom(inputs, +// true, // true, // func(input string, inputType INPUT_TYPE) error { // switch inputType { @@ -46,7 +47,8 @@ func inputHelper(str string, op func(string, INPUT_TYPE) error) error { // return ErrNotIP // } // return nil -// }) +// }, +// ) func GetInputFrom( inputs []string, stdin bool,