Started working on proper controllers

This commit is contained in:
Leon Mika 2022-05-19 09:55:15 +10:00
parent 306640abdb
commit 6df67ce93b
9 changed files with 225 additions and 91 deletions

View file

@ -0,0 +1,13 @@
package controllers
import (
"context"
"github.com/lmika/awstools/internal/dynamo-browse/models"
)
type TableReadService interface {
ListTables(background context.Context) ([]string, error)
Describe(ctx context.Context, table string) (*models.TableInfo, error)
Scan(ctx context.Context, tableInfo *models.TableInfo) (*models.ResultSet, error)
Filter(resultSet *models.ResultSet, filter string) *models.ResultSet
}

View file

@ -5,13 +5,12 @@ import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/common/ui/events" "github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/dynamo-browse/models" "github.com/lmika/awstools/internal/dynamo-browse/models"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/pkg/errors" "github.com/pkg/errors"
"sync" "sync"
) )
type TableReadController struct { type TableReadController struct {
tableService *tables.Service tableService TableReadService
tableName string tableName string
// state // state
@ -20,7 +19,7 @@ type TableReadController struct {
filter string filter string
} }
func NewTableReadController(tableService *tables.Service, tableName string) *TableReadController { func NewTableReadController(tableService TableReadService, tableName string) *TableReadController {
return &TableReadController{ return &TableReadController{
tableService: tableService, tableService: tableService,
tableName: tableName, tableName: tableName,

View file

@ -0,0 +1,107 @@
package controllers_test
import (
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/lmika/awstools/test/testdynamo"
"github.com/stretchr/testify/assert"
"testing"
)
func TestTableReadController_InitTable(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
provider := dynamo.NewProvider(client)
service := tables.NewService(provider)
t.Run("should prompt for table if no table name provided", func(t *testing.T) {
readController := controllers.NewTableReadController(service, "")
cmd := readController.Init()
event := cmd()
assert.IsType(t, controllers.PromptForTableMsg{}, event)
})
t.Run("should scan table if table name provided", func(t *testing.T) {
readController := controllers.NewTableReadController(service, "")
cmd := readController.Init()
event := cmd()
assert.IsType(t, controllers.PromptForTableMsg{}, event)
})
}
func TestTableReadController_ListTables(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn()
provider := dynamo.NewProvider(client)
service := tables.NewService(provider)
readController := controllers.NewTableReadController(service, "")
t.Run("returns a list of tables", func(t *testing.T) {
cmd := readController.ListTables()
event := cmd().(controllers.PromptForTableMsg)
assert.Equal(t, []string{"alpha-table", "bravo-table"}, event.Tables)
selectedCmd := event.OnSelected("alpha-table")
selectedEvent := selectedCmd()
resultSet := selectedEvent.(controllers.NewResultSet)
assert.Equal(t, "alpha-table", resultSet.ResultSet.TableInfo.Name)
assert.Equal(t, "pk", resultSet.ResultSet.TableInfo.Keys.PartitionKey)
assert.Equal(t, "sk", resultSet.ResultSet.TableInfo.Keys.SortKey)
})
}
var testData = []testdynamo.TestData{
{
TableName: "alpha-table",
Data: []map[string]interface{}{
{
"pk": "abc",
"sk": "111",
"alpha": "This is some value",
},
{
"pk": "abc",
"sk": "222",
"alpha": "This is another some value",
"beta": 1231,
},
{
"pk": "bbb",
"sk": "131",
"beta": 2468,
"gamma": "foobar",
},
},
},
{
TableName: "bravo-table",
Data: []map[string]interface{}{
{
"pk": "foo",
"sk": "bar",
"alpha": "This is some value",
},
{
"pk": "abc",
"sk": "222",
"alpha": "This is another some value",
"beta": 1231,
},
{
"pk": "bbb",
"sk": "131",
"beta": 2468,
"gamma": "foobar",
},
},
},
}

View file

@ -2,11 +2,6 @@ package controllers_test
import ( import (
"testing" "testing"
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/lmika/awstools/test/testdynamo"
) )
func TestTableWriteController_ToggleReadWrite(t *testing.T) { func TestTableWriteController_ToggleReadWrite(t *testing.T) {
@ -159,6 +154,7 @@ func TestTableWriteController_Delete(t *testing.T) {
*/ */
} }
/*
type controller struct { type controller struct {
tableName string tableName string
tableService *tables.Service tableService *tables.Service
@ -197,3 +193,4 @@ var testData = testdynamo.TestData{
"gamma": "foobar", "gamma": "foobar",
}, },
} }
*/

View file

@ -11,9 +11,9 @@ import (
) )
func TestProvider_ScanItems(t *testing.T) { func TestProvider_ScanItems(t *testing.T) {
tableName := "provider-scanimages-test-table" tableName := "test-table"
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -24,9 +24,9 @@ func TestProvider_ScanItems(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, items, 3) assert.Len(t, items, 3)
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[0]))
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[1])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[1]))
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[2]))
}) })
t.Run("should return error if table name does not exist", func(t *testing.T) { t.Run("should return error if table name does not exist", func(t *testing.T) {
@ -39,10 +39,10 @@ func TestProvider_ScanItems(t *testing.T) {
} }
func TestProvider_DeleteItem(t *testing.T) { func TestProvider_DeleteItem(t *testing.T) {
tableName := "provider-deleteitem-test-table" tableName := "test-table"
t.Run("should delete item if exists in table", func(t *testing.T) { t.Run("should delete item if exists in table", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -57,14 +57,14 @@ func TestProvider_DeleteItem(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, items, 2) assert.Len(t, items, 2)
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[0]))
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[2]))
assert.NotContains(t, items, testdynamo.TestRecordAsItem(t, testData[1])) assert.NotContains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[1]))
}) })
t.Run("should do nothing if key does not exist", func(t *testing.T) { t.Run("should do nothing if key does not exist", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -79,13 +79,13 @@ func TestProvider_DeleteItem(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, items, 3) assert.Len(t, items, 3)
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[0]))
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[1])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[1]))
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2])) assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0].Data[2]))
}) })
t.Run("should return error if table name does not exist", func(t *testing.T) { t.Run("should return error if table name does not exist", func(t *testing.T) {
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -97,22 +97,27 @@ func TestProvider_DeleteItem(t *testing.T) {
}) })
} }
var testData = testdynamo.TestData{ var testData = []testdynamo.TestData{
{ {
"pk": "abc", TableName: "test-table",
"sk": "111", Data: []map[string]interface{}{
"alpha": "This is some value", {
}, "pk": "abc",
{ "sk": "111",
"pk": "abc", "alpha": "This is some value",
"sk": "222", },
"alpha": "This is another some value", {
"beta": 1231, "pk": "abc",
}, "sk": "222",
{ "alpha": "This is another some value",
"pk": "bbb", "beta": 1231,
"sk": "131", },
"beta": 2468, {
"gamma": "foobar", "pk": "bbb",
"sk": "131",
"beta": 2468,
"gamma": "foobar",
},
},
}, },
} }

