Fixes made as part of updating the user manual
This commit is contained in:
parent
72b7a40e23
commit
cbbdea75a0
55
internal/dynamo-browse/services/scriptmanager/builtins.go
Normal file
55
internal/dynamo-browse/services/scriptmanager/builtins.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Builtins adopted and modified from Taramin
|
||||
* Copyright (c) 2022 Curtis Myzie
|
||||
*/
|
||||
|
||||
package scriptmanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cloudcmds/tamarin/object"
|
||||
"log"
|
||||
)
|
||||
|
||||
func printBuiltin(ctx context.Context, args ...object.Object) object.Object {
|
||||
env := scriptEnvFromCtx(ctx)
|
||||
prefix := "script " + env.filename + ":"
|
||||
|
||||
values := make([]interface{}, len(args)+1)
|
||||
values[0] = prefix
|
||||
for i, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
case *object.String:
|
||||
values[i+1] = arg.Value()
|
||||
default:
|
||||
values[i+1] = arg.Inspect()
|
||||
}
|
||||
}
|
||||
log.Println(values...)
|
||||
return object.Nil
|
||||
}
|
||||
|
||||
func printfBuiltin(ctx context.Context, args ...object.Object) object.Object {
|
||||
env := scriptEnvFromCtx(ctx)
|
||||
prefix := "script " + env.filename + ":"
|
||||
|
||||
numArgs := len(args)
|
||||
if numArgs < 1 {
|
||||
return object.Errorf("type error: printf() takes 1 or more arguments (%d given)", len(args))
|
||||
}
|
||||
format, err := object.AsString(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var values = []interface{}{prefix}
|
||||
for _, arg := range args[1:] {
|
||||
switch arg := arg.(type) {
|
||||
case *object.String:
|
||||
values = append(values, arg.Value())
|
||||
default:
|
||||
values = append(values, arg.Interface())
|
||||
}
|
||||
}
|
||||
log.Printf("%s "+format, values...)
|
||||
return object.Nil
|
||||
}
|
|
@ -31,6 +31,8 @@ func (m *extModule) register(scp *scope.Scope) {
|
|||
}
|
||||
|
||||
func (m *extModule) command(ctx context.Context, args ...object.Object) object.Object {
|
||||
thisEnv := scriptEnvFromCtx(ctx)
|
||||
|
||||
if err := arg.Require("ext.command", 2, args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -56,7 +58,9 @@ func (m *extModule) command(ctx context.Context, args ...object.Object) object.O
|
|||
objArgs[i] = object.NewString(a)
|
||||
}
|
||||
|
||||
ctx = ctxWithOptions(ctx, m.scriptPlugin.scriptService.options)
|
||||
newEnv := thisEnv
|
||||
newEnv.options = m.scriptPlugin.scriptService.options
|
||||
ctx = ctxWithScriptEnv(ctx, newEnv)
|
||||
|
||||
res := callFn(ctx, fnRes.Scope(), fnRes, objArgs)
|
||||
if object.IsError(res) {
|
||||
|
@ -74,6 +78,8 @@ func (m *extModule) command(ctx context.Context, args ...object.Object) object.O
|
|||
}
|
||||
|
||||
func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) object.Object {
|
||||
thisEnv := scriptEnvFromCtx(ctx)
|
||||
|
||||
if err := arg.Require("ext.key_binding", 3, args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -112,7 +118,9 @@ func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) objec
|
|||
objArgs[i] = object.NewString(a)
|
||||
}
|
||||
|
||||
ctx = ctxWithOptions(ctx, m.scriptPlugin.scriptService.options)
|
||||
newEnv := thisEnv
|
||||
newEnv.options = m.scriptPlugin.scriptService.options
|
||||
ctx = ctxWithScriptEnv(ctx, newEnv)
|
||||
|
||||
res := callFn(ctx, fnRes.Scope(), fnRes, objArgs)
|
||||
if object.IsError(res) {
|
||||
|
|
|
@ -22,7 +22,7 @@ func (om *osModule) exec(ctx context.Context, args ...object.Object) object.Obje
|
|||
return objErr
|
||||
}
|
||||
|
||||
opts := optionFromCtx(ctx)
|
||||
opts := scriptEnvFromCtx(ctx).options
|
||||
if !opts.Permissions.AllowShellCommands {
|
||||
return object.NewErrResult(object.Errorf("permission error: no permission to shell out"))
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ func (om *osModule) env(ctx context.Context, args ...object.Object) object.Objec
|
|||
return objErr
|
||||
}
|
||||
|
||||
opts := optionFromCtx(ctx)
|
||||
opts := scriptEnvFromCtx(ctx).options
|
||||
if !opts.Permissions.AllowEnv {
|
||||
return object.Nil
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ func TestModSession_Table(t *testing.T) {
|
|||
table := session.current_table()
|
||||
|
||||
assert(table.name == "test_table")
|
||||
assert(table.keys["partition"] == "pk")
|
||||
assert(table.keys["sort"] == "sk")
|
||||
assert(table.keys["hash"] == "pk")
|
||||
assert(table.keys["range"] == "sk")
|
||||
assert(len(table.gsis) == 1)
|
||||
assert(table.gsis[0].name == "index-1")
|
||||
assert(table.gsis[0].keys["partition"] == "ipk")
|
||||
assert(table.gsis[0].keys["sort"] == "isk")
|
||||
assert(table.gsis[0].keys["hash"] == "ipk")
|
||||
assert(table.gsis[0].keys["range"] == "isk")
|
||||
|
||||
assert(table == session.result_set().table)
|
||||
`)
|
||||
|
|
|
@ -40,7 +40,7 @@ func (um *uiModule) prompt(ctx context.Context, args ...object.Object) object.Ob
|
|||
if hasResp {
|
||||
return object.NewString(resp)
|
||||
} else {
|
||||
return object.NewError(ctx.Err())
|
||||
return object.Nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return object.NewError(ctx.Err())
|
||||
|
|
|
@ -39,11 +39,12 @@ func TestModUI_Prompt(t *testing.T) {
|
|||
mockedUIService.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("should return error if prompt was cancelled", func(t *testing.T) {
|
||||
t.Run("should return nil if prompt was cancelled", func(t *testing.T) {
|
||||
testFS := testScriptFile(t, "test.tm", `
|
||||
ui.print("Hello, world")
|
||||
var name = ui.prompt("What is your name? ")
|
||||
ui.print("After")
|
||||
ui.print(nil)
|
||||
`)
|
||||
|
||||
promptChan := make(chan string)
|
||||
|
@ -53,6 +54,8 @@ func TestModUI_Prompt(t *testing.T) {
|
|||
|
||||
mockedUIService := mocks.NewUIService(t)
|
||||
mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, world")
|
||||
mockedUIService.EXPECT().PrintMessage(mock.Anything, "After")
|
||||
mockedUIService.EXPECT().PrintMessage(mock.Anything, "nil")
|
||||
mockedUIService.EXPECT().Prompt(mock.Anything, "What is your name? ").Return(promptChan)
|
||||
|
||||
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
||||
|
@ -61,9 +64,8 @@ func TestModUI_Prompt(t *testing.T) {
|
|||
})
|
||||
|
||||
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
||||
assert.Error(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockedUIService.AssertNotCalled(t, "Prompt", "after")
|
||||
mockedUIService.AssertExpectations(t)
|
||||
})
|
||||
|
||||
|
|
|
@ -34,15 +34,21 @@ type Permissions struct {
|
|||
AllowEnv bool
|
||||
}
|
||||
|
||||
type optionCtxKeyType struct{}
|
||||
// scriptEnv is the runtime environment for a particular script execution
|
||||
type scriptEnv struct {
|
||||
filename string
|
||||
options Options
|
||||
}
|
||||
|
||||
var optionCtxKey = optionCtxKeyType{}
|
||||
type scriptEnvKeyType struct{}
|
||||
|
||||
func optionFromCtx(ctx context.Context) Options {
|
||||
perms, _ := ctx.Value(optionCtxKey).(Options)
|
||||
var scriptEnvKey = scriptEnvKeyType{}
|
||||
|
||||
func scriptEnvFromCtx(ctx context.Context) scriptEnv {
|
||||
perms, _ := ctx.Value(scriptEnvKey).(scriptEnv)
|
||||
return perms
|
||||
}
|
||||
|
||||
func ctxWithOptions(ctx context.Context, perms Options) context.Context {
|
||||
return context.WithValue(ctx, optionCtxKey, perms)
|
||||
func ctxWithScriptEnv(ctx context.Context, perms scriptEnv) context.Context {
|
||||
return context.WithValue(ctx, scriptEnvKey, perms)
|
||||
}
|
||||
|
|
|
@ -177,27 +177,6 @@ func (i *itemProxy) value(ctx context.Context, args ...object.Object) object.Obj
|
|||
return object.NewError(err)
|
||||
}
|
||||
return tVal
|
||||
|
||||
// TODO
|
||||
//switch v := av.(type) {
|
||||
//case *types.AttributeValueMemberS:
|
||||
// return object.NewString(v.Value)
|
||||
//case *types.AttributeValueMemberN:
|
||||
// // TODO: better
|
||||
// f, err := strconv.ParseFloat(v.Value, 64)
|
||||
// if err != nil {
|
||||
// return object.NewError(errors.Errorf("value error: invalid N value: %v", v.Value))
|
||||
// }
|
||||
// return object.NewFloat(f)
|
||||
//case *types.AttributeValueMemberBOOL:
|
||||
// if v.Value {
|
||||
// return object.True
|
||||
// }
|
||||
// return object.False
|
||||
//case *types.AttributeValueMemberNULL:
|
||||
// return object.Nil
|
||||
//}
|
||||
//return object.NewError(errors.New("TODO"))
|
||||
}
|
||||
|
||||
func (i *itemProxy) setValue(ctx context.Context, args ...object.Object) object.Object {
|
||||
|
|
|
@ -3,10 +3,12 @@ package scriptmanager
|
|||
import (
|
||||
"context"
|
||||
"github.com/cloudcmds/tamarin/exec"
|
||||
"github.com/cloudcmds/tamarin/object"
|
||||
"github.com/cloudcmds/tamarin/scope"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
|
||||
"github.com/pkg/errors"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -86,7 +88,7 @@ func (s *Service) StartAdHocScript(ctx context.Context, filename string, errChan
|
|||
func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan chan error) {
|
||||
defer close(errChan)
|
||||
|
||||
code, err := s.readScript(filename)
|
||||
code, err := s.readScript(filename, true)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrapf(err, "cannot load script file %v", filename)
|
||||
return
|
||||
|
@ -94,12 +96,16 @@ func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan
|
|||
|
||||
scp := scope.New(scope.Opts{Parent: s.parentScope()})
|
||||
|
||||
ctx = ctxWithOptions(ctx, s.options)
|
||||
ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options})
|
||||
|
||||
if _, err = exec.Execute(ctx, exec.Opts{
|
||||
Input: string(code),
|
||||
File: filename,
|
||||
Scope: scp,
|
||||
Builtins: []*object.Builtin{
|
||||
object.NewBuiltin("print", printBuiltin),
|
||||
object.NewBuiltin("printf", printfBuiltin),
|
||||
},
|
||||
}); err != nil {
|
||||
errChan <- errors.Wrapf(err, "script %v", filename)
|
||||
return
|
||||
|
@ -114,7 +120,7 @@ type loadedScriptResult struct {
|
|||
func (s *Service) loadScript(ctx context.Context, filename string, resChan chan loadedScriptResult) {
|
||||
defer close(resChan)
|
||||
|
||||
code, err := s.readScript(filename)
|
||||
code, err := s.readScript(filename, false)
|
||||
if err != nil {
|
||||
resChan <- loadedScriptResult{err: errors.Wrapf(err, "cannot load script file %v", filename)}
|
||||
return
|
||||
|
@ -129,7 +135,7 @@ func (s *Service) loadScript(ctx context.Context, filename string, resChan chan
|
|||
|
||||
(&extModule{scriptPlugin: newPlugin}).register(scp)
|
||||
|
||||
ctx = ctxWithOptions(ctx, s.options)
|
||||
ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options})
|
||||
|
||||
if _, err = exec.Execute(ctx, exec.Opts{
|
||||
Input: string(code),
|
||||
|
@ -143,8 +149,33 @@ func (s *Service) loadScript(ctx context.Context, filename string, resChan chan
|
|||
resChan <- loadedScriptResult{scriptPlugin: newPlugin}
|
||||
}
|
||||
|
||||
func (s *Service) readScript(filename string) ([]byte, error) {
|
||||
func (s *Service) readScript(filename string, allowCwd bool) ([]byte, error) {
|
||||
if allowCwd {
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
fullScriptPath := filepath.Join(cwd, filename)
|
||||
log.Printf("checking %v", fullScriptPath)
|
||||
if stat, err := os.Stat(fullScriptPath); err == nil && !stat.IsDir() {
|
||||
code, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
} else {
|
||||
log.Printf("warn: cannot get cwd for reading script %v: %v", filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(filename, string(filepath.Separator)) {
|
||||
code, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
|
||||
for _, currFS := range s.lookupPaths {
|
||||
log.Printf("checking %v/%v", currFS, filename)
|
||||
stat, err := fs.Stat(currFS, filename)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
tableProxyPartitionKey = "partition"
|
||||
tableProxySortKey = "sort"
|
||||
tableProxyPartitionKey = "hash"
|
||||
tableProxySortKey = "range"
|
||||
)
|
||||
|
||||
type tableProxy struct {
|
||||
|
@ -42,6 +42,13 @@ func (t *tableProxy) GetAttr(name string) (object.Object, bool) {
|
|||
case "name":
|
||||
return object.NewString(t.table.Name), true
|
||||
case "keys":
|
||||
if t.table.Keys.SortKey == "" {
|
||||
return object.NewMap(map[string]object.Object{
|
||||
tableProxyPartitionKey: object.NewString(t.table.Keys.PartitionKey),
|
||||
tableProxySortKey: object.Nil,
|
||||
}), true
|
||||
}
|
||||
|
||||
return object.NewMap(map[string]object.Object{
|
||||
tableProxyPartitionKey: object.NewString(t.table.Keys.PartitionKey),
|
||||
tableProxySortKey: object.NewString(t.table.Keys.SortKey),
|
||||
|
@ -66,11 +73,11 @@ func newTableIndexProxy(gsi models.TableGSI) object.Object {
|
|||
}
|
||||
|
||||
func (t tableIndexProxy) Type() object.Type {
|
||||
return "index"
|
||||
return "table_index"
|
||||
}
|
||||
|
||||
func (t tableIndexProxy) Inspect() string {
|
||||
return "index(gsi," + t.gsi.Name + ")"
|
||||
return "table_index(gsi," + t.gsi.Name + ")"
|
||||
}
|
||||
|
||||
func (t tableIndexProxy) Interface() interface{} {
|
||||
|
|
|
@ -139,9 +139,8 @@ func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
newTextInput, cmd := s.textInput.Update(msg)
|
||||
s.textInput = newTextInput
|
||||
return s, cmd
|
||||
s.textInput = cc.Collect(s.textInput.Update(msg)).(textinput.Model)
|
||||
return s, cc.Cmd()
|
||||
} else {
|
||||
s.statusMessage = ""
|
||||
}
|
||||
|
|
|
@ -95,7 +95,6 @@ func main() {
|
|||
for i := 0; i < totalItems; i++ {
|
||||
if err := tableService.Put(ctx, inventoryTableInfo, models.Item{
|
||||
"pk": &types.AttributeValueMemberS{Value: key},
|
||||
"sk": &types.AttributeValueMemberN{Value: fmt.Sprint(i)},
|
||||
"uuid": &types.AttributeValueMemberS{Value: gofakeit.UUID()},
|
||||
}); err != nil {
|
||||
log.Fatalln(err)
|
||||
|
|
Loading…
Reference in a new issue