From 7dc3fca83ea03590329cbf33d3da004fbf30ab21 Mon Sep 17 00:00:00 2001 From: p4u Date: Fri, 30 Aug 2024 10:19:19 +0200 Subject: [PATCH] add a very simple go shell client for census3 Signed-off-by: p4u --- cmd/cli/main.go | 228 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 + go.sum | 4 + 3 files changed, 235 insertions(+) create mode 100644 cmd/cli/main.go diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 00000000..eadce24c --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,228 @@ +package main + +import ( + "fmt" + "log" + "net/url" + "os" + "strconv" + "strings" + + "github.com/fatih/color" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/vocdoni/census3/api" + "github.com/vocdoni/census3/apiclient" +) + +var client *apiclient.HTTPclient + +func main() { + var apiUrl string + var authToken string + + // Root command + rootCmd := &cobra.Command{ + Use: "census3-cli", + Short: "Census3 API Interactive Client", + Long: "An interactive client to interact with the Census3 API using Go.", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + // Setup client before running commands + u, err := url.Parse(apiUrl) + if err != nil { + log.Fatalf("Invalid API URL: %v", err) + } + client, err = apiclient.NewHTTPclient(u, nil) + if err != nil { + log.Fatalf("Failed to create API client: %v", err) + } + + if authToken != "" { + t := uuid.MustParse(authToken) + client.SetAuthToken(&t) + } + }, + } + + rootCmd.PersistentFlags().StringVar(&apiUrl, "api-url", "https://census3.vocdoni.net/api", "Census3 API URL") + rootCmd.PersistentFlags().StringVar(&authToken, "auth-token", "", "Bearer authentication token") + + // Info command + infoCmd := &cobra.Command{ + Use: "info", + Short: "Get API information", + Run: func(cmd *cobra.Command, args []string) { + info, err := client.Info() + if err != nil { + color.Red("Error fetching API info: %v", err) + return + } + color.Green("API Information:") + fmt.Printf(" Supported Chains: %v\n", info.SupportedChains) + }, + } + rootCmd.AddCommand(infoCmd) + + // Tokens command + tokensCmd := &cobra.Command{ + Use: "tokens", + Short: "List all tokens", + Run: func(cmd *cobra.Command, args []string) { + tokens, err := client.Tokens(100, "", "") + if err != nil { + color.Red("Error fetching tokens: %v", err) + return + } + for _, token := range tokens { + color.Cyan("Token ID: %s\n", token.ID) + fmt.Printf(" Type: %s\n", token.Type) + fmt.Printf(" Chain ID: %d\n", token.ChainID) + fmt.Printf(" Tags: %v\n", token.Tags) + fmt.Println() + } + }, + } + rootCmd.AddCommand(tokensCmd) + + // Token command + tokenCmd := &cobra.Command{ + Use: "token [tokenID] [chainID]", + Short: "Get information about a specific token", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + tokenID := args[0] + chainID, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + color.Red("Invalid chain ID: %v", err) + return + } + + token, err := client.Token(tokenID, chainID, "") + if err != nil { + color.Red("Error fetching token: %v", err) + return + } + color.Green("Token Details:") + fmt.Printf(" ID: %s\n", token.ID) + fmt.Printf(" Type: %s\n", token.Type) + fmt.Printf(" Chain ID: %d\n", token.ChainID) + fmt.Printf(" Start Block: %d\n", token.StartBlock) + fmt.Printf(" Tags: %v\n", token.Tags) + }, + } + rootCmd.AddCommand(tokenCmd) + + // Add Token command + addTokenCmd := &cobra.Command{ + Use: "add_token", + Short: "Add a new token", + Run: func(cmd *cobra.Command, args []string) { + var tokenID, tokenType, chainID, startBlock, tags string + + fmt.Print("Enter Token ID: ") + if _, err := fmt.Scanln(&tokenID); err != nil { + color.Red("Invalid token ID: %v", err) + return + } + fmt.Print("Enter Token Type (e.g., erc20): ") + if _, err := fmt.Scanln(&tokenType); err != nil { + color.Red("Invalid token type: %v", err) + return + } + fmt.Print("Enter Chain ID: ") + if _, err := fmt.Scanln(&chainID); err != nil { + color.Red("Invalid chain ID: %v", err) + return + } + fmt.Print("Enter Start Block: ") + fmt.Scanln(&startBlock) + fmt.Print("Enter Tags (comma-separated): ") + fmt.Scanln(&tags) + + chainIDUint, err := strconv.ParseUint(chainID, 10, 64) + if err != nil { + color.Red("Invalid chain ID: %v", err) + return + } + startBlockUint, err := strconv.ParseUint(startBlock, 10, 64) + if err != nil { + startBlockUint = 0 + } + + token := &api.Token{ + ID: tokenID, + Type: tokenType, + ChainID: chainIDUint, + StartBlock: startBlockUint, + Tags: tags, + } + + if err := client.CreateToken(token); err != nil { + color.Red("Error creating token: %v", err) + return + } + color.Green("Token created successfully.") + }, + } + rootCmd.AddCommand(addTokenCmd) + + // Token command + holdersCmd := &cobra.Command{ + Use: "holders ", + Short: "Get the list of token holders for an strategy", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + strategyID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + color.Red("Invalid strategy ID: %v", err) + return + } + holders, err := client.AllHoldersByStrategy(strategyID, true) + if err != nil { + color.Red("Error fetching holders: %v", err) + return + } + color.Green("Holders:") + for addr, amount := range holders { + fmt.Printf(" %s %s\n", addr.String(), amount.String()) + } + }, + } + rootCmd.AddCommand(holdersCmd) + + // Strategies command + strategiesCmd := &cobra.Command{ + Use: "strategies [tokenID]", + Short: "List all strategies (filter by token if provided)", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + strategies, err := client.Strategies(-1, "", "") + if err != nil { + color.Red("Error fetching strategies: %v", err) + return + } + + for _, strategy := range strategies { + if args[0] != "" && !strings.Contains(strategy.Predicate, args[0]) { + continue + } + color.Cyan("Strategy ID: %d\n", strategy.ID) + fmt.Printf(" Alias: %s\n", strategy.Alias) + fmt.Printf(" Predicate: %s\n", strategy.Predicate) + tokens := "" + for _, token := range strategy.Tokens { + tokens += token.ID + ", " + } + fmt.Printf(" URI: %s\n", strategy.URI) + fmt.Println() + } + }, + } + rootCmd.AddCommand(strategiesCmd) + + // Run the root command + if err := rootCmd.Execute(); err != nil { + log.Fatalf("Error: %v", err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index 84916d76..4a25710d 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,14 @@ toolchain go1.22.4 require ( github.com/VictoriaMetrics/metrics v1.24.0 github.com/ethereum/go-ethereum v1.14.0 + github.com/fatih/color v1.16.0 github.com/frankban/quicktest v1.14.6 github.com/google/uuid v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/ipfs/go-cid v0.4.1 github.com/mattn/go-sqlite3 v1.14.22 github.com/pressly/goose/v3 v3.20.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 go.vocdoni.io/dvote v1.10.2-0.20240617105531-77480ae05205 @@ -102,6 +104,7 @@ require ( github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iden3/go-iden3-crypto v0.0.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs-shipyard/nopfs v0.0.12 // indirect github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c // indirect github.com/ipfs/bbloom v0.0.4 // indirect diff --git a/go.sum b/go.sum index 2c4ff7d3..1dcc6d4a 100644 --- a/go.sum +++ b/go.sum @@ -433,6 +433,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/iden3/go-iden3-crypto v0.0.13 h1:ixWRiaqDULNyIDdOWz2QQJG5t4PpNHkQk2P6GV94cok= github.com/iden3/go-iden3-crypto v0.0.13/go.mod h1:swXIv0HFbJKobbQBtsB50G7IHr6PbTowutSew/iBEoo= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs-shipyard/nopfs v0.0.12 h1:mvwaoefDF5VI9jyvgWCmaoTJIJFAfrbyQV5fJz35hlk= github.com/ipfs-shipyard/nopfs v0.0.12/go.mod h1:mQyd0BElYI2gB/kq/Oue97obP4B3os4eBmgfPZ+hnrE= github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7UynTbtdlt+w08ggb1UGLGaGjp1mMaZhoTZSctpn5Ak= @@ -1016,6 +1018,8 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=