View file

@ -11,9 +11,9 @@ import (
) )
func TestService_Describe(t *testing.T) { func TestService_Describe(t *testing.T) {
tableName := "service-describe-table" tableName := "service-test-data"
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -33,9 +33,9 @@ func TestService_Describe(t *testing.T) {
} }
func TestService_Scan(t *testing.T) { func TestService_Scan(t *testing.T) {
tableName := "service-scan-test-table" tableName := "service-test-data"
client, cleanupFn := testdynamo.SetupTestTable(t, tableName, testData) client, cleanupFn := testdynamo.SetupTestTable(t, testData)
defer cleanupFn() defer cleanupFn()
provider := dynamo.NewProvider(client) provider := dynamo.NewProvider(client)
@ -58,22 +58,27 @@ func TestService_Scan(t *testing.T) {
}) })
} }
var testData = testdynamo.TestData{ var testData = []testdynamo.TestData{
{ {
"pk": "abc", TableName: "service-test-data",
"sk": "222", Data: []map[string]interface{}{
"alpha": "This is another some value", {
"beta": 1231, "pk": "abc",
}, "sk": "222",
{ "alpha": "This is another some value",
"pk": "abc", "beta": 1231,
"sk": "111", },
"alpha": "This is some value", {
}, "pk": "abc",
{ "sk": "111",
"pk": "bbb", "alpha": "This is some value",
"sk": "131", },
"beta": 2468, {
"gamma": "foobar", "pk": "bbb",
"sk": "131",
"beta": 2468,
"gamma": "foobar",
},
},
}, },
} }

View file

