put-items: added a command to put a dirty item

This commit is contained in:
Leon Mika 2022-05-26 10:17:21 +10:00
parent 174bab36c3
commit 16cb6bdc6b
5 changed files with 152 additions and 14 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/dynamo-browse/models"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/pkg/errors"
)
type TableWriteController struct {
@ -46,7 +47,7 @@ func (twc *TableWriteController) NewItem() tea.Cmd {
}
}
func (twc *TableWriteController) SetItemValue(idx int, key string) tea.Cmd {
func (twc *TableWriteController) SetStringValue(idx int, key string) tea.Cmd {
return func() tea.Msg {
return events.PromptForInputMsg{
Prompt: "string value: ",
@ -63,6 +64,31 @@ func (twc *TableWriteController) SetItemValue(idx int, key string) tea.Cmd {
}
}
func (twc *TableWriteController) PutItem(idx int) tea.Cmd {
return func() tea.Msg {
resultSet := twc.state.ResultSet()
if !resultSet.IsDirty(idx) {
return events.Error(errors.New("item is not dirty"))
}
return events.PromptForInputMsg{
Prompt: "put item? ",
OnDone: func(value string) tea.Cmd {
return func() tea.Msg {
if value != "y" {
return nil
}
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
}
},
}
}
}
func (twc *TableWriteController) DeleteMarked() tea.Cmd {
return func() tea.Msg {
resultSet := twc.state.ResultSet()

View file

@ -204,7 +204,7 @@ func TestTableWriteController_NewItem(t *testing.T) {
})
}
func TestTableWriteController_SetItemValue(t *testing.T) {
func TestTableWriteController_SetStringValue(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
@ -221,10 +221,97 @@ func TestTableWriteController_SetItemValue(t *testing.T) {
assert.Equal(t, "This is some value", before)
assert.False(t, state.ResultSet().IsDirty(0))
invokeCommandWithPrompt(t, writeController.SetItemValue(0, "alpha"), "a new value")
invokeCommandWithPrompt(t, writeController.SetStringValue(0, "alpha"), "a new value")
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "a new value", after)
assert.True(t, state.ResultSet().IsDirty(0))
})
t.Run("should prevent duplicate partition,sort keys", func(t *testing.T) {
t.Skip("TODO")
})
}
func TestTableWriteController_PutItem(t *testing.T) {
t.Run("should put the selected item if dirty", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
provider := dynamo.NewProvider(client)
service := tables.NewService(provider)
state := controllers.NewState()
readController := controllers.NewTableReadController(state, service, "alpha-table")
writeController := controllers.NewTableWriteController(state, service, readController)
// Read the table
invokeCommand(t, readController.Init())
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "This is some value", before)
assert.False(t, state.ResultSet().IsDirty(0))
// Modify the item and put it
invokeCommandWithPrompt(t, writeController.SetStringValue(0, "alpha"), "a new value")
invokeCommandWithPrompt(t, writeController.PutItem(0), "y")
// Rescan the table
invokeCommand(t, readController.Rescan())
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "a new value", after)
assert.False(t, state.ResultSet().IsDirty(0))
})
t.Run("should not put the selected item if user does not confirm", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
provider := dynamo.NewProvider(client)
service := tables.NewService(provider)
state := controllers.NewState()
readController := controllers.NewTableReadController(state, service, "alpha-table")
writeController := controllers.NewTableWriteController(state, service, readController)
// Read the table
invokeCommand(t, readController.Init())
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "This is some value", before)
assert.False(t, state.ResultSet().IsDirty(0))
// Modify the item but do not put it
invokeCommandWithPrompt(t, writeController.SetStringValue(0, "alpha"), "a new value")
invokeCommandWithPrompt(t, writeController.PutItem(0), "n")
current, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "a new value", current)
assert.True(t, state.ResultSet().IsDirty(0))
// Rescan the table to confirm item is not modified
invokeCommand(t, readController.Rescan())
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "This is some value", after)
assert.False(t, state.ResultSet().IsDirty(0))
})
t.Run("should not put the selected item if not dirty", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
provider := dynamo.NewProvider(client)
service := tables.NewService(provider)
state := controllers.NewState()
readController := controllers.NewTableReadController(state, service, "alpha-table")
writeController := controllers.NewTableWriteController(state, service, readController)
// Read the table
invokeCommand(t, readController.Init())
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
assert.Equal(t, "This is some value", before)
assert.False(t, state.ResultSet().IsDirty(0))
invokeCommandExpectingError(t, writeController.PutItem(0))
})
}

View file

@ -81,6 +81,17 @@ func (s *Service) Put(ctx context.Context, tableInfo *models.TableInfo, item mod
return s.provider.PutItem(ctx, tableInfo.Name, item)
}
func (s *Service) PutItemAt(ctx context.Context, resultSet *models.ResultSet, index int) error {
item := resultSet.Items()[index]
if err := s.provider.PutItem(ctx, resultSet.TableInfo.Name, item); err != nil {
return err
}
resultSet.SetDirty(index, false)
resultSet.SetNew(index, false)
return nil
}
func (s *Service) Delete(ctx context.Context, tableInfo *models.TableInfo, items []models.Item) error {
for _, item := range items {
if err := s.provider.DeleteItem(ctx, tableInfo.Name, item.KeyValue(tableInfo)); err != nil {

View file

@ -51,11 +51,14 @@ func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteCon
// TEMP
"new-item": commandctrl.NoArgCommand(wc.NewItem()),
"set": func(args []string) tea.Cmd {
"set-string": func(args []string) tea.Cmd {
if len(args) != 1 {
return events.SetError(errors.New("expected attribute key"))
}
return wc.SetItemValue(dtv.SelectedItemIndex(), args[0])
return wc.SetStringValue(dtv.SelectedItemIndex(), args[0])
},
"put": func(args []string) tea.Cmd {
return wc.PutItem(dtv.SelectedItemIndex())
},
},
})

View file

@ -14,6 +14,10 @@ import (
var (
markedRowStyle = lipgloss.NewStyle().
Background(lipgloss.Color("#e1e1e1"))
dirtyRowStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#e13131"))
newRowStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#31e131"))
)
type itemTableRow struct {
@ -24,6 +28,8 @@ type itemTableRow struct {
func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) {
isMarked := mtr.resultSet.Marked(mtr.itemIndex)
isDirty := mtr.resultSet.IsDirty(mtr.itemIndex)
isNew := mtr.resultSet.IsNew(mtr.itemIndex)
sb := strings.Builder{}
for i, colName := range mtr.resultSet.Columns {
@ -42,15 +48,20 @@ func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) {
sb.WriteString("(other)")
}
}
var style lipgloss.Style
if index == model.Cursor() {
style := model.Styles.SelectedRow
if isMarked {
style = style.Copy().Inherit(markedRowStyle)
}
fmt.Fprintln(w, style.Render(sb.String()))
} else if isMarked {
fmt.Fprintln(w, markedRowStyle.Render(sb.String()))
} else {
fmt.Fprintln(w, sb.String())
style = model.Styles.SelectedRow
}
if isMarked {
style = style.Copy().Inherit(markedRowStyle)
}
if isNew {
style = style.Copy().Inherit(newRowStyle)
} else if isDirty {
style = style.Copy().Inherit(dirtyRowStyle)
}
fmt.Fprintln(w, style.Render(sb.String()))
}