Fixed some small paper-cuts

- Fixed a bug that was pushing duplicate view entries to the backstack
- The appended column will now be selected once added
This commit is contained in:
Leon Mika 2022-10-16 09:50:27 +11:00
parent b51c13dfb1
commit bfd0943c4f
14 changed files with 155 additions and 46 deletions

View file

@ -19,7 +19,7 @@ import (
"github.com/lmika/audax/internal/dynamo-browse/services/jobs"
keybindings_service "github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
"github.com/lmika/audax/internal/dynamo-browse/services/tables"
workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces"
"github.com/lmika/audax/internal/dynamo-browse/services/viewsnapshot"
"github.com/lmika/audax/internal/dynamo-browse/ui"
"github.com/lmika/audax/internal/dynamo-browse/ui/keybindings"
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles"
@ -93,7 +93,7 @@ func main() {
}
tableService := tables.NewService(dynamoProvider, settingStore)
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
workspaceService := viewsnapshot.NewService(resultSetSnapshotStore)
itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo)
jobsService := jobs.NewService(eventBus)

View file

@ -90,7 +90,10 @@ func (cc *ColumnsController) AddColumn(afterIndex int) tea.Msg {
cc.colModel.Columns = newCols
}
return ColumnsUpdated{}
return tea.Batch(
events.SetTeaMessage(ColumnsUpdated{}),
events.SetTeaMessage(SetSelectedColumnInColSelector(afterIndex+1)),
)()
})
}

View file

@ -16,6 +16,8 @@ type SettingsUpdated struct {
type ColumnsUpdated struct {
}
type SetSelectedColumnInColSelector int
type MoveLeftmostDisplayedColumnInTableViewBy int
type NewResultSet struct {

View file

@ -9,7 +9,7 @@ import (
"github.com/lmika/audax/internal/dynamo-browse/models/queryexpr"
"github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
"github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer"
"github.com/lmika/audax/internal/dynamo-browse/services/workspaces"
"github.com/lmika/audax/internal/dynamo-browse/services/viewsnapshot"
bus "github.com/lmika/events"
"github.com/pkg/errors"
"golang.design/x/clipboard"
@ -39,7 +39,7 @@ const (
type TableReadController struct {
tableService TableReadService
workspaceService *workspaces.ViewSnapshotService
workspaceService *viewsnapshot.ViewSnapshotService
itemRendererService *itemrenderer.Service
jobController *JobsController
eventBus *bus.Bus
@ -55,7 +55,7 @@ type TableReadController struct {
func NewTableReadController(
state *State,
tableService TableReadService,
workspaceService *workspaces.ViewSnapshotService,
workspaceService *viewsnapshot.ViewSnapshotService,
itemRendererService *itemrenderer.Service,
jobController *JobsController,
eventBus *bus.Bus,
@ -211,8 +211,16 @@ func (c *TableReadController) doScan(resultSet *models.ResultSet, query models.Q
}
func (c *TableReadController) setResultSetAndFilter(resultSet *models.ResultSet, filter string, pushBackstack bool, op resultSetUpdateOp) tea.Msg {
if pushBackstack {
if err := c.workspaceService.PushSnapshot(resultSet, filter); err != nil {
if resultSet != nil && pushBackstack {
details := serialisable.ViewSnapshotDetails{
TableName: resultSet.TableInfo.Name,
Filter: filter,
}
if q := resultSet.Query; q != nil {
details.Query = q.String()
}
if err := c.workspaceService.PushSnapshot(details); err != nil {
log.Printf("cannot push snapshot: %v", err)
}
}
@ -308,13 +316,13 @@ func (c *TableReadController) updateViewToSnapshot(viewSnapshot *serialisable.Vi
if currentResultSet == nil {
return NewJob(c.jobController, "Fetching table info…", func(ctx context.Context) (*models.TableInfo, error) {
tableInfo, err := c.tableService.Describe(context.Background(), viewSnapshot.TableName)
tableInfo, err := c.tableService.Describe(context.Background(), viewSnapshot.Details.TableName)
if err != nil {
return nil, err
}
return tableInfo, nil
}).OnDone(func(tableInfo *models.TableInfo) tea.Msg {
return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false)
return c.runQuery(tableInfo, viewSnapshot.Details.Query, viewSnapshot.Details.Filter, false)
}).Submit()
}
@ -323,22 +331,22 @@ func (c *TableReadController) updateViewToSnapshot(viewSnapshot *serialisable.Vi
currentQueryExpr = currentResultSet.Query.String()
}
if viewSnapshot.TableName == currentResultSet.TableInfo.Name && viewSnapshot.Query == currentQueryExpr {
if viewSnapshot.Details.TableName == currentResultSet.TableInfo.Name && viewSnapshot.Details.Query == currentQueryExpr {
return NewJob(c.jobController, "Applying filter…", func(ctx context.Context) (*models.ResultSet, error) {
return c.tableService.Filter(currentResultSet, viewSnapshot.Filter), nil
}).OnEither(c.handleResultSetFromJobResult(viewSnapshot.Filter, false, resultSetUpdateSnapshotRestore)).Submit()
return c.tableService.Filter(currentResultSet, viewSnapshot.Details.Filter), nil
}).OnEither(c.handleResultSetFromJobResult(viewSnapshot.Details.Filter, false, resultSetUpdateSnapshotRestore)).Submit()
}
return NewJob(c.jobController, "Running query…", func(ctx context.Context) (tea.Msg, error) {
tableInfo := currentResultSet.TableInfo
if viewSnapshot.TableName != currentResultSet.TableInfo.Name {
tableInfo, err = c.tableService.Describe(context.Background(), viewSnapshot.TableName)
if viewSnapshot.Details.TableName != currentResultSet.TableInfo.Name {
tableInfo, err = c.tableService.Describe(context.Background(), viewSnapshot.Details.TableName)
if err != nil {
return nil, err
}
}
return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false), nil
return c.runQuery(tableInfo, viewSnapshot.Details.Query, viewSnapshot.Details.Filter, false), nil
}).OnDone(func(m tea.Msg) tea.Msg {
return m
}).Submit()

View file

@ -4,7 +4,6 @@ import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/audax/internal/common/ui/events"
"github.com/lmika/audax/internal/common/workspaces"
"github.com/lmika/audax/internal/dynamo-browse/controllers"
"github.com/lmika/audax/test/testdynamo"
"github.com/stretchr/testify/assert"
@ -124,19 +123,6 @@ func tempFile(t *testing.T) string {
return tempFile.Name()
}
func testWorkspace(t *testing.T) *workspaces.Workspace {
wsTempFile := tempFile(t)
wsManager := workspaces.New(workspaces.MetaInfo{Command: "dynamo-browse"})
ws, err := wsManager.Open(wsTempFile)
if err != nil {
t.Fatalf("cannot create workspace manager: %v", err)
}
t.Cleanup(func() { ws.Close() })
return ws
}
func invokeCommand(t *testing.T, msg tea.Msg) tea.Msg {
err, isErr := msg.(events.ErrorMsg)
if isErr {

View file

@ -13,6 +13,7 @@ import (
"github.com/lmika/audax/internal/dynamo-browse/services/tables"
workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces"
"github.com/lmika/audax/test/testdynamo"
"github.com/lmika/audax/test/testworkspace"
bus "github.com/lmika/events"
"github.com/stretchr/testify/assert"
"testing"
@ -583,7 +584,7 @@ type serviceConfig struct {
}
func newService(t *testing.T, cfg serviceConfig) *services {
ws := testWorkspace(t)
ws := testworkspace.New(t)
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws)
settingStore := settingstore.New(ws)

View file

@ -9,6 +9,10 @@ type ViewSnapshot struct {
BackLink int64 `storm:"index"`
ForeLink int64 `storm:"index"`
Time time.Time
Details ViewSnapshotDetails
}
type ViewSnapshotDetails struct {
TableName string
Query string
Filter string

View file

@ -24,7 +24,6 @@ func (s *ResultSetSnapshotStore) Save(rs *serialisable.ViewSnapshot) error {
if err := s.ws.Save(rs); err != nil {
return errors.Wrap(err, "cannot save result set")
}
log.Printf("saved result set: table='%v', query='%v', filter='%v'", rs.TableName, rs.Query, rs.Filter)
return nil
}
@ -142,3 +141,7 @@ func (s *ResultSetSnapshotStore) Remove(resultSetId int64) error {
}
return nil
}
func (s *ResultSetSnapshotStore) Len() (int, error) {
return s.ws.Count(&serialisable.ViewSnapshot{})
}

View file

@ -1,4 +1,4 @@
package workspaces
package viewsnapshot
import "github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
@ -8,6 +8,7 @@ type ViewSnapshotStore interface {
CurrentlyViewedSnapshot() (*serialisable.ViewSnapshot, error)
SetCurrentlyViewedSnapshot(resultSetId int64) error
Find(resultSetID int64) (*serialisable.ViewSnapshot, error)
Len() (int, error)
Head() (*serialisable.ViewSnapshot, error)
Remove(resultSetId int64) error
Dehead(fromNode *serialisable.ViewSnapshot) error

View file

@ -1,7 +1,6 @@
package workspaces
package viewsnapshot
import (
"github.com/lmika/audax/internal/dynamo-browse/models"
"github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
"github.com/pkg/errors"
"time"
@ -17,21 +16,22 @@ func NewService(store ViewSnapshotStore) *ViewSnapshotService {
}
}
func (s *ViewSnapshotService) PushSnapshot(rs *models.ResultSet, filter string) error {
func (s *ViewSnapshotService) PushSnapshot(details serialisable.ViewSnapshotDetails) error {
newSnapshot := &serialisable.ViewSnapshot{
Time: time.Now(),
TableName: rs.TableInfo.Name,
Details: details,
}
if q := rs.Query; q != nil {
newSnapshot.Query = q.String()
}
newSnapshot.Filter = filter
oldHead, err := s.store.CurrentlyViewedSnapshot()
if err != nil {
return errors.Wrap(err, "cannot get snapshot head")
}
if oldHead != nil && oldHead.Details == details {
// Attempting to push a duplicate
return nil
}
if oldHead != nil {
newSnapshot.BackLink = oldHead.ID
@ -62,6 +62,10 @@ func (s *ViewSnapshotService) PushSnapshot(rs *models.ResultSet, filter string)
return nil
}
func (s *ViewSnapshotService) Len() (int, error) {
return s.store.Len()
}
func (s *ViewSnapshotService) ViewRestore() (*serialisable.ViewSnapshot, error) {
vs, err := s.store.CurrentlyViewedSnapshot()
if err != nil {

View file

@ -0,0 +1,53 @@
package viewsnapshot_test
import (
"github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
"github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore"
"github.com/lmika/audax/internal/dynamo-browse/services/viewsnapshot"
"github.com/lmika/audax/test/testworkspace"
"github.com/stretchr/testify/assert"
"testing"
)
func TestViewSnapshotService_PushSnapshot(t *testing.T) {
t.Run("should not push duplicate snapshots", func(t *testing.T) {
ws := testworkspace.New(t)
service := viewsnapshot.NewService(workspacestore.NewResultSetSnapshotStore(ws))
// Push some snapshots
err := service.PushSnapshot(serialisable.ViewSnapshotDetails{
TableName: "normal-table",
Query: "pk = 'abc'",
Filter: "",
})
assert.NoError(t, err)
cnt, err := service.Len()
assert.NoError(t, err)
assert.Equal(t, 1, cnt)
err = service.PushSnapshot(serialisable.ViewSnapshotDetails{
TableName: "abnormal-table",
Query: "pk = 'abc'",
Filter: "fla",
})
assert.NoError(t, err)
cnt, err = service.Len()
assert.NoError(t, err)
assert.Equal(t, 2, cnt)
// Push a duplicate
err = service.PushSnapshot(serialisable.ViewSnapshotDetails{
TableName: "abnormal-table",
Query: "pk = 'abc'",
Filter: "fla",
})
assert.NoError(t, err)
cnt, err = service.Len()
assert.NoError(t, err)
assert.Equal(t, 2, cnt)
})
}

View file

@ -10,6 +10,7 @@ import (
"github.com/lmika/audax/internal/dynamo-browse/ui/keybindings"
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout"
table "github.com/lmika/go-bubble-table"
"log"
"strings"
)
@ -46,6 +47,12 @@ func (c *colListModel) Init() tea.Cmd {
func (m *colListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case controllers.SetSelectedColumnInColSelector:
// HACK: this needs to work for all cases
log.Printf("%d == %d?", int(msg), m.table.Cursor()+1)
if int(msg) == m.table.Cursor()+1 {
m.table.GoDown()
}
case tea.KeyMsg:
switch {
// Column operations

View file

@ -49,6 +49,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case controllers.ColumnsUpdated:
m.colListModel.refreshTable()
m.subModel = cc.Collect(m.subModel.Update(msg)).(tea.Model)
case controllers.SetSelectedColumnInColSelector:
m.compositor = cc.Collect(m.compositor.Update(msg)).(*layout.Compositor)
case tea.KeyMsg:
m.compositor = cc.Collect(m.compositor.Update(msg)).(*layout.Compositor)
default:

View file

@ -0,0 +1,35 @@
package testworkspace
import (
"github.com/lmika/audax/internal/common/workspaces"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func New(t *testing.T) *workspaces.Workspace {
wsTempFile := tempFile(t)
wsManager := workspaces.New(workspaces.MetaInfo{Command: "dynamo-browse"})
ws, err := wsManager.Open(wsTempFile)
if err != nil {
t.Fatalf("cannot create workspace manager: %v", err)
}
t.Cleanup(func() { ws.Close() })
return ws
}
func tempFile(t *testing.T) string {
t.Helper()
tempFile, err := os.CreateTemp("", "export.csv")
assert.NoError(t, err)
tempFile.Close()
t.Cleanup(func() {
os.Remove(tempFile.Name())
})
return tempFile.Name()
}