@ -8,7 +8,7 @@ import (
) )
type Model struct { type Model struct {
submodel tea.Model submodel tea.Model
lineDetails *linedetails.Model lineDetails *linedetails.Model
visible bool visible bool
@ -16,7 +16,7 @@ type Model struct {
func NewModel(submodel tea.Model) *Model { func NewModel(submodel tea.Model) *Model {
return &Model{ return &Model{
submodel: submodel, submodel: submodel,
lineDetails: linedetails.New(), lineDetails: linedetails.New(),
} }
} }
@ -49,6 +49,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *Model) ViewItem(item *models.LogLine) { func (m *Model) ViewItem(item *models.LogLine) {
m.visible = true m.visible = true
m.lineDetails.SetSelectedItem(item) m.lineDetails.SetSelectedItem(item)
m.lineDetails.SetFocused(true)
} }
func (m *Model) View() string { func (m *Model) View() string {

View file

@ -14,7 +14,7 @@ type itemTableRow struct {
func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) { func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) {
firstLine := strings.SplitN(mtr.item.Value, "\n", 2)[0] firstLine := strings.SplitN(mtr.item.Value, "\n", 2)[0]
line := fmt.Sprintf("%s\t%s\t%s", mtr.item.Name, "String", firstLine) line := fmt.Sprintf("%s\t%s\t%s", mtr.item.Name, mtr.item.Type, firstLine)
if index == model.Cursor() { if index == model.Cursor() {
fmt.Fprintln(w, model.Styles.SelectedRow.Render(line)) fmt.Fprintln(w, model.Styles.SelectedRow.Render(line))

View file

@ -13,9 +13,12 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type TestData []map[string]interface{} type TestData struct {
TableName string
Data []map[string]interface{}
}
func SetupTestTable(t *testing.T, tableName string, testData TestData) (*dynamodb.Client, func()) { func SetupTestTable(t *testing.T, testData []TestData) (*dynamodb.Client, func()) {
t.Helper() t.Helper()
ctx := context.Background() ctx := context.Background()
@ -27,37 +30,41 @@ func SetupTestTable(t *testing.T, tableName string, testData TestData) (*dynamod
dynamoClient := dynamodb.NewFromConfig(cfg, dynamoClient := dynamodb.NewFromConfig(cfg,
dynamodb.WithEndpointResolver(dynamodb.EndpointResolverFromURL("http://localhost:8000"))) dynamodb.WithEndpointResolver(dynamodb.EndpointResolverFromURL("http://localhost:8000")))
_, err = dynamoClient.CreateTable(ctx, &dynamodb.CreateTableInput{ for _, table := range testData {
TableName: aws.String(tableName), _, err = dynamoClient.CreateTable(ctx, &dynamodb.CreateTableInput{
KeySchema: []types.KeySchemaElement{ TableName: aws.String(table.TableName),
{AttributeName: aws.String("pk"), KeyType: types.KeyTypeHash}, KeySchema: []types.KeySchemaElement{
{AttributeName: aws.String("sk"), KeyType: types.KeyTypeRange}, {AttributeName: aws.String("pk"), KeyType: types.KeyTypeHash},
}, {AttributeName: aws.String("sk"), KeyType: types.KeyTypeRange},
AttributeDefinitions: []types.AttributeDefinition{ },
{AttributeName: aws.String("pk"), AttributeType: types.ScalarAttributeTypeS}, AttributeDefinitions: []types.AttributeDefinition{
{AttributeName: aws.String("sk"), AttributeType: types.ScalarAttributeTypeS}, {AttributeName: aws.String("pk"), AttributeType: types.ScalarAttributeTypeS},
}, {AttributeName: aws.String("sk"), AttributeType: types.ScalarAttributeTypeS},
ProvisionedThroughput: &types.ProvisionedThroughput{ },
ReadCapacityUnits: aws.Int64(100), ProvisionedThroughput: &types.ProvisionedThroughput{
WriteCapacityUnits: aws.Int64(100), ReadCapacityUnits: aws.Int64(100),
}, WriteCapacityUnits: aws.Int64(100),
}) },
assert.NoError(t, err)
for _, item := range testData {
m, err := attributevalue.MarshalMap(item)
assert.NoError(t, err)
_, err = dynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
TableName: aws.String(tableName),
Item: m,
}) })
assert.NoError(t, err) assert.NoError(t, err)
for _, item := range table.Data {
m, err := attributevalue.MarshalMap(item)
assert.NoError(t, err)
_, err = dynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
TableName: aws.String(table.TableName),
Item: m,
})
assert.NoError(t, err)
}
} }
return dynamoClient, func() { return dynamoClient, func() {
dynamoClient.DeleteTable(ctx, &dynamodb.DeleteTableInput{ for _, table := range testData {
TableName: aws.String(tableName), dynamoClient.DeleteTable(ctx, &dynamodb.DeleteTableInput{
}) TableName: aws.String(table.TableName),
})
}
} }
} }