backstack: added saving of backstack to workspace
This commit is contained in:
parent
96207821a2
commit
721d3abe5e
|
@ -11,9 +11,12 @@ import (
|
||||||
"github.com/lmika/audax/internal/common/ui/commandctrl"
|
"github.com/lmika/audax/internal/common/ui/commandctrl"
|
||||||
"github.com/lmika/audax/internal/common/ui/logging"
|
"github.com/lmika/audax/internal/common/ui/logging"
|
||||||
"github.com/lmika/audax/internal/common/ui/osstyle"
|
"github.com/lmika/audax/internal/common/ui/osstyle"
|
||||||
|
"github.com/lmika/audax/internal/common/workspaces"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/controllers"
|
"github.com/lmika/audax/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/providers/dynamo"
|
"github.com/lmika/audax/internal/dynamo-browse/providers/dynamo"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/services/tables"
|
"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/ui"
|
"github.com/lmika/audax/internal/dynamo-browse/ui"
|
||||||
"github.com/lmika/gopkgs/cli"
|
"github.com/lmika/gopkgs/cli"
|
||||||
"log"
|
"log"
|
||||||
|
@ -34,6 +37,16 @@ func main() {
|
||||||
cli.Fatalf("cannot load AWS config: %v", err)
|
cli.Fatalf("cannot load AWS config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wsManager := workspaces.New(workspaces.MetaInfo{
|
||||||
|
Command: "sqs-browse",
|
||||||
|
})
|
||||||
|
//ws, err := wsManager.CreateTemp()
|
||||||
|
ws, err := wsManager.Open("temp.workspace")
|
||||||
|
if err != nil {
|
||||||
|
cli.Fatalf("cannot create workspace: %v", ws)
|
||||||
|
}
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
var dynamoClient *dynamodb.Client
|
var dynamoClient *dynamodb.Client
|
||||||
if *flagLocal != "" {
|
if *flagLocal != "" {
|
||||||
host, port, err := net.SplitHostPort(*flagLocal)
|
host, port, err := net.SplitHostPort(*flagLocal)
|
||||||
|
@ -53,11 +66,13 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
||||||
|
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws)
|
||||||
|
|
||||||
tableService := tables.NewService(dynamoProvider)
|
tableService := tables.NewService(dynamoProvider)
|
||||||
|
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
||||||
|
|
||||||
state := controllers.NewState()
|
state := controllers.NewState()
|
||||||
tableReadController := controllers.NewTableReadController(state, tableService, *flagTable)
|
tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, *flagTable)
|
||||||
tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController)
|
tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController)
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController()
|
commandController := commandctrl.NewCommandController()
|
||||||
|
|
37
internal/common/workspaces/manager.go
Normal file
37
internal/common/workspaces/manager.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package workspaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/asdine/storm"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetaInfo struct {
|
||||||
|
Command string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
metainfo MetaInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(metaInfo MetaInfo) *Manager {
|
||||||
|
return &Manager{metainfo: metaInfo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Open(filename string) (*Workspace, error) {
|
||||||
|
db, err := storm.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "cannot open workspace at %v", filename)
|
||||||
|
}
|
||||||
|
return &Workspace{db: db}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) CreateTemp() (*Workspace, error) {
|
||||||
|
workspaceFile, err := os.CreateTemp("", m.metainfo.Command+"*.workspace")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "cannot create workspace file")
|
||||||
|
}
|
||||||
|
workspaceFile.Close() // We just need the filename
|
||||||
|
|
||||||
|
return m.Open(workspaceFile.Name())
|
||||||
|
}
|
19
internal/common/workspaces/workspaces.go
Normal file
19
internal/common/workspaces/workspaces.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package workspaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/asdine/storm"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Workspace struct {
|
||||||
|
db *storm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *Workspace) DB() *storm.DB {
|
||||||
|
return ws.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *Workspace) Close() {
|
||||||
|
log.Printf("close workspace")
|
||||||
|
ws.db.Close()
|
||||||
|
}
|
|
@ -7,14 +7,17 @@ import (
|
||||||
"github.com/lmika/audax/internal/common/ui/events"
|
"github.com/lmika/audax/internal/common/ui/events"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/models"
|
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/models/queryexpr"
|
"github.com/lmika/audax/internal/dynamo-browse/models/queryexpr"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/services/workspaces"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableReadController struct {
|
type TableReadController struct {
|
||||||
tableService TableReadService
|
tableService TableReadService
|
||||||
tableName string
|
workspaceService *workspaces.Service
|
||||||
|
tableName string
|
||||||
|
|
||||||
// state
|
// state
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
|
@ -23,12 +26,13 @@ type TableReadController struct {
|
||||||
//filter string
|
//filter string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTableReadController(state *State, tableService TableReadService, tableName string) *TableReadController {
|
func NewTableReadController(state *State, tableService TableReadService, workspaceService *workspaces.Service, tableName string) *TableReadController {
|
||||||
return &TableReadController{
|
return &TableReadController{
|
||||||
state: state,
|
state: state,
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
tableName: tableName,
|
workspaceService: workspaceService,
|
||||||
mutex: new(sync.Mutex),
|
tableName: tableName,
|
||||||
|
mutex: new(sync.Mutex),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +103,9 @@ func (c *TableReadController) PromptForQuery() tea.Cmd {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.workspaceService.PushSnapshot(resultSet); err != nil {
|
||||||
|
log.Printf("cannot push snapshot: %v", err)
|
||||||
|
}
|
||||||
return c.setResultSetAndFilter(newResultSet, "")
|
return c.setResultSetAndFilter(newResultSet, "")
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,9 +3,8 @@ package models
|
||||||
import "sort"
|
import "sort"
|
||||||
|
|
||||||
type ResultSet struct {
|
type ResultSet struct {
|
||||||
TableInfo *TableInfo
|
TableInfo *TableInfo
|
||||||
Query Queryable
|
Query Queryable
|
||||||
//Columns []string
|
|
||||||
items []Item
|
items []Item
|
||||||
attributes []ItemAttribute
|
attributes []ItemAttribute
|
||||||
|
|
||||||
|
|
19
internal/dynamo-browse/models/serialisable/resultset.go
Normal file
19
internal/dynamo-browse/models/serialisable/resultset.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package serialisable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResultSetSnapshot struct {
|
||||||
|
ID int64 `storm:"id,increment"`
|
||||||
|
BackLink int64 `storm:"index"`
|
||||||
|
Time time.Time
|
||||||
|
TableInfo *models.TableInfo
|
||||||
|
Query Query
|
||||||
|
Filter string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
Expression string
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package workspacestore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/asdine/storm"
|
||||||
|
"github.com/lmika/audax/internal/common/workspaces"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const resultSetSnapshotsBucket = "ResultSetSnapshots"
|
||||||
|
|
||||||
|
type ResultSetSnapshotStore struct {
|
||||||
|
ws storm.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResultSetSnapshotStore(ws *workspaces.Workspace) *ResultSetSnapshotStore {
|
||||||
|
return &ResultSetSnapshotStore{
|
||||||
|
ws: ws.DB().From(resultSetSnapshotsBucket),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ResultSetSnapshotStore) Save(rs *serialisable.ResultSetSnapshot) error {
|
||||||
|
if err := s.ws.Save(rs); err != nil {
|
||||||
|
return errors.Wrap(err, "cannot save result set")
|
||||||
|
}
|
||||||
|
log.Printf("saved result set")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ResultSetSnapshotStore) SetAsHead(resultSetID int64) error {
|
||||||
|
if err := s.ws.Set("head", "id", resultSetID); err != nil {
|
||||||
|
return errors.Wrap(err, "cannot set as head")
|
||||||
|
}
|
||||||
|
log.Printf("saved result set head")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ResultSetSnapshotStore) Head() (*serialisable.ResultSetSnapshot, error) {
|
||||||
|
var headResultSetID int64
|
||||||
|
if err := s.ws.Get("head", "id", &headResultSetID); err != nil && !errors.Is(err, storm.ErrNotFound) {
|
||||||
|
return nil, errors.Wrap(err, "cannot get head")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rss serialisable.ResultSetSnapshot
|
||||||
|
if err := s.ws.One("ID", headResultSetID, &rss); err != nil {
|
||||||
|
if errors.Is(err, storm.ErrNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.Wrap(err, "cannot get head")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rss, nil
|
||||||
|
}
|
9
internal/dynamo-browse/services/workspaces/iface.go
Normal file
9
internal/dynamo-browse/services/workspaces/iface.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package workspaces
|
||||||
|
|
||||||
|
import "github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
|
||||||
|
|
||||||
|
type ResultSetSnapshotStore interface {
|
||||||
|
Save(rs *serialisable.ResultSetSnapshot) error
|
||||||
|
SetAsHead(resultSetId int64) error
|
||||||
|
Head() (*serialisable.ResultSetSnapshot, error)
|
||||||
|
}
|
43
internal/dynamo-browse/services/workspaces/service.go
Normal file
43
internal/dynamo-browse/services/workspaces/service.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package workspaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
store ResultSetSnapshotStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(store ResultSetSnapshotStore) *Service {
|
||||||
|
return &Service{
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) PushSnapshot(rs *models.ResultSet) error {
|
||||||
|
newSnapshot := &serialisable.ResultSetSnapshot{
|
||||||
|
Time: time.Now(),
|
||||||
|
TableInfo: rs.TableInfo,
|
||||||
|
}
|
||||||
|
if q := rs.Query; q != nil {
|
||||||
|
newSnapshot.Query.Expression = q.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if head, err := s.store.Head(); head != nil {
|
||||||
|
newSnapshot.BackLink = head.ID
|
||||||
|
} else if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot get head result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.store.Save(newSnapshot); err != nil {
|
||||||
|
return errors.Wrap(err, "cannot save snapshot")
|
||||||
|
}
|
||||||
|
if err := s.store.SetAsHead(newSnapshot.ID); err != nil {
|
||||||
|
return errors.Wrap(err, "cannot set new snapshot as head")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue