[#1223] lens/tui: Add detailed view for key-value records

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
Aleksey Savchuk 2024-07-26 12:57:47 +03:00
parent 249f621b56
commit cd282edfd5
No known key found for this signature in database
6 changed files with 111 additions and 55 deletions

View file

@ -42,7 +42,10 @@ func NewApplication(ctx context.Context, db *bbolt.DB) (*Application, error) {
nav: NewNavigation(app),
}
a.nav.Push(ctx, a.NewIntoView)
err := a.nav.Push(ctx, a.NewIntoView)
if err != nil {
return nil, err
}
go func() {
if err := <-a.errCh; err != nil {
@ -63,7 +66,10 @@ func (a *Application) NewIntoView(ctx context.Context) (tview.Primitive, error)
view.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEnter {
a.nav.Push(ctx, a.NewBucketsView)
err := a.nav.Push(ctx, a.NewBucketsView)
if err != nil {
a.errCh <- err
}
return nil
}
return event
@ -84,13 +90,7 @@ func (a *Application) NewBucketsView(ctx context.Context) (tview.Primitive, erro
SetExpanded(true).
SetReference(&Bucket{Handler: handlers.StartHandler})
err := a.GetChildren(ctx, root, func(a *handlers.KV) bool {
return true
// id := cid.ID{}
// x, _ := base58.Decode("FSTsE9ztCwheSyhj1cew9ScMX6UX9eS3Y9mcmJitc99E")
// id.Decode(x)
// return a.Key.HasCID(id)
})
err := a.GetChildren(ctx, root, func(_ *handlers.KV) bool { return true })
if err != nil {
return nil, err
}
@ -102,7 +102,7 @@ func (a *Application) NewBucketsView(ctx context.Context) (tview.Primitive, erro
tree.SetSelectedFunc(func(node *tview.TreeNode) { node.SetExpanded(!node.IsExpanded()) })
a.buckets = tree
a.setBucketsInputCapture()
a.setBucketsInputCapture(ctx)
return tree, nil
}
@ -148,16 +148,18 @@ func (a *Application) GetChildren(
return nil
}
func (a *Application) setBucketsInputCapture() {
func (a *Application) setBucketsInputCapture(ctx context.Context) {
a.buckets.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyTab:
a.nav.Push(context.Background(), func(ctx context.Context) (tview.Primitive, error) {
err := a.nav.Push(ctx, func(ctx context.Context) (tview.Primitive, error) {
ref := a.buckets.GetCurrentNode().GetReference().(*Bucket)
return a.NewRecordsView(ctx, ref), nil
})
// case tcell.KeyEsc:
// a.history.Pop()
if err != nil {
a.errCh <- err
return nil
}
default:
return event
}
@ -165,11 +167,6 @@ func (a *Application) setBucketsInputCapture() {
})
}
// func (a *Application) bucketsExpandCollapseInputHandler() {
// node := a.buckets.GetCurrentNode()
// node.SetExpanded(!node.IsExpanded())
// }
func (a *Application) NewRecordsView(ctx context.Context, ref *Bucket) tview.Primitive {
view := NewMultipageView(a.app)
view.SetBorder(true)
@ -203,12 +200,41 @@ func (a *Application) NewRecordsView(ctx context.Context, ref *Bucket) tview.Pri
a.nav.Pop()
return nil
}
if event.Key() == tcell.KeyTab {
item := view.GetSelectedItem()
if item == nil {
return nil
}
err := a.nav.Push(ctx, func(_ context.Context) (tview.Primitive, error) {
kv := item.Reference.(*handlers.KV)
text := tview.NewTextView()
text.SetBorder(true).SetTitle(kv.Key.String())
text.SetText(kv.Val.DetailedString())
text.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyBacktab {
a.nav.Pop()
return nil
}
return event
})
return text, nil
})
if err != nil {
a.errCh <- err
return nil
}
}
return event
})
view.OnLoading(
func() {
a.nav.Push(ctx, a.NewLoadingScreen)
err := a.nav.Push(ctx, a.NewLoadingScreen)
if err != nil {
a.errCh <- err
}
},
func() { a.nav.Pop() },
)

View file

@ -55,45 +55,47 @@ func load(
buffer := make(chan any, bufferSize)
errCh := make(chan error)
go db.View(func(tx *bbolt.Tx) error {
defer close(buffer)
go func() {
// TODO how to handle an error
_ = db.View(func(tx *bbolt.Tx) error {
defer close(buffer)
var cursor *bbolt.Cursor
if len(path) == 0 {
cursor = tx.Cursor()
} else {
bucket, err := follow(tx, path)
if err != nil {
errCh <- fmt.Errorf("can't find bucket: %w", err)
var cursor *bbolt.Cursor
if len(path) == 0 {
cursor = tx.Cursor()
} else {
bucket, err := follow(tx, path)
if err != nil {
errCh <- fmt.Errorf("can't find bucket: %w", err)
}
cursor = bucket.Cursor()
}
cursor = bucket.Cursor()
}
close(errCh)
close(errCh)
key, value := cursor.First()
for {
select {
case <-ctx.Done():
return nil
default:
// End of iteration
if key == nil {
key, value := cursor.First()
for {
select {
case <-ctx.Done():
return nil
}
if filter != nil && !filter(key, value) {
default:
// End of iteration
if key == nil {
return nil
}
if filter != nil && !filter(key, value) {
key, value = cursor.Next()
continue
}
if len(buffer) == bufferSize {
continue
}
buffer <- transform(key, value)
key, value = cursor.Next()
continue
}
if len(buffer) == bufferSize {
continue
}
buffer <- transform(key, value)
key, value = cursor.Next()
}
}
})
})
}()
if err := <-errCh; err != nil {
return nil, err
}

View file

@ -211,8 +211,8 @@ func (v *MultipageView) InputHandler() func(event *tcell.EventKey, _ func(p tvie
v.moveToPrevPage()
v.selectLastItem()
}
// default:
// return
default:
return
}
})
}

View file

@ -7,11 +7,14 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema/parse/types"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/davecgh/go-spew/spew"
)
type Result interface {
fmt.Stringer
DetailedString() string
HasOID(id oid.ID) bool
HasCID(id cid.ID) bool
}
@ -36,6 +39,19 @@ func (r *ResultValue) String() string {
return s
}
func (r *ResultValue) DetailedString() string {
switch x := r.value.(type) {
case types.Object:
return spew.Sdump(x.Obj)
case types.OIDsList:
return spew.Sdump(x.OIDs)
case types.Empty:
return ""
default:
return spew.Sdump(x)
}
}
func (r *ResultValue) HasOID(id oid.ID) bool {
switch x := r.value.(type) {
case types.OID:
@ -74,6 +90,14 @@ func (r *ResultArray) String() string {
return strings.TrimRight(strings.Join(acc, " "), " ")
}
func (r *ResultArray) DetailedString() string {
b := strings.Builder{}
for _, result := range r.results {
_, _ = b.WriteString(result.DetailedString())
}
return b.String()
}
func (r *ResultArray) HasOID(id oid.ID) bool {
for _, x := range r.results {
if x.HasOID(id) {
@ -104,6 +128,10 @@ func (r *RawResult) String() string {
return r.s
}
func (r *RawResult) DetailedString() string {
return r.s + "\n"
}
func (r *RawResult) HasOID(_ oid.ID) bool {
return false
}

2
go.mod
View file

@ -16,6 +16,7 @@ require (
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/cheggaaa/pb v1.0.29
github.com/chzyer/readline v1.5.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
github.com/gdamore/tcell/v2 v2.7.1
github.com/go-pkgz/expirable-cache/v3 v3.0.0
@ -65,7 +66,6 @@ require (
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect

BIN
go.sum

Binary file not shown.