Have got the table view working again
This commit is contained in:
parent
507226f571
commit
2638597f42
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
120
internal/dynamo-browse/ui/teamodels/dynamotableview/model.go
Normal file
120
internal/dynamo-browse/ui/teamodels/dynamotableview/model.go
Normal 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())
|
||||
}
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
package ui
|
||||
package dynamotableview
|
||||
|
||||
import (
|
||||
"fmt"
|
36
internal/dynamo-browse/ui/teamodels/layout/boxsize.go
Normal file
36
internal/dynamo-browse/ui/teamodels/layout/boxsize.go
Normal 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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Reference in a new issue