Issue 24: Added read-only mode (#27)
- Added settings to workspace, and added the read-only mode - Added the `-ro` field which will launch Dynamo-Browse in read-only mode - Added the `set ro` to enable read-only mode, and `set rw` to enable read-write mode
This commit is contained in:
parent
a1717572c5
commit
93ec519127
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/lmika/audax/internal/common/workspaces"
|
"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/settingstore"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore"
|
"github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer"
|
"github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer"
|
||||||
keybindings_service "github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
|
keybindings_service "github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
|
||||||
|
@ -32,6 +33,7 @@ func main() {
|
||||||
var flagTable = flag.String("t", "", "dynamodb table name")
|
var flagTable = flag.String("t", "", "dynamodb table name")
|
||||||
var flagLocal = flag.String("local", "", "local endpoint")
|
var flagLocal = flag.String("local", "", "local endpoint")
|
||||||
var flagDebug = flag.String("debug", "", "file to log debug messages")
|
var flagDebug = flag.String("debug", "", "file to log debug messages")
|
||||||
|
var flagRO = flag.Bool("ro", false, "enable readonly mode")
|
||||||
var flagWorkspace = flag.String("w", "", "workspace file")
|
var flagWorkspace = flag.String("w", "", "workspace file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -73,14 +75,22 @@ func main() {
|
||||||
uiStyles := styles.DefaultStyles
|
uiStyles := styles.DefaultStyles
|
||||||
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws)
|
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws)
|
||||||
|
settingStore := settingstore.New(ws)
|
||||||
|
|
||||||
tableService := tables.NewService(dynamoProvider)
|
if *flagRO {
|
||||||
|
if err := settingStore.SetReadOnly(*flagRO); err != nil {
|
||||||
|
cli.Fatalf("unable to set read-only mode: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableService := tables.NewService(dynamoProvider, settingStore)
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
||||||
itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo)
|
itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo)
|
||||||
|
|
||||||
state := controllers.NewState()
|
state := controllers.NewState()
|
||||||
tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable, true)
|
tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable, true)
|
||||||
tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController)
|
tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController, settingStore)
|
||||||
|
settingsController := controllers.NewSettingsController(settingStore)
|
||||||
keyBindings := keybindings.Default()
|
keyBindings := keybindings.Default()
|
||||||
|
|
||||||
keyBindingService := keybindings_service.NewService(keyBindings)
|
keyBindingService := keybindings_service.NewService(keyBindings)
|
||||||
|
@ -91,6 +101,7 @@ func main() {
|
||||||
model := ui.NewModel(
|
model := ui.NewModel(
|
||||||
tableReadController,
|
tableReadController,
|
||||||
tableWriteController,
|
tableWriteController,
|
||||||
|
settingsController,
|
||||||
itemRendererService,
|
itemRendererService,
|
||||||
commandController,
|
commandController,
|
||||||
keyBindingController,
|
keyBindingController,
|
||||||
|
|
|
@ -10,6 +10,11 @@ type ErrorMsg error
|
||||||
// Message indicates that a message should be shown to the user
|
// Message indicates that a message should be shown to the user
|
||||||
type StatusMsg string
|
type StatusMsg string
|
||||||
|
|
||||||
|
type WrappedStatusMsg struct {
|
||||||
|
Message StatusMsg
|
||||||
|
Next tea.Msg
|
||||||
|
}
|
||||||
|
|
||||||
// ModeMessage indicates that the mode should be changed to the following
|
// ModeMessage indicates that the mode should be changed to the following
|
||||||
type ModeMessage string
|
type ModeMessage string
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ type SetTableItemView struct {
|
||||||
ViewIndex int
|
ViewIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SettingsUpdated struct {
|
||||||
|
}
|
||||||
|
|
||||||
type NewResultSet struct {
|
type NewResultSet struct {
|
||||||
ResultSet *models.ResultSet
|
ResultSet *models.ResultSet
|
||||||
currentFilter string
|
currentFilter string
|
||||||
|
@ -44,10 +47,6 @@ func (rs NewResultSet) StatusMessage() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetReadWrite struct {
|
|
||||||
NewValue bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type PromptForTableMsg struct {
|
type PromptForTableMsg struct {
|
||||||
Tables []string
|
Tables []string
|
||||||
OnSelected func(tableName string) tea.Msg
|
OnSelected func(tableName string) tea.Msg
|
||||||
|
|
|
@ -12,3 +12,8 @@ type TableReadService interface {
|
||||||
Filter(resultSet *models.ResultSet, filter string) *models.ResultSet
|
Filter(resultSet *models.ResultSet, filter string) *models.ResultSet
|
||||||
ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, query models.Queryable) (*models.ResultSet, error)
|
ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, query models.Queryable) (*models.ResultSet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SettingsProvider interface {
|
||||||
|
IsReadOnly() (bool, error)
|
||||||
|
SetReadOnly(ro bool) error
|
||||||
|
}
|
||||||
|
|
49
internal/dynamo-browse/controllers/settings.go
Normal file
49
internal/dynamo-browse/controllers/settings.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/audax/internal/common/ui/events"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingsController struct {
|
||||||
|
settings SettingsProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSettingsController(sp SettingsProvider) *SettingsController {
|
||||||
|
return &SettingsController{
|
||||||
|
settings: sp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SettingsController) SetSetting(name string, value string) tea.Msg {
|
||||||
|
switch name {
|
||||||
|
case "ro":
|
||||||
|
if err := sc.settings.SetReadOnly(true); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
return events.WrappedStatusMsg{
|
||||||
|
Message: "In read-only mode",
|
||||||
|
Next: SettingsUpdated{},
|
||||||
|
}
|
||||||
|
case "rw":
|
||||||
|
if err := sc.settings.SetReadOnly(false); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
return events.WrappedStatusMsg{
|
||||||
|
Message: "In read-write mode",
|
||||||
|
Next: SettingsUpdated{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return events.Error(errors.Errorf("unrecognised setting: %v", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SettingsController) IsReadOnly() bool {
|
||||||
|
ro, err := sc.settings.IsReadOnly()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("warn: unable to determine if R/O is available: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ro
|
||||||
|
}
|
30
internal/dynamo-browse/controllers/settings_test.go
Normal file
30
internal/dynamo-browse/controllers/settings_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package controllers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lmika/audax/internal/common/ui/events"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/controllers"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSettingsController_SetSetting(t *testing.T) {
|
||||||
|
t.Run("read-only setting", func(t *testing.T) {
|
||||||
|
srv := newService(t, false)
|
||||||
|
|
||||||
|
msg := invokeCommand(t, srv.settingsController.SetSetting("ro", ""))
|
||||||
|
|
||||||
|
assert.True(t, srv.settingsController.IsReadOnly())
|
||||||
|
assert.IsType(t, events.WrappedStatusMsg{}, msg)
|
||||||
|
assert.IsType(t, controllers.SettingsUpdated{}, msg.(events.WrappedStatusMsg).Next)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("read-write setting", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
msg := invokeCommand(t, srv.settingsController.SetSetting("rw", ""))
|
||||||
|
|
||||||
|
assert.False(t, srv.settingsController.IsReadOnly())
|
||||||
|
assert.IsType(t, events.WrappedStatusMsg{}, msg)
|
||||||
|
assert.IsType(t, controllers.SettingsUpdated{}, msg.(events.WrappedStatusMsg).Next)
|
||||||
|
})
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ func TestTableReadController_InitTable(t *testing.T) {
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
provider := dynamo.NewProvider(client)
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, &mockedSetting{})
|
||||||
|
|
||||||
t.Run("should prompt for table if no table name provided", func(t *testing.T) {
|
t.Run("should prompt for table if no table name provided", func(t *testing.T) {
|
||||||
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false)
|
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false)
|
||||||
|
@ -53,7 +53,7 @@ func TestTableReadController_ListTables(t *testing.T) {
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
provider := dynamo.NewProvider(client)
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, &mockedSetting{})
|
||||||
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false)
|
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false)
|
||||||
|
|
||||||
t.Run("returns a list of tables", func(t *testing.T) {
|
t.Run("returns a list of tables", func(t *testing.T) {
|
||||||
|
@ -78,7 +78,7 @@ func TestTableReadController_Rescan(t *testing.T) {
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
provider := dynamo.NewProvider(client)
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, &mockedSetting{})
|
||||||
state := controllers.NewState()
|
state := controllers.NewState()
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "bravo-table", false)
|
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "bravo-table", false)
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ func TestTableReadController_ExportCSV(t *testing.T) {
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
provider := dynamo.NewProvider(client)
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, &mockedSetting{})
|
||||||
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false)
|
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false)
|
||||||
|
|
||||||
t.Run("should export result set to CSV file", func(t *testing.T) {
|
t.Run("should export result set to CSV file", func(t *testing.T) {
|
||||||
|
@ -155,7 +155,7 @@ func TestTableReadController_Query(t *testing.T) {
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
provider := dynamo.NewProvider(client)
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, &mockedSetting{})
|
||||||
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false)
|
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false)
|
||||||
|
|
||||||
t.Run("should run scan with filter based on user query", func(t *testing.T) {
|
t.Run("should run scan with filter based on user query", func(t *testing.T) {
|
||||||
|
|
|
@ -17,13 +17,15 @@ type TableWriteController struct {
|
||||||
state *State
|
state *State
|
||||||
tableService *tables.Service
|
tableService *tables.Service
|
||||||
tableReadControllers *TableReadController
|
tableReadControllers *TableReadController
|
||||||
|
settingProvider SettingsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTableWriteController(state *State, tableService *tables.Service, tableReadControllers *TableReadController) *TableWriteController {
|
func NewTableWriteController(state *State, tableService *tables.Service, tableReadControllers *TableReadController, settingProvider SettingsProvider) *TableWriteController {
|
||||||
return &TableWriteController{
|
return &TableWriteController{
|
||||||
state: state,
|
state: state,
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
tableReadControllers: tableReadControllers,
|
tableReadControllers: tableReadControllers,
|
||||||
|
settingProvider: settingProvider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +38,10 @@ func (twc *TableWriteController) ToggleMark(idx int) tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) NewItem() tea.Msg {
|
func (twc *TableWriteController) NewItem() tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Work out which keys we need to prompt for
|
// Work out which keys we need to prompt for
|
||||||
rs := twc.state.ResultSet()
|
rs := twc.state.ResultSet()
|
||||||
|
|
||||||
|
@ -226,6 +232,10 @@ func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) PutItem(idx int) tea.Msg {
|
func (twc *TableWriteController) PutItem(idx int) tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
resultSet := twc.state.ResultSet()
|
resultSet := twc.state.ResultSet()
|
||||||
if !resultSet.IsDirty(idx) {
|
if !resultSet.IsDirty(idx) {
|
||||||
return events.Error(errors.New("item is not dirty"))
|
return events.Error(errors.New("item is not dirty"))
|
||||||
|
@ -247,6 +257,10 @@ func (twc *TableWriteController) PutItem(idx int) tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) PutItems() tea.Msg {
|
func (twc *TableWriteController) PutItems() tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
markedItemCount int
|
markedItemCount int
|
||||||
)
|
)
|
||||||
|
@ -309,6 +323,10 @@ func (twc *TableWriteController) PutItems() tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) TouchItem(idx int) tea.Msg {
|
func (twc *TableWriteController) TouchItem(idx int) tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
resultSet := twc.state.ResultSet()
|
resultSet := twc.state.ResultSet()
|
||||||
if resultSet.IsDirty(idx) {
|
if resultSet.IsDirty(idx) {
|
||||||
return events.Error(errors.New("cannot touch dirty items"))
|
return events.Error(errors.New("cannot touch dirty items"))
|
||||||
|
@ -330,6 +348,10 @@ func (twc *TableWriteController) TouchItem(idx int) tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Msg {
|
func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
resultSet := twc.state.ResultSet()
|
resultSet := twc.state.ResultSet()
|
||||||
if resultSet.IsDirty(idx) {
|
if resultSet.IsDirty(idx) {
|
||||||
return events.Error(errors.New("cannot noisy touch dirty items"))
|
return events.Error(errors.New("cannot noisy touch dirty items"))
|
||||||
|
@ -359,6 +381,10 @@ func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) DeleteMarked() tea.Msg {
|
func (twc *TableWriteController) DeleteMarked() tea.Msg {
|
||||||
|
if err := twc.assertReadWrite(); err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
resultSet := twc.state.ResultSet()
|
resultSet := twc.state.ResultSet()
|
||||||
markedItems := resultSet.MarkedItems()
|
markedItems := resultSet.MarkedItems()
|
||||||
|
|
||||||
|
@ -385,6 +411,16 @@ func (twc *TableWriteController) DeleteMarked() tea.Msg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (twc *TableWriteController) assertReadWrite() error {
|
||||||
|
b, err := twc.settingProvider.IsReadOnly()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if b {
|
||||||
|
return models.ErrReadOnly
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyToN(prefix string, n int, singular, plural, suffix string) string {
|
func applyToN(prefix string, n int, singular, plural, suffix string) string {
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
return fmt.Sprintf("%v%v %v%v", prefix, n, singular, suffix)
|
return fmt.Sprintf("%v%v %v%v", prefix, n, singular, suffix)
|
||||||
|
|
|
@ -16,27 +16,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTableWriteController_NewItem(t *testing.T) {
|
func TestTableWriteController_NewItem(t *testing.T) {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
t.Run("should add an item with pk and sk set at the end of the result set", func(t *testing.T) {
|
t.Run("should add an item with pk and sk set at the end of the result set", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
assert.Len(t, srv.state.ResultSet().Items(), 3)
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
assert.Len(t, state.ResultSet().Items(), 3)
|
|
||||||
|
|
||||||
// Prompt for keys
|
// Prompt for keys
|
||||||
invokeCommandWithPrompts(t, writeController.NewItem(), "pk-value", "sk-value")
|
invokeCommandWithPrompts(t, srv.writeController.NewItem(), "pk-value", "sk-value")
|
||||||
|
|
||||||
newResultSet := state.ResultSet()
|
newResultSet := srv.state.ResultSet()
|
||||||
assert.Len(t, newResultSet.Items(), 4)
|
assert.Len(t, newResultSet.Items(), 4)
|
||||||
assert.Len(t, newResultSet.Items()[3], 2)
|
assert.Len(t, newResultSet.Items()[3], 2)
|
||||||
|
|
||||||
|
@ -47,13 +36,25 @@ func TestTableWriteController_NewItem(t *testing.T) {
|
||||||
assert.True(t, newResultSet.IsNew(3))
|
assert.True(t, newResultSet.IsNew(3))
|
||||||
assert.True(t, newResultSet.IsDirty(3))
|
assert.True(t, newResultSet.IsDirty(3))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should do nothing when in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
assert.Len(t, srv.state.ResultSet().Items(), 3)
|
||||||
|
|
||||||
|
// Prompt for keys
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.NewItem())
|
||||||
|
|
||||||
|
// Confirm no changes
|
||||||
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
|
|
||||||
|
newResultSet := srv.state.ResultSet()
|
||||||
|
assert.Len(t, newResultSet.Items(), 3)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_SetAttributeValue(t *testing.T) {
|
func TestTableWriteController_SetAttributeValue(t *testing.T) {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
t.Run("should preserve the type of the field if unspecified", func(t *testing.T) {
|
t.Run("should preserve the type of the field if unspecified", func(t *testing.T) {
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
|
@ -85,51 +86,32 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) {
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(fmt.Sprintf("should set value of field: %v", scenario.attrKey), func(t *testing.T) {
|
t.Run(fmt.Sprintf("should set value of field: %v", scenario.attrKey), func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.UnsetItemType, scenario.attrKey), scenario.attrValue)
|
||||||
|
|
||||||
state := controllers.NewState()
|
after, _ := srv.state.ResultSet().Items()[0][scenario.attrKey]
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.UnsetItemType, scenario.attrKey), scenario.attrValue)
|
|
||||||
|
|
||||||
after, _ := state.ResultSet().Items()[0][scenario.attrKey]
|
|
||||||
assert.Equal(t, scenario.expected, after)
|
assert.Equal(t, scenario.expected, after)
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should use type of selected item for marked fields if unspecified", func(t *testing.T) {
|
t.Run("should use type of selected item for marked fields if unspecified", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
invokeCommand(t, srv.writeController.ToggleMark(0))
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(1, models.UnsetItemType, "alpha"), "a brand new value")
|
||||||
|
|
||||||
state := controllers.NewState()
|
after1 := srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
invokeCommand(t, writeController.ToggleMark(0))
|
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(1, models.UnsetItemType, "alpha"), "a brand new value")
|
|
||||||
|
|
||||||
after1 := state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value
|
|
||||||
assert.Equal(t, "a brand new value", after1)
|
assert.Equal(t, "a brand new value", after1)
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.False(t, state.ResultSet().IsDirty(1))
|
assert.False(t, srv.state.ResultSet().IsDirty(1))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should change the value to a particular field if already present", func(t *testing.T) {
|
t.Run("should change the value to a particular field if already present", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
attrType models.ItemType
|
attrType models.ItemType
|
||||||
attrValue string
|
attrValue string
|
||||||
|
@ -164,97 +146,80 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) {
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(fmt.Sprintf("should change the value of a field to type %v", scenario.attrType), func(t *testing.T) {
|
t.Run(fmt.Sprintf("should change the value of a field to type %v", scenario.attrType), func(t *testing.T) {
|
||||||
state := controllers.NewState()
|
srv := newService(t, false)
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
if scenario.attrValue == "" {
|
if scenario.attrValue == "" {
|
||||||
invokeCommand(t, writeController.SetAttributeValue(0, scenario.attrType, "alpha"))
|
invokeCommand(t, srv.writeController.SetAttributeValue(0, scenario.attrType, "alpha"))
|
||||||
} else {
|
} else {
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, scenario.attrType, "alpha"), scenario.attrValue)
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, scenario.attrType, "alpha"), scenario.attrValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
after, _ := state.ResultSet().Items()[0]["alpha"]
|
after, _ := srv.state.ResultSet().Items()[0]["alpha"]
|
||||||
assert.Equal(t, scenario.expected, after)
|
assert.Equal(t, scenario.expected, after)
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("should change value of nested field to type %v", scenario.attrType), func(t *testing.T) {
|
t.Run(fmt.Sprintf("should change value of nested field to type %v", scenario.attrType), func(t *testing.T) {
|
||||||
state := controllers.NewState()
|
srv := newService(t, false)
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
|
||||||
beforeAddress := state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
beforeAddress := srv.state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
||||||
beforeStreet := beforeAddress.Value["street"].(*types.AttributeValueMemberS).Value
|
beforeStreet := beforeAddress.Value["street"].(*types.AttributeValueMemberS).Value
|
||||||
|
|
||||||
assert.Equal(t, "Fake st.", beforeStreet)
|
assert.Equal(t, "Fake st.", beforeStreet)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
if scenario.attrValue == "" {
|
if scenario.attrValue == "" {
|
||||||
invokeCommand(t, writeController.SetAttributeValue(0, scenario.attrType, "address.street"))
|
invokeCommand(t, srv.writeController.SetAttributeValue(0, scenario.attrType, "address.street"))
|
||||||
} else {
|
} else {
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, scenario.attrType, "address.street"), scenario.attrValue)
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, scenario.attrType, "address.street"), scenario.attrValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
afterAddress := state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
afterAddress := srv.state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
||||||
after := afterAddress.Value["street"]
|
after := afterAddress.Value["street"]
|
||||||
|
|
||||||
assert.Equal(t, scenario.expected, after)
|
assert.Equal(t, scenario.expected, after)
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_DeleteAttribute(t *testing.T) {
|
func TestTableWriteController_DeleteAttribute(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
|
||||||
|
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
t.Run("should delete top level attribute", func(t *testing.T) {
|
t.Run("should delete top level attribute", func(t *testing.T) {
|
||||||
state := controllers.NewState()
|
srv := newService(t, false)
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("age")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("age")
|
||||||
assert.Equal(t, "23", before)
|
assert.Equal(t, "23", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
invokeCommand(t, writeController.DeleteAttribute(0, "age"))
|
invokeCommand(t, srv.writeController.DeleteAttribute(0, "age"))
|
||||||
|
|
||||||
_, hasAge := state.ResultSet().Items()[0]["age"]
|
_, hasAge := srv.state.ResultSet().Items()[0]["age"]
|
||||||
assert.False(t, hasAge)
|
assert.False(t, hasAge)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should delete attribute of map", func(t *testing.T) {
|
t.Run("should delete attribute of map", func(t *testing.T) {
|
||||||
state := controllers.NewState()
|
srv := newService(t, false)
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
|
||||||
beforeAddress := state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
beforeAddress := srv.state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
||||||
beforeStreet := beforeAddress.Value["no"].(*types.AttributeValueMemberN).Value
|
beforeStreet := beforeAddress.Value["no"].(*types.AttributeValueMemberN).Value
|
||||||
|
|
||||||
assert.Equal(t, "123", beforeStreet)
|
assert.Equal(t, "123", beforeStreet)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
invokeCommand(t, writeController.DeleteAttribute(0, "address.no"))
|
invokeCommand(t, srv.writeController.DeleteAttribute(0, "address.no"))
|
||||||
|
|
||||||
afterAddress := state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
afterAddress := srv.state.ResultSet().Items()[0]["address"].(*types.AttributeValueMemberM)
|
||||||
_, hasStreet := afterAddress.Value["no"]
|
_, hasStreet := afterAddress.Value["no"]
|
||||||
|
|
||||||
assert.False(t, hasStreet)
|
assert.False(t, hasStreet)
|
||||||
|
@ -262,296 +227,383 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_PutItem(t *testing.T) {
|
func TestTableWriteController_PutItem(t *testing.T) {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
t.Run("should put the selected item if dirty", func(t *testing.T) {
|
t.Run("should put the selected item if dirty", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
// Read the table
|
// Read the table
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandWithPrompt(t, writeController.PutItem(0), "y")
|
invokeCommandWithPrompt(t, srv.writeController.PutItem(0), "y")
|
||||||
|
|
||||||
// Rescan the table
|
// Rescan the table
|
||||||
invokeCommand(t, readController.Rescan())
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "a new value", after)
|
assert.Equal(t, "a new value", after)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not put the selected item if user does not confirm", func(t *testing.T) {
|
t.Run("should not put the selected item if user does not confirm", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
// Read the table
|
// Read the table
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
// Modify the item but do not put it
|
// Modify the item but do not put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandWithPrompt(t, writeController.PutItem(0), "n")
|
invokeCommandWithPrompt(t, srv.writeController.PutItem(0), "n")
|
||||||
|
|
||||||
current, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
current, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "a new value", current)
|
assert.Equal(t, "a new value", current)
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
// Rescan the table to confirm item is not modified
|
// Rescan the table to confirm item is not modified
|
||||||
invokeCommandWithPrompt(t, readController.Rescan(), "y")
|
invokeCommandWithPrompt(t, srv.readController.Rescan(), "y")
|
||||||
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", after)
|
assert.Equal(t, "This is some value", after)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not put the selected item if not dirty", func(t *testing.T) {
|
t.Run("should not put the selected item if not dirty", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
// Read the table
|
// Read the table
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
invokeCommandExpectingError(t, writeController.PutItem(0))
|
invokeCommandExpectingError(t, srv.writeController.PutItem(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not put the selected item if in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", before)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Modify the item but do not put it
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.PutItem(0))
|
||||||
|
|
||||||
|
current, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "a new value", current)
|
||||||
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Rescan the table to confirm item is not modified
|
||||||
|
invokeCommandWithPrompt(t, srv.readController.Rescan(), "y")
|
||||||
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", after)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_PutItems(t *testing.T) {
|
func TestTableWriteController_PutItems(t *testing.T) {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
t.Run("should put all dirty items if none are marked", func(t *testing.T) {
|
t.Run("should put all dirty items if none are marked", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
||||||
|
|
||||||
invokeCommandWithPrompt(t, writeController.PutItems(), "y")
|
invokeCommandWithPrompt(t, srv.writeController.PutItems(), "y")
|
||||||
|
|
||||||
// Rescan the table
|
// Rescan the table
|
||||||
invokeCommand(t, readController.Rescan())
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
|
|
||||||
assert.Equal(t, "a new value", state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "a new value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
assert.Equal(t, "another new value", state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "another new value", srv.state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.False(t, state.ResultSet().IsDirty(2))
|
assert.False(t, srv.state.ResultSet().IsDirty(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("only put marked items", func(t *testing.T) {
|
t.Run("only put marked items", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
||||||
invokeCommand(t, writeController.ToggleMark(0))
|
invokeCommand(t, srv.writeController.ToggleMark(0))
|
||||||
|
|
||||||
invokeCommandWithPrompt(t, writeController.PutItems(), "y")
|
invokeCommandWithPrompt(t, srv.writeController.PutItems(), "y")
|
||||||
|
|
||||||
// Verify dirty items are unchanged
|
// Verify dirty items are unchanged
|
||||||
assert.Equal(t, "a new value", state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "a new value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
assert.Equal(t, "another new value", state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "another new value", srv.state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.True(t, state.ResultSet().IsDirty(2))
|
assert.True(t, srv.state.ResultSet().IsDirty(2))
|
||||||
|
|
||||||
// Rescan the table and verify dirty items were not written
|
// Rescan the table and verify dirty items were not written
|
||||||
invokeCommandWithPrompt(t, readController.Rescan(), "y")
|
invokeCommandWithPrompt(t, srv.readController.Rescan(), "y")
|
||||||
|
|
||||||
assert.Equal(t, "a new value", state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "a new value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
assert.Nil(t, state.ResultSet().Items()[2]["alpha"])
|
assert.Nil(t, srv.state.ResultSet().Items()[2]["alpha"])
|
||||||
|
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.False(t, state.ResultSet().IsDirty(2))
|
assert.False(t, srv.state.ResultSet().IsDirty(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("do not put marked items which are not diry", func(t *testing.T) {
|
t.Run("do not put marked items which are not dirty", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
invokeCommand(t, srv.readController.Init())
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
invokeCommand(t, readController.Init())
|
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
||||||
invokeCommand(t, writeController.ToggleMark(1))
|
invokeCommand(t, srv.writeController.ToggleMark(1))
|
||||||
|
|
||||||
invokeCommand(t, writeController.PutItems())
|
invokeCommand(t, srv.writeController.PutItems())
|
||||||
|
|
||||||
// Verify dirty items are unchanged
|
// Verify dirty items are unchanged
|
||||||
assert.Equal(t, "a new value", state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "a new value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
assert.Equal(t, "another new value", state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "another new value", srv.state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
|
||||||
assert.True(t, state.ResultSet().IsDirty(0))
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.True(t, state.ResultSet().IsDirty(2))
|
assert.True(t, srv.state.ResultSet().IsDirty(2))
|
||||||
|
|
||||||
// Rescan the table and verify dirty items were not written
|
// Rescan the table and verify dirty items were not written
|
||||||
invokeCommandWithPrompt(t, readController.Rescan(), "y")
|
invokeCommandWithPrompt(t, srv.readController.Rescan(), "y")
|
||||||
|
|
||||||
assert.Equal(t, "This is some value", state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
assert.Equal(t, "This is some value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
assert.Nil(t, state.ResultSet().Items()[2]["alpha"])
|
assert.Nil(t, srv.state.ResultSet().Items()[2]["alpha"])
|
||||||
|
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
assert.False(t, state.ResultSet().IsDirty(2))
|
assert.False(t, srv.state.ResultSet().IsDirty(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("do nothing if in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
|
||||||
|
// Modify the item and put it
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(2, models.StringItemType, "alpha"), "another new value")
|
||||||
|
invokeCommand(t, srv.writeController.ToggleMark(0))
|
||||||
|
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.PutItems())
|
||||||
|
|
||||||
|
// Verify dirty items are unchanged
|
||||||
|
assert.Equal(t, "a new value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
assert.Equal(t, "another new value", srv.state.ResultSet().Items()[2]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
|
||||||
|
assert.True(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
assert.True(t, srv.state.ResultSet().IsDirty(2))
|
||||||
|
|
||||||
|
// Rescan the table and verify dirty items were not written
|
||||||
|
invokeCommandWithPrompt(t, srv.readController.Rescan(), "y")
|
||||||
|
|
||||||
|
assert.Equal(t, "This is some value", srv.state.ResultSet().Items()[0]["alpha"].(*types.AttributeValueMemberS).Value)
|
||||||
|
assert.Nil(t, srv.state.ResultSet().Items()[2]["alpha"])
|
||||||
|
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(2))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_TouchItem(t *testing.T) {
|
func TestTableWriteController_TouchItem(t *testing.T) {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
|
||||||
|
|
||||||
t.Run("should put the selected item if unmodified", func(t *testing.T) {
|
t.Run("should put the selected item if unmodified", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
// Read the table
|
// Read the table
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.TouchItem(0), "y")
|
invokeCommandWithPrompt(t, srv.writeController.TouchItem(0), "y")
|
||||||
|
|
||||||
// Rescan the table
|
// Rescan the table
|
||||||
invokeCommand(t, readController.Rescan())
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", after)
|
assert.Equal(t, "This is some value", after)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not put the selected item if modified", func(t *testing.T) {
|
t.Run("should not put the selected item if modified", func(t *testing.T) {
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
srv := newService(t, false)
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
|
||||||
|
|
||||||
// Read the table
|
// Read the table
|
||||||
invokeCommand(t, readController.Init())
|
invokeCommand(t, srv.readController.Init())
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
assert.Equal(t, "This is some value", before)
|
assert.Equal(t, "This is some value", before)
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
// Modify the item and put it
|
// Modify the item and put it
|
||||||
invokeCommandWithPrompt(t, writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
invokeCommandExpectingError(t, writeController.TouchItem(0))
|
invokeCommandExpectingError(t, srv.writeController.TouchItem(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not put the selected item if in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", before)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Modify the item and put it
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.TouchItem(0))
|
||||||
|
|
||||||
|
// Rescan the table
|
||||||
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", after)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableWriteController_NoisyTouchItem(t *testing.T) {
|
func TestTableWriteController_NoisyTouchItem(t *testing.T) {
|
||||||
|
t.Run("should delete and put the selected item if unmodified", func(t *testing.T) {
|
||||||
|
srv := newService(t, false)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", before)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Modify the item and put it
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.NoisyTouchItem(0), "y")
|
||||||
|
|
||||||
|
// Rescan the table
|
||||||
|
invokeCommand(t, srv.readController.Rescan())
|
||||||
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", after)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not put the selected item if modified", func(t *testing.T) {
|
||||||
|
srv := newService(t, false)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", before)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Modify the item and put it
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.NoisyTouchItem(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not put the selected item if in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
before, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", before)
|
||||||
|
assert.False(t, srv.state.ResultSet().IsDirty(0))
|
||||||
|
|
||||||
|
// Modify the item and put it
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.NoisyTouchItem(0))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTableWriteController_DeleteMarked(t *testing.T) {
|
||||||
|
t.Run("should delete marked items", func(t *testing.T) {
|
||||||
|
srv := newService(t, false)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
assert.Len(t, srv.state.ResultSet().Items(), 3)
|
||||||
|
|
||||||
|
// Mark some items
|
||||||
|
invokeCommand(t, srv.writeController.ToggleMark(0))
|
||||||
|
invokeCommand(t, srv.writeController.ToggleMark(2))
|
||||||
|
|
||||||
|
// Delete it
|
||||||
|
invokeCommandWithPrompt(t, srv.writeController.DeleteMarked(), "y")
|
||||||
|
|
||||||
|
// Rescan and confirm marked items are deleted
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
assert.Len(t, srv.state.ResultSet().Items(), 1)
|
||||||
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is another some value", after)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not delete marked items if in read-only mode", func(t *testing.T) {
|
||||||
|
srv := newService(t, true)
|
||||||
|
|
||||||
|
// Read the table
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
assert.Len(t, srv.state.ResultSet().Items(), 3)
|
||||||
|
|
||||||
|
// Mark some items
|
||||||
|
invokeCommand(t, srv.writeController.ToggleMark(0))
|
||||||
|
invokeCommand(t, srv.writeController.ToggleMark(2))
|
||||||
|
|
||||||
|
// Delete it
|
||||||
|
invokeCommandExpectingError(t, srv.writeController.DeleteMarked())
|
||||||
|
|
||||||
|
// Rescan and confirm marked items are not deleted
|
||||||
|
invokeCommand(t, srv.readController.Init())
|
||||||
|
assert.Len(t, srv.state.ResultSet().Items(), 3)
|
||||||
|
after, _ := srv.state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
||||||
|
assert.Equal(t, "This is some value", after)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type services struct {
|
||||||
|
state *controllers.State
|
||||||
|
readController *controllers.TableReadController
|
||||||
|
writeController *controllers.TableWriteController
|
||||||
|
settingsController *controllers.SettingsController
|
||||||
|
}
|
||||||
|
|
||||||
|
func newService(t *testing.T, isReadOnly bool) *services {
|
||||||
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t))
|
||||||
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
workspaceService := workspaces_service.NewService(resultSetSnapshotStore)
|
||||||
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
|
||||||
t.Run("should delete and put the selected item if unmodified", func(t *testing.T) {
|
client := testdynamo.SetupTestTable(t, testData)
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
|
||||||
|
|
||||||
provider := dynamo.NewProvider(client)
|
settingProvider := &mockedSetting{isReadOnly: isReadOnly}
|
||||||
service := tables.NewService(provider)
|
provider := dynamo.NewProvider(client)
|
||||||
|
service := tables.NewService(provider, settingProvider)
|
||||||
|
|
||||||
state := controllers.NewState()
|
state := controllers.NewState()
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
||||||
writeController := controllers.NewTableWriteController(state, service, readController)
|
writeController := controllers.NewTableWriteController(state, service, readController, settingProvider)
|
||||||
|
settingsController := controllers.NewSettingsController(settingProvider)
|
||||||
|
|
||||||
// Read the table
|
return &services{
|
||||||
invokeCommand(t, readController.Init())
|
state: state,
|
||||||
before, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
readController: readController,
|
||||||
assert.Equal(t, "This is some value", before)
|
writeController: writeController,
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
settingsController: settingsController,
|
||||||
|
}
|
||||||
// Modify the item and put it
|
}
|
||||||
invokeCommandWithPrompt(t, writeController.NoisyTouchItem(0), "y")
|
|
||||||
|
type mockedSetting struct {
|
||||||
// Rescan the table
|
isReadOnly bool
|
||||||
invokeCommand(t, readController.Rescan())
|
}
|
||||||
after, _ := state.ResultSet().Items()[0].AttributeValueAsString("alpha")
|
|
||||||
assert.Equal(t, "This is some value", after)
|
func (ms *mockedSetting) SetReadOnly(ro bool) error {
|
||||||
assert.False(t, state.ResultSet().IsDirty(0))
|
ms.isReadOnly = ro
|
||||||
})
|
return nil
|
||||||
|
}
|
||||||
t.Run("should not put the selected item if modified", func(t *testing.T) {
|
|
||||||
client := testdynamo.SetupTestTable(t, testData)
|
func (ms *mockedSetting) IsReadOnly() (bool, error) {
|
||||||
|
return ms.isReadOnly, nil
|
||||||
provider := dynamo.NewProvider(client)
|
|
||||||
service := tables.NewService(provider)
|
|
||||||
|
|
||||||
state := controllers.NewState()
|
|
||||||
readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false)
|
|
||||||
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.SetAttributeValue(0, models.StringItemType, "alpha"), "a new value")
|
|
||||||
invokeCommandExpectingError(t, writeController.NoisyTouchItem(0))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
5
internal/dynamo-browse/models/errors.go
Normal file
5
internal/dynamo-browse/models/errors.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
|
var ErrReadOnly = errors.New("in read-only mode")
|
|
@ -0,0 +1,31 @@
|
||||||
|
package settingstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/asdine/storm"
|
||||||
|
"github.com/lmika/audax/internal/common/workspaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
const settingBucket = "Settings"
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyTableReadOnly = "table_ro"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingStore struct {
|
||||||
|
ws storm.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ws *workspaces.Workspace) *SettingStore {
|
||||||
|
return &SettingStore{
|
||||||
|
ws: ws.DB(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SettingStore) IsReadOnly() (b bool, err error) {
|
||||||
|
err = c.ws.Get(settingBucket, keyTableReadOnly, &b)
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SettingStore) SetReadOnly(ro bool) error {
|
||||||
|
return c.ws.Set(settingBucket, keyTableReadOnly, ro)
|
||||||
|
}
|
|
@ -17,3 +17,7 @@ type TableProvider interface {
|
||||||
PutItem(ctx context.Context, name string, item models.Item) error
|
PutItem(ctx context.Context, name string, item models.Item) error
|
||||||
PutItems(ctx context.Context, name string, items []models.Item) error
|
PutItems(ctx context.Context, name string, items []models.Item) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ROProvider interface {
|
||||||
|
IsReadOnly() (bool, error)
|
||||||
|
}
|
||||||
|
|
|
@ -12,12 +12,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
provider TableProvider
|
provider TableProvider
|
||||||
|
roProvider ROProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(provider TableProvider) *Service {
|
func NewService(provider TableProvider, roProvider ROProvider) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
provider: provider,
|
provider: provider,
|
||||||
|
roProvider: roProvider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +77,18 @@ func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, expr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Put(ctx context.Context, tableInfo *models.TableInfo, item models.Item) error {
|
func (s *Service) Put(ctx context.Context, tableInfo *models.TableInfo, item models.Item) error {
|
||||||
|
if err := s.assertReadWrite(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return s.provider.PutItem(ctx, tableInfo.Name, item)
|
return s.provider.PutItem(ctx, tableInfo.Name, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) PutItemAt(ctx context.Context, resultSet *models.ResultSet, index int) error {
|
func (s *Service) PutItemAt(ctx context.Context, resultSet *models.ResultSet, index int) error {
|
||||||
|
if err := s.assertReadWrite(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
item := resultSet.Items()[index]
|
item := resultSet.Items()[index]
|
||||||
if err := s.provider.PutItem(ctx, resultSet.TableInfo.Name, item); err != nil {
|
if err := s.provider.PutItem(ctx, resultSet.TableInfo.Name, item); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -90,6 +100,10 @@ func (s *Service) PutItemAt(ctx context.Context, resultSet *models.ResultSet, in
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) PutSelectedItems(ctx context.Context, resultSet *models.ResultSet, markedItems []models.ItemIndex) error {
|
func (s *Service) PutSelectedItems(ctx context.Context, resultSet *models.ResultSet, markedItems []models.ItemIndex) error {
|
||||||
|
if err := s.assertReadWrite(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(markedItems) == 0 {
|
if len(markedItems) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -108,6 +122,10 @@ func (s *Service) PutSelectedItems(ctx context.Context, resultSet *models.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Delete(ctx context.Context, tableInfo *models.TableInfo, items []models.Item) error {
|
func (s *Service) Delete(ctx context.Context, tableInfo *models.TableInfo, items []models.Item) error {
|
||||||
|
if err := s.assertReadWrite(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if err := s.provider.DeleteItem(ctx, tableInfo.Name, item.KeyValue(tableInfo)); err != nil {
|
if err := s.provider.DeleteItem(ctx, tableInfo.Name, item.KeyValue(tableInfo)); err != nil {
|
||||||
return errors.Wrapf(err, "cannot delete item")
|
return errors.Wrapf(err, "cannot delete item")
|
||||||
|
@ -120,6 +138,16 @@ func (s *Service) ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo,
|
||||||
return s.doScan(ctx, tableInfo, expr)
|
return s.doScan(ctx, tableInfo, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) assertReadWrite() error {
|
||||||
|
b, err := s.roProvider.IsReadOnly()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if b {
|
||||||
|
return models.ErrReadOnly
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: move into a new service
|
// TODO: move into a new service
|
||||||
func (s *Service) Filter(resultSet *models.ResultSet, filter string) *models.ResultSet {
|
func (s *Service) Filter(resultSet *models.ResultSet, filter string) *models.ResultSet {
|
||||||
for i, item := range resultSet.Items() {
|
for i, item := range resultSet.Items() {
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestService_Describe(t *testing.T) {
|
||||||
t.Run("return details of the table", func(t *testing.T) {
|
t.Run("return details of the table", func(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, mockedReadOnlyProvider{readOnly: false})
|
||||||
ti, err := service.Describe(ctx, tableName)
|
ti, err := service.Describe(ctx, tableName)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func TestService_Scan(t *testing.T) {
|
||||||
t.Run("return all columns and fields in sorted order", func(t *testing.T) {
|
t.Run("return all columns and fields in sorted order", func(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
service := tables.NewService(provider)
|
service := tables.NewService(provider, mockedReadOnlyProvider{readOnly: false})
|
||||||
ti, err := service.Describe(ctx, tableName)
|
ti, err := service.Describe(ctx, tableName)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -77,3 +77,11 @@ var testData = []testdynamo.TestData{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockedReadOnlyProvider struct {
|
||||||
|
readOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockedReadOnlyProvider) IsReadOnly() (bool, error) {
|
||||||
|
return m.readOnly, nil
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ const (
|
||||||
type Model struct {
|
type Model struct {
|
||||||
tableReadController *controllers.TableReadController
|
tableReadController *controllers.TableReadController
|
||||||
tableWriteController *controllers.TableWriteController
|
tableWriteController *controllers.TableWriteController
|
||||||
|
settingsController *controllers.SettingsController
|
||||||
commandController *commandctrl.CommandController
|
commandController *commandctrl.CommandController
|
||||||
itemEdit *dynamoitemedit.Model
|
itemEdit *dynamoitemedit.Model
|
||||||
statusAndPrompt *statusandprompt.StatusAndPrompt
|
statusAndPrompt *statusandprompt.StatusAndPrompt
|
||||||
|
@ -56,6 +57,7 @@ type Model struct {
|
||||||
func NewModel(
|
func NewModel(
|
||||||
rc *controllers.TableReadController,
|
rc *controllers.TableReadController,
|
||||||
wc *controllers.TableWriteController,
|
wc *controllers.TableWriteController,
|
||||||
|
settingsController *controllers.SettingsController,
|
||||||
itemRendererService *itemrenderer.Service,
|
itemRendererService *itemrenderer.Service,
|
||||||
cc *commandctrl.CommandController,
|
cc *commandctrl.CommandController,
|
||||||
keyBindingController *controllers.KeyBindingController,
|
keyBindingController *controllers.KeyBindingController,
|
||||||
|
@ -63,7 +65,7 @@ func NewModel(
|
||||||
) Model {
|
) Model {
|
||||||
uiStyles := styles.DefaultStyles
|
uiStyles := styles.DefaultStyles
|
||||||
|
|
||||||
dtv := dynamotableview.New(defaultKeyMap.TableView, uiStyles)
|
dtv := dynamotableview.New(defaultKeyMap.TableView, settingsController, uiStyles)
|
||||||
div := dynamoitemview.New(itemRendererService, uiStyles)
|
div := dynamoitemview.New(itemRendererService, uiStyles)
|
||||||
mainView := layout.NewVBox(layout.LastChildFixedAt(14), dtv, div)
|
mainView := layout.NewVBox(layout.LastChildFixedAt(14), dtv, div)
|
||||||
|
|
||||||
|
@ -141,6 +143,15 @@ func NewModel(
|
||||||
}
|
}
|
||||||
return events.StatusMsg(s.String())
|
return events.StatusMsg(s.String())
|
||||||
},
|
},
|
||||||
|
"set": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
||||||
|
switch len(args) {
|
||||||
|
case 1:
|
||||||
|
return settingsController.SetSetting(args[0], "")
|
||||||
|
case 2:
|
||||||
|
return settingsController.SetSetting(args[0], args[1])
|
||||||
|
}
|
||||||
|
return events.Error(errors.New("expected: settingName [value]"))
|
||||||
|
},
|
||||||
"rebind": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"rebind": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return events.Error(errors.New("expected: bindingName newKey"))
|
return events.Error(errors.New("expected: bindingName newKey"))
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout"
|
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles"
|
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles"
|
||||||
table "github.com/lmika/go-bubble-table"
|
table "github.com/lmika/go-bubble-table"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,29 +22,38 @@ var (
|
||||||
Background(lipgloss.Color("#4479ff"))
|
Background(lipgloss.Color("#4479ff"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Setting interface {
|
||||||
|
IsReadOnly() bool
|
||||||
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
frameTitle frame.FrameTitle
|
frameTitle frame.FrameTitle
|
||||||
table table.Model
|
table table.Model
|
||||||
w, h int
|
w, h int
|
||||||
keyBinding *keybindings.TableKeyBinding
|
keyBinding *keybindings.TableKeyBinding
|
||||||
|
setting Setting
|
||||||
|
|
||||||
// model state
|
// model state
|
||||||
colOffset int
|
isReadOnly bool
|
||||||
rows []table.Row
|
colOffset int
|
||||||
resultSet *models.ResultSet
|
rows []table.Row
|
||||||
|
resultSet *models.ResultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(keyBinding *keybindings.TableKeyBinding, uiStyles styles.Styles) *Model {
|
func New(keyBinding *keybindings.TableKeyBinding, setting Setting, uiStyles styles.Styles) *Model {
|
||||||
tbl := table.New(table.SimpleColumns([]string{"pk", "sk"}), 100, 100)
|
tbl := table.New(table.SimpleColumns([]string{"pk", "sk"}), 100, 100)
|
||||||
rows := make([]table.Row, 0)
|
rows := make([]table.Row, 0)
|
||||||
tbl.SetRows(rows)
|
tbl.SetRows(rows)
|
||||||
|
|
||||||
frameTitle := frame.NewFrameTitle("No table", true, uiStyles.Frames)
|
frameTitle := frame.NewFrameTitle("No table", true, uiStyles.Frames)
|
||||||
|
isReadOnly := setting.IsReadOnly()
|
||||||
|
|
||||||
return &Model{
|
return &Model{
|
||||||
|
isReadOnly: isReadOnly,
|
||||||
frameTitle: frameTitle,
|
frameTitle: frameTitle,
|
||||||
table: tbl,
|
table: tbl,
|
||||||
keyBinding: keyBinding,
|
keyBinding: keyBinding,
|
||||||
|
setting: setting,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +67,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
m.resultSet = msg.ResultSet
|
m.resultSet = msg.ResultSet
|
||||||
m.updateTable()
|
m.updateTable()
|
||||||
return m, m.postSelectedItemChanged
|
return m, m.postSelectedItemChanged
|
||||||
|
case controllers.SettingsUpdated:
|
||||||
|
m.updateTableHeading()
|
||||||
|
return m, nil
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch {
|
switch {
|
||||||
// Table nav
|
// Table nav
|
||||||
|
@ -113,10 +126,19 @@ func (m *Model) Resize(w, h int) layout.ResizingModel {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateTable() {
|
func (m *Model) updateTableHeading() {
|
||||||
m.colOffset = 0
|
tableName := new(strings.Builder)
|
||||||
|
tableName.WriteString("Table: " + m.resultSet.TableInfo.Name)
|
||||||
|
if m.setting.IsReadOnly() {
|
||||||
|
tableName.WriteString(" [RO]")
|
||||||
|
}
|
||||||
|
|
||||||
m.frameTitle.SetTitle("Table: " + m.resultSet.TableInfo.Name)
|
m.frameTitle.SetTitle(tableName.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Model) updateTable() {
|
||||||
|
m.updateTableHeading()
|
||||||
|
m.colOffset = 0
|
||||||
m.rebuildTable()
|
m.rebuildTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/lmika/audax/internal/common/sliceutils"
|
"github.com/lmika/audax/internal/common/sliceutils"
|
||||||
"github.com/lmika/audax/internal/common/ui/events"
|
"github.com/lmika/audax/internal/common/ui/events"
|
||||||
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout"
|
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
|
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/utils"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,11 +37,16 @@ func (s *StatusAndPrompt) Init() tea.Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cc utils.CmdCollector
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case events.ErrorMsg:
|
case events.ErrorMsg:
|
||||||
s.statusMessage = "Error: " + msg.Error()
|
s.statusMessage = "Error: " + msg.Error()
|
||||||
case events.StatusMsg:
|
case events.StatusMsg:
|
||||||
s.statusMessage = string(msg)
|
s.statusMessage = string(msg)
|
||||||
|
case events.WrappedStatusMsg:
|
||||||
|
s.statusMessage = string(msg.Message)
|
||||||
|
cc.Add(func() tea.Msg { return msg.Next })
|
||||||
case events.ModeMessage:
|
case events.ModeMessage:
|
||||||
s.modeLine = string(msg)
|
s.modeLine = string(msg)
|
||||||
case events.MessageWithStatus:
|
case events.MessageWithStatus:
|
||||||
|
@ -86,9 +92,9 @@ func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newModel, cmd := s.model.Update(msg)
|
newModel := cc.Collect(s.model.Update(msg))
|
||||||
s.model = newModel.(layout.ResizingModel)
|
s.model = newModel.(layout.ResizingModel)
|
||||||
return s, cmd
|
return s, cc.Cmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusAndPrompt) InPrompt() bool {
|
func (s *StatusAndPrompt) InPrompt() bool {
|
||||||
|
|
|
@ -49,7 +49,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
dynamoProvider := dynamo.NewProvider(dynamoClient)
|
||||||
tableService := tables.NewService(dynamoProvider)
|
tableService := tables.NewService(dynamoProvider, notROService{})
|
||||||
|
|
||||||
_, _ = tableService, tableInfo
|
_, _ = tableService, tableInfo
|
||||||
|
|
||||||
|
@ -103,3 +103,9 @@ func createTable(ctx context.Context, dynamoClient *dynamodb.Client, tableName s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type notROService struct{}
|
||||||
|
|
||||||
|
func (n notROService) IsReadOnly() (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue