Added paging and the ability to specify the table in query script method (#44)
* paging: added controller for paging through results * paging: added command and key binding for going to the next page * paging: added the ability to specify the table in the query script method * paging: have got exclusive start key written to backstack
This commit is contained in:
parent
9e658b8619
commit
ad1a77a257
20 changed files with 444 additions and 95 deletions
|
|
@ -31,6 +31,7 @@ type SessionService interface {
|
|||
}
|
||||
|
||||
type QueryOptions struct {
|
||||
TableName string
|
||||
NamePlaceholders map[string]string
|
||||
ValuePlaceholders map[string]types.AttributeValue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ func (um *sessionModule) query(ctx context.Context, args ...object.Object) objec
|
|||
return objErr
|
||||
}
|
||||
|
||||
// Table name
|
||||
if val, isVal := objMap.Get("table").(*object.String); isVal && val.Value() != "" {
|
||||
options.TableName = val.Value()
|
||||
}
|
||||
|
||||
// Placeholders
|
||||
if argsVal, isArgsValMap := objMap.Get("args").(*object.Map); isArgsValMap {
|
||||
options.NamePlaceholders = make(map[string]string)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,37 @@ func TestModSession_Query(t *testing.T) {
|
|||
mockedSessionService.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("should successfully specify table name", func(t *testing.T) {
|
||||
rs := &models.ResultSet{}
|
||||
|
||||
mockedSessionService := mocks.NewSessionService(t)
|
||||
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{
|
||||
TableName: "some-table",
|
||||
}).Return(rs, nil)
|
||||
|
||||
mockedUIService := mocks.NewUIService(t)
|
||||
|
||||
testFS := testScriptFile(t, "test.tm", `
|
||||
res := session.query("some expr", {
|
||||
table: "some-table",
|
||||
})
|
||||
assert(!res.is_err())
|
||||
`)
|
||||
|
||||
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
||||
srv.SetIFaces(scriptmanager.Ifaces{
|
||||
UI: mockedUIService,
|
||||
Session: mockedSessionService,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockedUIService.AssertExpectations(t)
|
||||
mockedSessionService.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("should set placeholder values", func(t *testing.T) {
|
||||
rs := &models.ResultSet{}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package tables
|
|||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||
)
|
||||
|
|
@ -11,11 +10,24 @@ import (
|
|||
type TableProvider interface {
|
||||
ListTables(ctx context.Context) ([]string, error)
|
||||
DescribeTable(ctx context.Context, tableName string) (*models.TableInfo, error)
|
||||
QueryItems(ctx context.Context, tableName string, filterExpr *expression.Expression, maxItems int) ([]models.Item, error)
|
||||
ScanItems(ctx context.Context, tableName string, filterExpr *expression.Expression, maxItems int) ([]models.Item, error)
|
||||
DeleteItem(ctx context.Context, tableName string, key map[string]types.AttributeValue) error
|
||||
PutItem(ctx context.Context, name string, item models.Item) error
|
||||
PutItems(ctx context.Context, name string, items []models.Item) error
|
||||
|
||||
QueryItems(
|
||||
ctx context.Context,
|
||||
tableName string,
|
||||
filterExpr *expression.Expression,
|
||||
exclusiveStartKey map[string]types.AttributeValue,
|
||||
maxItems int,
|
||||
) (items []models.Item, lastEvaluatedKey map[string]types.AttributeValue, err error)
|
||||
ScanItems(
|
||||
ctx context.Context,
|
||||
tableName string,
|
||||
filterExpr *expression.Expression,
|
||||
exclusiveStartKey map[string]types.AttributeValue,
|
||||
maxItems int,
|
||||
) (item []models.Item, lastEvaluatedKey map[string]types.AttributeValue, err error)
|
||||
}
|
||||
|
||||
type ConfigProvider interface {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/audax/internal/common/sliceutils"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/services/jobs"
|
||||
"log"
|
||||
|
|
@ -35,10 +36,16 @@ func (s *Service) Describe(ctx context.Context, table string) (*models.TableInfo
|
|||
}
|
||||
|
||||
func (s *Service) Scan(ctx context.Context, tableInfo *models.TableInfo) (*models.ResultSet, error) {
|
||||
return s.doScan(ctx, tableInfo, nil, s.configProvider.DefaultLimit())
|
||||
return s.doScan(ctx, tableInfo, nil, nil, s.configProvider.DefaultLimit())
|
||||
}
|
||||
|
||||
func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, expr models.Queryable, limit int) (*models.ResultSet, error) {
|
||||
func (s *Service) doScan(
|
||||
ctx context.Context,
|
||||
tableInfo *models.TableInfo,
|
||||
expr models.Queryable,
|
||||
exclusiveStartKey map[string]types.AttributeValue,
|
||||
limit int,
|
||||
) (*models.ResultSet, error) {
|
||||
var (
|
||||
filterExpr *expression.Expression
|
||||
runAsQuery bool
|
||||
|
|
@ -55,26 +62,31 @@ func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, expr
|
|||
}
|
||||
|
||||
var results []models.Item
|
||||
var lastEvalKey map[string]types.AttributeValue
|
||||
if runAsQuery {
|
||||
log.Printf("executing query")
|
||||
results, err = s.provider.QueryItems(ctx, tableInfo.Name, filterExpr, limit)
|
||||
results, lastEvalKey, err = s.provider.QueryItems(ctx, tableInfo.Name, filterExpr, exclusiveStartKey, limit)
|
||||
} else {
|
||||
log.Printf("executing scan")
|
||||
results, err = s.provider.ScanItems(ctx, tableInfo.Name, filterExpr, limit)
|
||||
results, lastEvalKey, err = s.provider.ScanItems(ctx, tableInfo.Name, filterExpr, exclusiveStartKey, limit)
|
||||
}
|
||||
|
||||
if err != nil && len(results) == 0 {
|
||||
return &models.ResultSet{
|
||||
TableInfo: tableInfo,
|
||||
Query: expr,
|
||||
TableInfo: tableInfo,
|
||||
Query: expr,
|
||||
ExclusiveStartKey: exclusiveStartKey,
|
||||
LastEvaluatedKey: lastEvalKey,
|
||||
}, errors.Wrapf(err, "unable to scan table %v", tableInfo.Name)
|
||||
}
|
||||
|
||||
models.Sort(results, tableInfo)
|
||||
|
||||
resultSet := &models.ResultSet{
|
||||
TableInfo: tableInfo,
|
||||
Query: expr,
|
||||
TableInfo: tableInfo,
|
||||
Query: expr,
|
||||
ExclusiveStartKey: exclusiveStartKey,
|
||||
LastEvaluatedKey: lastEvalKey,
|
||||
}
|
||||
resultSet.SetItems(results)
|
||||
resultSet.RefreshColumns()
|
||||
|
|
@ -147,8 +159,12 @@ func (s *Service) Delete(ctx context.Context, tableInfo *models.TableInfo, items
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, expr models.Queryable) (*models.ResultSet, error) {
|
||||
return s.doScan(ctx, tableInfo, expr, s.configProvider.DefaultLimit())
|
||||
func (s *Service) ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, expr models.Queryable, exclusiveStartKey map[string]types.AttributeValue) (*models.ResultSet, error) {
|
||||
return s.doScan(ctx, tableInfo, expr, exclusiveStartKey, s.configProvider.DefaultLimit())
|
||||
}
|
||||
|
||||
func (s *Service) NextPage(ctx context.Context, resultSet *models.ResultSet) (*models.ResultSet, error) {
|
||||
return s.doScan(ctx, resultSet.TableInfo, resultSet.Query, resultSet.LastEvaluatedKey, s.configProvider.DefaultLimit())
|
||||
}
|
||||
|
||||
func (s *Service) assertReadWrite() error {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue