Skip to content

Commit

Permalink
Complete re-write of wizard along with testing
Browse files Browse the repository at this point in the history
  • Loading branch information
tjayrush committed Jan 20, 2025
1 parent c09e28c commit b510b6a
Show file tree
Hide file tree
Showing 18 changed files with 1,156 additions and 491 deletions.
37 changes: 25 additions & 12 deletions app/action_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app

import (
"fmt"
"strings"

"github.com/TrueBlocks/trueblocks-khedra/v2/app/wizard"
"github.com/urfave/cli/v2"
Expand All @@ -13,30 +14,42 @@ func (k *KhedraApp) initAction(c *cli.Context) error {
return fmt.Errorf("failed to load config: %w", err)
}

steps := []wizard.Step{
wizard.NewScreen(welcomeTitle, welcomeSubtitle, welcomeBody, welcomeOptions),
wizard.NewScreen(generalTitle, generalSubtitle, generalBody, generalOptions),
wizard.NewScreen(servicesTitle, servicesSubtitle, servicesBody, servicesOptions),
wizard.NewScreen(chainsTitle, chainsSubtitle, chainsBody, chainsOptions),
wizard.NewScreen(loggingTitle, loggingSubtitle, loggingBody, loggingOptions),
steps := []wizard.Screen{
wizard.AddScreen(getWelcomeScreen(k.config)),
wizard.AddScreen(getGeneralScreen(k.config)),
wizard.AddScreen(getServicesScreen(k.config)),
wizard.AddScreen(getChainsScreen(k.config)),
wizard.AddScreen(getSummaryScreen(k.config)),
}

w := wizard.NewWizard(steps, "")

if err := w.Run(); err != nil {
return err
}

return nil
}

var generalScreen = `Where should Khedra store its data?`

var servicesScreen = `There are four services. Which do you want to enable?`
func validWarn(msg, value string) error {
if strings.Contains(msg, "%s") {
return fmt.Errorf(msg+"%w", value, wizard.ErrValidateWarn)
}
return fmt.Errorf(msg+"%w", wizard.ErrValidateWarn)
}

var chainsScreen = `Which chains do you want to support?`
func validOk(msg, value string) error {
if strings.Contains(msg, "%s") {
return fmt.Errorf(msg+"%w", value, wizard.ErrValidateMsg)
}
return fmt.Errorf(msg+"%w", wizard.ErrValidateMsg)
}

var loggingScreen = `Where should we store log files?`
func validSkipNext(msg, value string) error {
if strings.Contains(msg, "%s") {
return fmt.Errorf(msg+"%w", value, wizard.ErrSkipQuestion)
}
return fmt.Errorf(msg+"%w", wizard.ErrSkipQuestion)
}

// func (k *KhedraApp) Initialize() error {
// k.Info("Test log: Scraper initialization started")
Expand Down
63 changes: 26 additions & 37 deletions app/action_init_chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,33 @@ package app
import (
"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors"
"github.com/TrueBlocks/trueblocks-khedra/v2/app/wizard"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/types"
)

var chainsTitle = `Chains`
var chainsSubtitle = `pg 1/1`
var chainsBody = `
This wizard helps you configure Khedra. It will walk you through four
sections General, Services, Chains, and Logging.
func getChainsScreen(cfg *types.Config) wizard.Screen {
_ = cfg // linter
var chainsScreen = wizard.Screen{
Title: `Chains Settings`,
Subtitle: ``,
Body: `
You may use Khedra against any EVM chain, however in order for it to
work, you must provide an RPC endpoint for the chain. Generally, the
tools work much better with a locally running node, but it does work
with remove RPCs (just not as fast).
`,
Instructions: `Type your answers and press enter. ("b"=back, "q"=quit)`,
Replacements: []wizard.Replacement{
{Color: colors.Yellow, Values: []string{"Chains Settings", "remove <chain>"}},
},
Questions: []wizard.Question{
{
Text: `Which chains do you want to index? (Enter a chain's name directly
to add chains or "remove <chain>" to remove them.)`,
Value: "mainnet, gnosis, sepolia, optimism",
},
},
Style: wizard.NewStyle(),
}

You may quit the wizard at any time by typing "q" or "quit". The next
time you run it, it will continue where you left off. Type "help" at any
point to get more information.
Type your answer below a press enter to continue.
`
var chainsOptions = wizard.Option{
Replacements: []wizard.Replacement{
{Color: colors.Yellow, Values: []string{"KHEDRA WIZARD"}},
{Color: colors.Green, Values: []string{"General", "Services", "Chains", "Logging"}},
},
return chainsScreen
}

// var chainsScreen = `
// 80 ┌──────────────────────────────────────────────────────────────────────────────┐
// │ │
// │ 34 ╔════════════════════════════════╗ │
// │ ║ KHEDRA ║ │
// │ ║ ║ │
// │ ║ Index, monitor, serve, ║ │
// │ ║ and share blockchain data. ║ │
// │ ╚════════════════════════════════╝ │
// │ │
// │ │
// │ This wizard will help you configure Khedra. The wizard walks you through │
// │ four sections General, Services, Chains, and Logging. │
// │ │
// │ Type "help" at any point to get more information. You may quit at any │
// │ time. The wizard will start over where you left off next time. Type "q" │
// │ or "quit" at any time to exit. Press enter to continue. │
// │ │
// └──────────────────────────────────────────────────────────────────────────────┘
// `
213 changes: 180 additions & 33 deletions app/action_init_general.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,193 @@
package app

import (
"fmt"
"path/filepath"
"strings"

"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors"
"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file"
"github.com/TrueBlocks/trueblocks-khedra/v2/app/wizard"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/types"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/utils"
)

var generalTitle = `General`
var generalSubtitle = `pg 1/1`
var generalBody = `
This wizard helps you configure Khedra. It will walk you through four
sections General, Services, Chains, and Logging.
// --------------------------------------------------------
func getGeneralScreen(cfg *types.Config) wizard.Screen {
_ = cfg // linter

var generalTitle = `General Settings`
var generalSubtitle = ``
var generalInstructions = `
Type your answer and press enter. ("q"=quit, "b"=back, "h"=help)`
var generalBody = `
The General group of options controls where Khedra stores the Unchained
Index and its caches. It also helps you choose a download strategy for
the index and helps you set up Khedra's logging options.
Choose your folders carefully. The index and logs can get quite large
depending on the configuration. As always, type "help" to get more
information.
You may use $HOME or ~/ in your paths to refer to your home directory.`
var generalReplacements = []wizard.Replacement{
{Color: colors.Yellow, Values: []string{generalTitle}},
{Color: colors.Green, Values: []string{"Unchained\nIndex", "$HOME", "~/"}},
}
var generalQuestions = []wizard.Question{generalQ0, generalQ1, generalQ2, generalQ3, generalQ4}

for i := 0; i < len(generalQuestions); i++ {
q := &generalQuestions[i]
q.Value = strings.ReplaceAll(q.Value, "{cfg.General.DataFolder}", cfg.General.DataFolder)
for j := 0; j < len(q.Messages); j++ {
q.Messages[j] = strings.ReplaceAll(q.Messages[j], "{cfg.Logging.Filename}", cfg.Logging.Filename)
}
}

var generalScreen = wizard.Screen{
Title: generalTitle,
Subtitle: generalSubtitle,
Instructions: generalInstructions,
Body: generalBody,
Replacements: generalReplacements,
Questions: generalQuestions,
Style: wizard.NewStyle(),
}

return generalScreen
}

// --------------------------------------------------------
var generalQ0 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Text: `Should we create the Unchained Index from scratch (starting at
block zero) or download from IPFS?`,
Hint: `Downloading is faster. Building from scratch is more secure.`,
Value: "download",
Validate: func(input string, q *wizard.Question) (string, error) {
switch input {
case "download":
return input, validOk(q.Messages[0], input)
case "scratch":
return input, validOk(q.Messages[1], input)
default:
return input, fmt.Errorf(q.Messages[2]+"%w", wizard.ErrValidate)
}

},
Messages: []string{
`the index will be downloaded`,
`the index will be built from scratch`,
`value must be either "download" or "scratch"`,
},
Replacements: []wizard.Replacement{
{Color: colors.BrightBlue, Values: []string{"download", "scratch"}},
},
}

// --------------------------------------------------------
var generalQ1 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Text: `Do you want to download only bloom filters or the entire index?`,
Hint: `Downloading blooms takes less time and is smaller (4gb), but is
slower when searching. Downloading the entire index takes longer
and is larger (180gb), but is much faster during search.`,
Value: "entire index",
Validate: func(input string, q *wizard.Question) (string, error) {
switch input {
case "bloom filters":
return input, validOk(q.Messages[0], input)
case "entire index":
return input, validOk(q.Messages[1], input)
default:
return input, fmt.Errorf(q.Messages[2]+"%w", wizard.ErrValidate)
}
},
Messages: []string{
`only bloom filters will be downloaded`,
`both bloom filters and index chunks will be downloaded`,
`value must be either "bloom filters" or "entire index"`,
},
Replacements: []wizard.Replacement{
{Color: colors.BrightBlue, Values: []string{"bloom filters", "entire index"}},
},
PrepareFn: func(input string, q *wizard.Question) (string, error) {
if q.Screen.Questions[0].Value == "scratch" {
return input, validSkipNext(`question skipped`, input)
}
return input, validOk(`question proceeds`, input)
},
}

// --------------------------------------------------------
var generalQ2 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Text: `Where do you want to store the Unchained Index?`,
Value: `{cfg.General.DataFolder}`,
Validate: func(input string, q *wizard.Question) (string, error) {
path, err := utils.ResolveValidPath(input)
if err != nil {
return input, fmt.Errorf(q.Messages[2]+"%w", path, wizard.ErrValidate)
}

if !file.FolderExists(path) {
file.EstablishFolder(path)
return input, validWarn(q.Messages[0], path)
}

You may quit the wizard at any time by typing "q" or "quit". The next
time you run it, it will continue where you left off. Type "help" at any
point to get more information.
return input, validOk(q.Messages[1], path)
},
Messages: []string{
`"%s" was created`,
`the index will be stored at %s`,
"unable to create folder: %s",
},
}

Type your answer below a press enter to continue.
`
var generalOptions = wizard.Option{
// --------------------------------------------------------
var generalQ3 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Text: "Do you want to enable file-based logging?",
Value: "no",
Hint: `Logging to the screen is always enabled. If you enable file-based
logging, Khedra will also write log files to disk.`,
Validate: func(input string, q *wizard.Question) (string, error) {
prevScreen := 2
path := filepath.Join(q.Screen.Questions[prevScreen].Value, q.Messages[3])
switch input {
case "yes":
return input, validOk(q.Messages[0], path)
case "no":
return input, validOk(q.Messages[1], path)
default:
return input, fmt.Errorf(q.Messages[2]+"%w", wizard.ErrValidate)
}
},
Replacements: []wizard.Replacement{
{Color: colors.Yellow, Values: []string{"KHEDRA WIZARD"}},
{Color: colors.Green, Values: []string{"General", "Services", "Chains", "Logging"}},
{Color: colors.BrightBlue, Values: []string{"yes", "no"}},
},
Messages: []string{
`logs will be stored at %s`,
`logs will be reported to screen only`,
`value must be either "yes" or "no"`,
`{cfg.Logging.Filename}`,
},
}

// var generalScreen = `
// 80 ┌──────────────────────────────────────────────────────────────────────────────┐
// │ │
// │ 34 ╔════════════════════════════════╗ │
// │ ║ KHEDRA ║ │
// │ ║ ║ │
// │ ║ Index, monitor, serve, ║ │
// │ ║ and share blockchain data. ║ │
// │ ╚════════════════════════════════╝ │
// │ │
// │ │
// │ This wizard will help you configure Khedra. The wizard walks you through │
// │ four sections General, Services, Chains, and Logging. │
// │ │
// │ Type "help" at any point to get more information. You may quit at any │
// │ time. The wizard will start over where you left off next time. Type "q" │
// │ or "quit" at any time to exit. Press enter to continue. │
// │ │
// └──────────────────────────────────────────────────────────────────────────────┘
// `
// --------------------------------------------------------
var generalQ4 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Text: "What log level do you want to enable (debug, info, warn, error)?",
Value: "info",
Validate: func(input string, q *wizard.Question) (string, error) {
if input != "debug" && input != "info" && input != "warn" && input != "error" {
err := fmt.Errorf(`value must be either "debug", "info", "warn", or "error"%w`, wizard.ErrValidate)
return input, err
}
msg := fmt.Errorf(q.Messages[0]+"%w", input, wizard.ErrValidateMsg)
return input, msg
},
Messages: []string{
`logging level will be "%s"`,
},
}
Loading

0 comments on commit b510b6a

Please sign in to comment.