Have got the table view working again

This commit is contained in:
Leon Mika 2022-03-27 21:21:52 +00:00 committed by GitHub
parent 507226f571
commit 2638597f42
9 changed files with 238 additions and 43 deletions

View file

@ -4,7 +4,12 @@ import (
"context"
"flag"
"fmt"
"log"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@ -16,15 +21,13 @@ import (
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/lmika/awstools/internal/dynamo-browse/ui"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamotableview"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
"github.com/lmika/gopkgs/cli"
"log"
"os"
"time"
)
func main() {
@ -33,7 +36,13 @@ func main() {
flag.Parse()
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx)
// TEMP
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion("ap-southeast-2"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("abc", "123", "")))
// END TEMP
if err != nil {
cli.Fatalf("cannot load AWS config: %v", err)
}
@ -57,9 +66,9 @@ func main() {
tableWriteController := controllers.NewTableWriteController(tableService, tableReadController, *flagTable)
commandController := commandctrl.NewCommandController(map[string]uimodels.Operation{
"scan": tableReadController.Scan(),
"rw": tableWriteController.ToggleReadWrite(),
"dup": tableWriteController.Duplicate(),
// "scan": tableReadController.Scan(),
"rw": tableWriteController.ToggleReadWrite(),
"dup": tableWriteController.Duplicate(),
})
uiModel := ui.NewModel(uiDispatcher, commandController, tableReadController, tableWriteController)
@ -70,7 +79,8 @@ func main() {
var model tea.Model = statusandprompt.New(
layout.NewVBox(
frame.NewFrame("This is the header", true, layout.Model(newTestModel("this is the top"))),
layout.LastChildFixedAt(11),
frame.NewFrame("This is the header", true, dynamotableview.New(tableReadController)),
frame.NewFrame("This is another header", false, layout.Model(newTestModel("this is the bottom"))),
),
"Hello world",

View file

@ -2,8 +2,9 @@ package controllers
import (
"context"
"log"
"github.com/lmika/awstools/internal/common/ui/uimodels"
tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/models"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/pkg/errors"
@ -21,6 +22,30 @@ func NewTableReadController(tableService *tables.Service, tableName string) *Tab
}
}
func (c *TableReadController) Scan() tea.Cmd {
return func() tea.Msg {
ctx := context.Background()
log.Println("Fetching table info")
tableInfo, err := c.tableInfo(ctx)
if err != nil {
log.Println("error: ", err)
return err
}
log.Println("Scanning")
resultSet, err := c.tableService.Scan(ctx, tableInfo)
if err != nil {
log.Println("error: ", err)
return err
}
log.Println("Scan done")
return NewResultSet{resultSet}
}
}
/*
func (c *TableReadController) Scan() uimodels.Operation {
return uimodels.OperationFn(func(ctx context.Context) error {
return c.doScan(ctx, false)
@ -50,13 +75,16 @@ func (c *TableReadController) doScan(ctx context.Context, quiet bool) (err error
uiCtx.Send(NewResultSet{resultSet})
return nil
}
*/
// tableInfo returns the table info from the state if a result set exists. If not, it fetches the
// table information from the service.
func (c *TableReadController) tableInfo(ctx context.Context) (*models.TableInfo, error) {
if existingResultSet := CurrentState(ctx).ResultSet; existingResultSet != nil {
return existingResultSet.TableInfo, nil
}
/*
if existingResultSet := CurrentState(ctx).ResultSet; existingResultSet != nil {
return existingResultSet.TableInfo, nil
}
*/
tableInfo, err := c.tableService.Describe(ctx, c.tableName)
if err != nil {

View file

@ -81,9 +81,9 @@ func (c *TableWriteController) Duplicate() uimodels.Operation {
}
// Rescan to get updated items
if err := c.tableReadControllers.doScan(ctx, true); err != nil {
return err
}
// if err := c.tableReadControllers.doScan(ctx, true); err != nil {
// return err
// }
return nil
}))
@ -122,9 +122,9 @@ func (c *TableWriteController) Delete() uimodels.Operation {
}
// Rescan to get updated items
if err := c.tableReadControllers.doScan(ctx, true); err != nil {
return err
}
// if err := c.tableReadControllers.doScan(ctx, true); err != nil {
// return err
// }
uiCtx.Message("Item deleted")
return nil

View file

@ -2,11 +2,8 @@ package ui
import (
"context"
"fmt"
"strings"
"text/tabwriter"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
table "github.com/calyptia/go-bubble-table"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/viewport"
@ -83,6 +80,7 @@ func (m uiModel) Init() tea.Cmd {
return nil
}
/*
func (m *uiModel) updateTable() {
if !m.ready {
return
@ -99,6 +97,8 @@ func (m *uiModel) updateTable() {
m.table = newTbl
}
func (m *uiModel) selectedItem() (itemTableRow, bool) {
resultSet := m.state.ResultSet
if m.ready && resultSet != nil && len(resultSet.Items) > 0 {
@ -136,6 +136,7 @@ func (m *uiModel) updateViewportToSelectedMessage() {
tabWriter.Flush()
m.viewport.SetContent(viewportContent.String())
}
*/
func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var textInputCommands tea.Cmd
@ -145,8 +146,8 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Local events
case controllers.NewResultSet:
m.state.ResultSet = msg.ResultSet
m.updateTable()
m.updateViewportToSelectedMessage()
// m.updateTable()
// m.updateViewportToSelectedMessage()
case controllers.SetReadWrite:
m.state.InReadWriteMode = msg.NewValue
@ -203,16 +204,16 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Quit
case "up", "i":
m.table.GoUp()
m.updateViewportToSelectedMessage()
// m.updateViewportToSelectedMessage()
case "down", "k":
m.table.GoDown()
m.updateViewportToSelectedMessage()
// m.updateViewportToSelectedMessage()
// TODO: these should be moved somewhere else
case ":":
m.invokeOperation(context.Background(), m.commandController.Prompt())
case "s":
m.invokeOperation(context.Background(), m.tableReadController.Scan())
// case "s":
// m.invokeOperation(context.Background(), m.tableReadController.Scan())
case "D":
m.invokeOperation(context.Background(), m.tableWriteController.Delete())
}
@ -233,9 +234,9 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m uiModel) invokeOperation(ctx context.Context, op uimodels.Operation) {
state := m.state
if selectedItem, ok := m.selectedItem(); ok {
state.SelectedItem = selectedItem.item
}
// if selectedItem, ok := m.selectedItem(); ok {
// state.SelectedItem = selectedItem.item
// }
ctx = controllers.ContextWithState(ctx, state)
m.dispatcher.Start(ctx, op)

View file

@ -0,0 +1,120 @@
package dynamotableview
import (
table "github.com/calyptia/go-bubble-table"
tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
"github.com/lmika/awstools/internal/dynamo-browse/models"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
)
type Model struct {
tableReadControllers *controllers.TableReadController
table table.Model
w, h int
// model state
resultSet *models.ResultSet
}
func New(tableReadControllers *controllers.TableReadController) Model {
tbl := table.New([]string{"pk", "sk"}, 100, 100)
rows := make([]table.Row, 0)
tbl.SetRows(rows)
return Model{tableReadControllers: tableReadControllers, table: tbl}
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case controllers.NewResultSet:
m.resultSet = msg.ResultSet
m.updateTable()
return m, nil
case tea.KeyMsg:
switch msg.String() {
// Table nav
case "i", "up":
m.table.GoUp()
return m, nil
case "k", "down":
m.table.GoDown()
return m, nil
// TEMP
case "s":
return m, m.tableReadControllers.Scan()
case "ctrl+c", "esc":
return m, tea.Quit
}
}
return m, nil
}
func (m Model) View() string {
return m.table.View()
}
func (m Model) Resize(w, h int) layout.ResizingModel {
m.w, m.h = w, h
m.table.SetSize(w, h)
return m
}
func (m *Model) updateTable() {
resultSet := m.resultSet
newTbl := table.New(resultSet.Columns, m.w, m.h)
newRows := make([]table.Row, len(resultSet.Items))
for i, r := range resultSet.Items {
newRows[i] = itemTableRow{resultSet, r}
}
newTbl.SetRows(newRows)
m.table = newTbl
}
func (m *Model) selectedItem() (itemTableRow, bool) {
resultSet := m.resultSet
if resultSet != nil && len(resultSet.Items) > 0 {
selectedItem, ok := m.table.SelectedRow().(itemTableRow)
if ok {
return selectedItem, true
}
}
return itemTableRow{}, false
}
/*
func (m *Model) updateViewportToSelectedMessage() {
selectedItem, ok := m.selectedItem()
if !ok {
m.viewport.SetContent("(no row selected)")
return
}
viewportContent := &strings.Builder{}
tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0)
for _, colName := range selectedItem.resultSet.Columns {
switch colVal := selectedItem.item[colName].(type) {
case nil:
break
case *types.AttributeValueMemberS:
fmt.Fprintf(tabWriter, "%v\tS\t%s\n", colName, colVal.Value)
case *types.AttributeValueMemberN:
fmt.Fprintf(tabWriter, "%v\tN\t%s\n", colName, colVal.Value)
default:
fmt.Fprintf(tabWriter, "%v\t?\t%s\n", colName, "(other)")
}
}
tabWriter.Flush()
m.viewport.SetContent(viewportContent.String())
}
*/

View file

@ -1,4 +1,4 @@
package ui
package dynamotableview
import (
"fmt"

View file

@ -0,0 +1,36 @@
package layout
type BoxSize interface {
childSize(idx, cnt, available int) int
}
func EqualSize() BoxSize {
return equalSize{}
}
type equalSize struct {
}
func (l equalSize) childSize(idx, cnt, available int) int {
childrenHeight := available / cnt
lastChildRem := available % cnt
if idx == cnt-1 {
return childrenHeight + lastChildRem
}
return childrenHeight
}
func LastChildFixedAt(size int) BoxSize {
return lastChildFixedAt{size}
}
type lastChildFixedAt struct {
lastChildSize int
}
func (l lastChildFixedAt) childSize(idx, cnt, available int) int {
if idx == cnt-1 {
return l.lastChildSize
}
return (equalSize{}).childSize(idx, cnt-1, available-l.lastChildSize)
}

View file

@ -1,18 +1,20 @@
package layout
import (
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
"strings"
)
// VBox is a model which will display its children vertically.
type VBox struct {
boxSize BoxSize
children []ResizingModel
}
func NewVBox(children ...ResizingModel) VBox {
return VBox{children: children}
func NewVBox(boxSize BoxSize, children ...ResizingModel) VBox {
return VBox{boxSize: boxSize, children: children}
}
func (vb VBox) Init() tea.Cmd {
@ -43,14 +45,9 @@ func (vb VBox) View() string {
}
func (vb VBox) Resize(w, h int) ResizingModel {
childrenHeight := h / len(vb.children)
lastChildRem := h % len(vb.children)
for i, c := range vb.children {
if i == len(vb.children)-1 {
vb.children[i] = c.Resize(w, childrenHeight+lastChildRem)
} else {
vb.children[i] = c.Resize(w, childrenHeight)
}
childHeight := vb.boxSize.childSize(i, len(vb.children), h)
vb.children[i] = c.Resize(w, childHeight)
}
return vb
}

View file

@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/brianvoe/gofakeit/v6"
@ -21,7 +22,9 @@ func main() {
tableName := "awstools-test"
totalItems := 300
cfg, err := config.LoadDefaultConfig(ctx)
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion("ap-southeast-2"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("abc", "123", "")))
if err != nil {
cli.Fatalf("cannot load AWS config: %v", err)
}
@ -32,7 +35,7 @@ func main() {
if _, err = dynamoClient.DeleteTable(ctx, &dynamodb.DeleteTableInput{
TableName: aws.String(tableName),
}); err != nil {
log.Printf("warn: cannot delete table: %v", tableName)
log.Printf("warn: cannot delete table: %v: %v", tableName, err)
}
if _, err = dynamoClient.CreateTable(ctx, &dynamodb.CreateTableInput{