github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/slurp/options.go (about)

     1  // Copyright 2016, 2024 The TrueBlocks Authors. All rights reserved.
     2  // Use of this source code is governed by a license that can
     3  // be found in the LICENSE file.
     4  /*
     5   * Parts of this file were auto generated. Edit only those parts of
     6   * the code inside of 'EXISTING_CODE' tags.
     7   */
     8  
     9  package slurpPkg
    10  
    11  import (
    12  	// EXISTING_CODE
    13  	"encoding/json"
    14  	"io"
    15  	"net/http"
    16  	"net/url"
    17  	"strings"
    18  
    19  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals"
    20  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    21  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps"
    22  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
    23  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    24  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc"
    25  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/provider"
    26  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate"
    27  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk"
    28  	// EXISTING_CODE
    29  )
    30  
    31  // SlurpOptions provides all command options for the chifra slurp command.
    32  type SlurpOptions struct {
    33  	Addrs       []string                 `json:"addrs,omitempty"`       // One or more addresses to slurp from Etherscan
    34  	Blocks      []string                 `json:"blocks,omitempty"`      // An optional range of blocks to slurp
    35  	BlockIds    []identifiers.Identifier `json:"blockIds,omitempty"`    // Block identifiers
    36  	Parts       []string                 `json:"parts,omitempty"`       // Which types of transactions to request
    37  	Appearances bool                     `json:"appearances,omitempty"` // Show only the blocknumber.tx_id appearances of the exported transactions
    38  	Articulate  bool                     `json:"articulate,omitempty"`  // Articulate the retrieved data if ABIs can be found
    39  	Source      string                   `json:"source,omitempty"`      // The source of the slurped data
    40  	Count       bool                     `json:"count,omitempty"`       // For --appearances mode only, display only the count of records
    41  	Page        uint64                   `json:"page,omitempty"`        // The page to retrieve (page number)
    42  	PageId      string                   `json:"pageId,omitempty"`      // The page to retrieve (page ID)
    43  	PerPage     uint64                   `json:"perPage,omitempty"`     // The number of records to request on each page
    44  	Sleep       float64                  `json:"sleep,omitempty"`       // Seconds to sleep between requests
    45  	Types       []string                 `json:"types,omitempty"`       // Deprecated, use --parts instead
    46  	Globals     globals.GlobalOptions    `json:"globals,omitempty"`     // The global options
    47  	Conn        *rpc.Connection          `json:"conn,omitempty"`        // The connection to the RPC server
    48  	BadFlag     error                    `json:"badFlag,omitempty"`     // An error flag if needed
    49  	// EXISTING_CODE
    50  	// EXISTING_CODE
    51  }
    52  
    53  var defaultSlurpOptions = SlurpOptions{
    54  	Source:  "etherscan",
    55  	PerPage: 1000,
    56  	Sleep:   .25,
    57  }
    58  
    59  // testLog is used only during testing to export the options for this test case.
    60  func (opts *SlurpOptions) testLog() {
    61  	logger.TestLog(len(opts.Addrs) > 0, "Addrs: ", opts.Addrs)
    62  	logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks)
    63  	logger.TestLog(len(opts.Parts) > 0, "Parts: ", opts.Parts)
    64  	logger.TestLog(opts.Appearances, "Appearances: ", opts.Appearances)
    65  	logger.TestLog(opts.Articulate, "Articulate: ", opts.Articulate)
    66  	logger.TestLog(len(opts.Source) > 0 && opts.Source != "etherscan", "Source: ", opts.Source)
    67  	logger.TestLog(opts.Count, "Count: ", opts.Count)
    68  	logger.TestLog(opts.Page != 0, "Page: ", opts.Page)
    69  	logger.TestLog(len(opts.PageId) > 0, "PageId: ", opts.PageId)
    70  	logger.TestLog(opts.PerPage != 1000, "PerPage: ", opts.PerPage)
    71  	logger.TestLog(opts.Sleep != float64(.25), "Sleep: ", opts.Sleep)
    72  	opts.Conn.TestLog(opts.getCaches())
    73  	opts.Globals.TestLog()
    74  }
    75  
    76  // String implements the Stringer interface
    77  func (opts *SlurpOptions) String() string {
    78  	b, _ := json.MarshalIndent(opts, "", "  ")
    79  	return string(b)
    80  }
    81  
    82  // slurpFinishParseApi finishes the parsing for server invocations. Returns a new SlurpOptions.
    83  func slurpFinishParseApi(w http.ResponseWriter, r *http.Request) *SlurpOptions {
    84  	values := r.URL.Query()
    85  	if r.Header.Get("User-Agent") == "testRunner" {
    86  		values.Set("testRunner", "true")
    87  	}
    88  	return SlurpFinishParseInternal(w, values)
    89  }
    90  
    91  func SlurpFinishParseInternal(w io.Writer, values url.Values) *SlurpOptions {
    92  	copy := defaultSlurpOptions
    93  	copy.Globals.Caps = getCaps()
    94  	opts := &copy
    95  	opts.Source = "etherscan"
    96  	opts.PerPage = 1000
    97  	opts.Sleep = .25
    98  	for key, value := range values {
    99  		switch key {
   100  		case "addrs":
   101  			for _, val := range value {
   102  				s := strings.Split(val, " ") // may contain space separated items
   103  				opts.Addrs = append(opts.Addrs, s...)
   104  			}
   105  		case "blocks":
   106  			for _, val := range value {
   107  				s := strings.Split(val, " ") // may contain space separated items
   108  				opts.Blocks = append(opts.Blocks, s...)
   109  			}
   110  		case "parts":
   111  			for _, val := range value {
   112  				s := strings.Split(val, " ") // may contain space separated items
   113  				opts.Parts = append(opts.Parts, s...)
   114  			}
   115  		case "appearances":
   116  			opts.Appearances = true
   117  		case "articulate":
   118  			opts.Articulate = true
   119  		case "source":
   120  			opts.Source = value[0]
   121  		case "count":
   122  			opts.Count = true
   123  		case "page":
   124  			opts.Page = base.MustParseUint64(value[0])
   125  		case "pageId":
   126  			opts.PageId = value[0]
   127  		case "perPage":
   128  			opts.PerPage = base.MustParseUint64(value[0])
   129  		case "sleep":
   130  			opts.Sleep = base.MustParseFloat64(value[0])
   131  		case "types":
   132  			for _, val := range value {
   133  				s := strings.Split(val, " ") // may contain space separated items
   134  				opts.Types = append(opts.Types, s...)
   135  			}
   136  		default:
   137  			if !copy.Globals.Caps.HasKey(key) {
   138  				err := validate.Usage("Invalid key ({0}) in {1} route.", key, "slurp")
   139  				if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() {
   140  					opts.BadFlag = err
   141  				}
   142  			}
   143  		}
   144  	}
   145  	opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches())
   146  
   147  	// Deprecated...
   148  	if len(opts.Types) > 0 && len(opts.Parts) == 0 {
   149  		logger.Warn("The --types flag is deprecated. Please use --parts instead.")
   150  		opts.Parts = opts.Types
   151  		opts.Types = []string{}
   152  	}
   153  
   154  	// EXISTING_CODE
   155  	for _, t := range opts.Parts {
   156  		if t == "all" {
   157  			opts.Parts = []string{"ext", "int", "token", "nfts", "1155", "miner", "uncles", "withdrawals"}
   158  			break
   159  		}
   160  	}
   161  	if len(opts.Parts) == 0 {
   162  		opts.Parts = []string{"ext"}
   163  	}
   164  	// EXISTING_CODE
   165  	opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs)
   166  
   167  	return opts
   168  }
   169  
   170  // slurpFinishParse finishes the parsing for command line invocations. Returns a new SlurpOptions.
   171  func slurpFinishParse(args []string) *SlurpOptions {
   172  	// remove duplicates from args if any (not needed in api mode because the server does it).
   173  	dedup := map[string]int{}
   174  	if len(args) > 0 {
   175  		tmp := []string{}
   176  		for _, arg := range args {
   177  			if value := dedup[arg]; value == 0 {
   178  				tmp = append(tmp, arg)
   179  			}
   180  			dedup[arg]++
   181  		}
   182  		args = tmp
   183  	}
   184  
   185  	defFmt := "txt"
   186  	opts := GetOptions()
   187  	opts.Conn = opts.Globals.FinishParse(args, opts.getCaches())
   188  
   189  	// Deprecated...
   190  	if len(opts.Types) > 0 && len(opts.Parts) == 0 {
   191  		logger.Warn("The --types flag is deprecated. Please use --parts instead.")
   192  		opts.Parts = opts.Types
   193  		opts.Types = []string{}
   194  	}
   195  
   196  	// EXISTING_CODE
   197  	for _, arg := range args {
   198  		if base.IsValidAddress(arg) {
   199  			opts.Addrs = append(opts.Addrs, arg)
   200  		} else {
   201  			opts.Blocks = append(opts.Blocks, arg)
   202  		}
   203  	}
   204  	for _, t := range opts.Parts {
   205  		if t == "all" {
   206  			opts.Parts = []string{"ext", "int", "token", "nfts", "1155", "miner", "uncles", "withdrawals"}
   207  			break
   208  		}
   209  	}
   210  	if len(opts.Parts) == 0 {
   211  		opts.Parts = []string{"ext"}
   212  	}
   213  	// EXISTING_CODE
   214  	opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs)
   215  	if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" {
   216  		opts.Globals.Format = defFmt
   217  	}
   218  
   219  	return opts
   220  }
   221  
   222  func GetOptions() *SlurpOptions {
   223  	// EXISTING_CODE
   224  	// EXISTING_CODE
   225  	return &defaultSlurpOptions
   226  }
   227  
   228  func getCaps() caps.Capability {
   229  	var capabilities caps.Capability // capabilities for chifra slurp
   230  	capabilities = capabilities.Add(caps.Default)
   231  	capabilities = capabilities.Add(caps.Caching)
   232  	capabilities = capabilities.Add(caps.Ether)
   233  	capabilities = capabilities.Add(caps.Names)
   234  	// EXISTING_CODE
   235  	// EXISTING_CODE
   236  	return capabilities
   237  }
   238  
   239  func ResetOptions(testMode bool) {
   240  	// We want to keep writer between command file calls
   241  	w := GetOptions().Globals.Writer
   242  	opts := SlurpOptions{}
   243  	globals.SetDefaults(&opts.Globals)
   244  	opts.Globals.TestMode = testMode
   245  	opts.Globals.Writer = w
   246  	opts.Globals.Caps = getCaps()
   247  	opts.Source = "etherscan"
   248  	opts.PerPage = 1000
   249  	opts.Sleep = .25
   250  	defaultSlurpOptions = opts
   251  }
   252  
   253  func (opts *SlurpOptions) getCaches() (caches map[walk.CacheType]bool) {
   254  	// EXISTING_CODE
   255  	caches = map[walk.CacheType]bool{
   256  		walk.Cache_Transactions: true,
   257  	}
   258  	// EXISTING_CODE
   259  	return
   260  }
   261  
   262  // EXISTING_CODE
   263  func (opts *SlurpOptions) Addresses() []base.Address {
   264  	addresses := make([]base.Address, 0, len(opts.Addrs))
   265  	for _, addr := range opts.Addrs {
   266  		addresses = append(addresses, base.HexToAddress(addr))
   267  	}
   268  	return addresses
   269  }
   270  
   271  func (opts *SlurpOptions) Query() *provider.Query {
   272  	return &provider.Query{
   273  		Addresses:   opts.Addresses(),
   274  		Resources:   opts.Parts,
   275  		PerPage:     uint(opts.PerPage),
   276  		StartPage:   uint(opts.Page),
   277  		StartPageId: opts.PageId,
   278  		BlockRange:  opts.BlockIds,
   279  	}
   280  }
   281  
   282  // Provider returns 3rd party RPC provider based on --source
   283  func (opts *SlurpOptions) Provider() (provider.Provider, error) {
   284  	switch opts.Source {
   285  	case "key":
   286  		return provider.NewKeyProvider(opts.Conn, opts.Globals.Chain)
   287  	case "covalent":
   288  		return provider.NewCovalentProvider(opts.Conn, opts.Globals.Chain)
   289  	case "alchemy":
   290  		return provider.NewAlchemyProvider(opts.Conn, opts.Globals.Chain)
   291  	case "etherscan":
   292  		fallthrough
   293  	default:
   294  		return provider.NewEtherscanProvider(opts.Conn)
   295  	}
   296  }
   297  
   298  // EXISTING_CODE