sqs-browse: added notion of workspaces in sqs-browse

Also added a tool to generate test tables
This commit is contained in:
Leon Mika 2022-03-24 15:44:57 +11:00
parent cecdbafabb
commit 30dbc4eefe
16 changed files with 239 additions and 60 deletions

View file

@ -11,7 +11,7 @@ import (
func TestCommandController_Prompt(t *testing.T) {
t.Run("prompt user for a command", func(t *testing.T) {
cmd := commandctrl.NewCommandController()
cmd := commandctrl.NewCommandController(nil)
ctx, uiCtx := testuictx.New(context.Background())
err := cmd.Prompt().Execute(ctx)

View file

@ -55,7 +55,7 @@ func TestTableWriteController_Delete(t *testing.T) {
ctx = controllers.ContextWithState(ctx, controllers.State{
ResultSet: resultSet,
SelectedItem: resultSet.Items[1],
InReadWriteMode: false,
InReadWriteMode: true,
})
op := twc.Delete()
@ -91,7 +91,7 @@ func TestTableWriteController_Delete(t *testing.T) {
ctx = controllers.ContextWithState(ctx, controllers.State{
ResultSet: resultSet,
SelectedItem: resultSet.Items[1],
InReadWriteMode: false,
InReadWriteMode: true,
})
op := twc.Delete()

View file

@ -2,9 +2,7 @@ package modexpr
import (
"github.com/alecthomas/participle/v2"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/pkg/errors"
"strconv"
)
type astExpr struct {
@ -12,19 +10,17 @@ type astExpr struct {
}
type astAttribute struct {
Name string `parser:"@Ident '='"`
Value string `parser:"@String"`
Names *astKeyList `parser:"@@ '='"`
Value *astLiteralValue `parser:"@@"`
}
func (a astAttribute) dynamoValue() (types.AttributeValue, error) {
// TODO: should be based on type
s, err := strconv.Unquote(a.Value)
if err != nil {
return nil, errors.Wrap(err, "cannot unquote string")
}
return &types.AttributeValueMemberS{Value: s}, nil
type astKeyList struct {
Names []string `parser:"@Ident ('/' @Ident)*"`
}
type astLiteralValue struct {
String string `parser:"@String"`
}
var parser = participle.MustBuild(&astExpr{})

View file

@ -0,0 +1,31 @@
package modexpr
import "github.com/lmika/awstools/internal/dynamo-browse/models"
func (a *astExpr) calcPatchMods(item models.Item) ([]patchMod, error) {
patchMods := make([]patchMod, 0)
for _, attr := range a.Attributes {
attrPatchMods, err := attr.calcPatchMods(item)
if err != nil {
return nil, err
}
patchMods = append(patchMods, attrPatchMods...)
}
return patchMods, nil
}
func (a *astAttribute) calcPatchMods(item models.Item) ([]patchMod, error) {
value, err := a.Value.dynamoValue()
if err != nil {
return nil, err
}
patchMods := make([]patchMod, 0)
for _, key := range a.Names.Names {
patchMods = append(patchMods, setAttributeMod{key: key, to: value})
}
return patchMods, nil
}

View file

@ -7,15 +7,14 @@ type ModExpr struct {
}
func (me *ModExpr) Patch(item models.Item) (models.Item, error) {
newItem := item.Clone()
mods, err := me.ast.calcPatchMods(item)
if err != nil {
return nil, err
}
for _, attribute := range me.ast.Attributes {
var err error
name := attribute.Name
newItem[name], err = attribute.dynamoValue()
if err != nil {
return nil, err
}
newItem := item.Clone()
for _, mod := range mods {
mod.Apply(newItem)
}
return newItem, nil

View file

@ -36,4 +36,20 @@ func TestModExpr_Patch(t *testing.T) {
assert.Equal(t, "new value", newItem["alpha"].(*types.AttributeValueMemberS).Value)
assert.Equal(t, "another new value", newItem["beta"].(*types.AttributeValueMemberS).Value)
})
t.Run("patch with key tuple", func(t *testing.T) {
modExpr, err := modexpr.Parse(`alpha/beta="new value"`)
assert.NoError(t, err)
oldItem := models.Item{
"old": &types.AttributeValueMemberS{Value: "before"},
"beta": &types.AttributeValueMemberS{Value: "before beta"},
}
newItem, err := modExpr.Patch(oldItem)
assert.NoError(t, err)
assert.Equal(t, "before", newItem["old"].(*types.AttributeValueMemberS).Value)
assert.Equal(t, "new value", newItem["alpha"].(*types.AttributeValueMemberS).Value)
assert.Equal(t, "new value", newItem["beta"].(*types.AttributeValueMemberS).Value)
})
}

View file

@ -0,0 +1,19 @@
package modexpr
import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/lmika/awstools/internal/dynamo-browse/models"
)
type patchMod interface {
Apply(item models.Item)
}
type setAttributeMod struct {
key string
to types.AttributeValue
}
func (sa setAttributeMod) Apply(item models.Item) {
item[sa.key] = sa.to
}

View file

@ -0,0 +1,15 @@
package modexpr
import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/pkg/errors"
"strconv"
)
func (a *astLiteralValue) dynamoValue() (types.AttributeValue, error) {
s, err := strconv.Unquote(a.String)
if err != nil {
return nil, errors.Wrap(err, "cannot unquote string")
}
return &types.AttributeValueMemberS{Value: s}, nil
}

View file

@ -155,10 +155,14 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Tea events
case tea.WindowSizeMsg:
fixedViewsHeight := lipgloss.Height(m.headerView()) + lipgloss.Height(m.splitterView()) + lipgloss.Height(m.footerView())
tableHeight := msg.Height / 2
viewportHeight := msg.Height / 2 // TODO: make this dynamic
if viewportHeight > 15 {
viewportHeight = 15
}
tableHeight := msg.Height - fixedViewsHeight - viewportHeight
if !m.ready {
m.viewport = viewport.New(msg.Width, msg.Height-tableHeight-fixedViewsHeight)
m.viewport = viewport.New(msg.Width, viewportHeight)
m.viewport.SetContent("(no message selected)")
m.ready = true
} else {

View file

@ -3,9 +3,9 @@ package models
import "time"
type Message struct {
ID uint64
ExtID string
Queue string
ID uint64 `storm:"id,increment"`
ExtID string `storm:"unique"`
Queue string `storm:"index"`
Received time.Time
Data string
}

View file

@ -1,31 +0,0 @@
package memstore
import (
"context"
"github.com/lmika/awstools/internal/sqs-browse/models"
"sync"
)
type Store struct {
messages []models.Message
mtx *sync.Mutex
currSeqNo uint64
}
func (s *Store) Save(ctx context.Context, msg *models.Message) error {
s.mtx.Lock()
defer s.mtx.Unlock()
s.currSeqNo++
msg.ID = s.currSeqNo
s.messages = append(s.messages, *msg)
return nil
}
func NewStore() *Store {
return &Store{
messages: make([]models.Message, 0),
mtx: new(sync.Mutex),
}
}

View file

@ -0,0 +1,30 @@
package stormstore
import (
"context"
"github.com/asdine/storm"
"github.com/lmika/awstools/internal/sqs-browse/models"
"github.com/pkg/errors"
)
type Store struct {
db *storm.DB
}
// TODO: should probably be a workspace provider
func NewStore(filename string) (*Store, error) {
db, err := storm.Open(filename)
if err != nil {
return nil, errors.Wrapf(err, "cannot open store %v", filename)
}
return &Store{db: db}, nil
}
func (s *Store) Close() {
s.db.Close()
}
func (s *Store) Save(ctx context.Context, msg *models.Message) error {
return s.db.Save(msg)
}