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:
parent
b51c13dfb1
commit
bfd0943c4f
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)),
|
||||
)()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ type SettingsUpdated struct {
|
|||
type ColumnsUpdated struct {
|
||||
}
|
||||
|
||||
type SetSelectedColumnInColSelector int
|
||||
|
||||
type MoveLeftmostDisplayedColumnInTableViewBy int
|
||||
|
||||
type NewResultSet struct {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{})
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
53
internal/dynamo-browse/services/viewsnapshot/service_test.go
Normal file
53
internal/dynamo-browse/services/viewsnapshot/service_test.go
Normal 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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
35
test/testworkspace/workspace.go
Normal file
35
test/testworkspace/workspace.go
Normal 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()
|
||||
}
|
Loading…
Reference in a new issue