sqs-browse: added notion of workspaces in sqs-browse
Also added a tool to generate test tables
This commit is contained in:
		
							parent
							
								
									cecdbafabb
								
							
						
					
					
						commit
						30dbc4eefe
					
				|  | @ -10,8 +10,8 @@ import ( | |||
| 	"github.com/lmika/awstools/internal/common/ui/dispatcher" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/controllers" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/models" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/providers/memstore" | ||||
| 	sqsprovider "github.com/lmika/awstools/internal/sqs-browse/providers/sqs" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/providers/stormstore" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/services/messages" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/services/pollmessage" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/ui" | ||||
|  | @ -35,7 +35,18 @@ func main() { | |||
| 
 | ||||
| 	bus := events.New() | ||||
| 
 | ||||
| 	msgStore := memstore.NewStore() | ||||
| 	workspaceFile, err := os.CreateTemp("", "sqs-browse*.workspace") | ||||
| 	if err != nil { | ||||
| 		cli.Fatalf("cannot create workspace file: %v", err) | ||||
| 	} | ||||
| 	workspaceFile.Close()		// We just need the filename
 | ||||
| 
 | ||||
| 	msgStore, err := stormstore.NewStore(workspaceFile.Name()) | ||||
| 	if err != nil { | ||||
| 		cli.Fatalf("cannot open workspace: %v", err) | ||||
| 	} | ||||
| 	defer msgStore.Close() | ||||
| 
 | ||||
| 	sqsProvider := sqsprovider.NewProvider(sqsClient) | ||||
| 
 | ||||
| 	messageService := messages.NewService(sqsProvider) | ||||
|  | @ -59,6 +70,8 @@ func main() { | |||
| 	} | ||||
| 	defer f.Close() | ||||
| 
 | ||||
| 	log.Printf("workspace file: %v", workspaceFile.Name()) | ||||
| 
 | ||||
| 	go func() { | ||||
| 		if err := pollService.Poll(context.Background()); err != nil { | ||||
| 			log.Printf("cannot start poller: %v", err) | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -21,6 +21,7 @@ require ( | |||
| 
 | ||||
| require ( | ||||
| 	github.com/alecthomas/participle/v2 v2.0.0-alpha7 // indirect | ||||
| 	github.com/asdine/storm v2.1.2+incompatible // indirect | ||||
| 	github.com/atotto/clipboard v0.1.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.6 // indirect | ||||
|  | @ -33,8 +34,10 @@ require ( | |||
| 	github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect | ||||
| 	github.com/aws/smithy-go v1.11.1 // indirect | ||||
| 	github.com/brianvoe/gofakeit/v6 v6.15.0 // indirect | ||||
| 	github.com/containerd/console v1.0.3 // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/google/uuid v1.3.0 // indirect | ||||
| 	github.com/jmespath/go-jmespath v0.4.0 // indirect | ||||
| 	github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc // indirect | ||||
| 	github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe // indirect | ||||
|  | @ -47,6 +50,7 @@ require ( | |||
| 	github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/rivo/uniseg v0.2.0 // indirect | ||||
| 	go.etcd.io/bbolt v1.3.6 // indirect | ||||
| 	golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect | ||||
| 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect | ||||
|  |  | |||
							
								
								
									
										9
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								go.sum
									
									
									
									
									
								
							|  | @ -2,6 +2,8 @@ github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+Bw | |||
| github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= | ||||
| github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= | ||||
| github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= | ||||
| github.com/asdine/storm v2.1.2+incompatible h1:dczuIkyqwY2LrtXPz8ixMrU/OFgZp71kbKTHGrXYt/Q= | ||||
| github.com/asdine/storm v2.1.2+incompatible/go.mod h1:RarYDc9hq1UPLImuiXK3BIWPJLdIygvV3PsInK0FbVQ= | ||||
| github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= | ||||
| github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= | ||||
| github.com/aws/aws-sdk-go-v2 v1.13.0 h1:1XIXAfxsEmbhbj5ry3D3vX+6ZcUYvIqSm4CWWEuGZCA= | ||||
|  | @ -46,6 +48,8 @@ github.com/aws/smithy-go v1.10.0 h1:gsoZQMNHnX+PaghNw4ynPsyGP7aUCqx5sY2dlPQsZ0w= | |||
| github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= | ||||
| github.com/aws/smithy-go v1.11.1 h1:IQ+lPZVkSM3FRtyaDox41R8YS6iwPMYIreejOgPW49g= | ||||
| github.com/aws/smithy-go v1.11.1/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= | ||||
| github.com/brianvoe/gofakeit/v6 v6.15.0 h1:lJPGJZ2/07TRGDazyTzD5b18N3y4tmmJpdhCUw18FlI= | ||||
| github.com/brianvoe/gofakeit/v6 v6.15.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= | ||||
| github.com/calyptia/go-bubble-table v0.1.0 h1:mXpaaBlrHGH4K8v5PvM8YqBFT9jlysS1YOycU2u3gEQ= | ||||
| github.com/calyptia/go-bubble-table v0.1.0/go.mod h1:2nnweuFos+eEIIbgweXvZuX+ROOatsMwB3NHnX/vTC4= | ||||
| github.com/charmbracelet/bubbles v0.10.3 h1:fKarbRaObLn/DCsZO4Y3vKCwRUzynQD9L+gGev1E/ho= | ||||
|  | @ -66,6 +70,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | |||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= | ||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= | ||||
| github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= | ||||
| github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= | ||||
|  | @ -115,8 +121,11 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ | |||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= | ||||
| go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= | ||||
| golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import ( | |||
| 
 | ||||
| func TestCommandController_Prompt(t *testing.T) { | ||||
| 	t.Run("prompt user for a command", func(t *testing.T) { | ||||
| 		cmd := commandctrl.NewCommandController() | ||||
| 		cmd := commandctrl.NewCommandController(nil) | ||||
| 
 | ||||
| 		ctx, uiCtx := testuictx.New(context.Background()) | ||||
| 		err := cmd.Prompt().Execute(ctx) | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ func TestTableWriteController_Delete(t *testing.T) { | |||
| 		ctx = controllers.ContextWithState(ctx, controllers.State{ | ||||
| 			ResultSet:       resultSet, | ||||
| 			SelectedItem:    resultSet.Items[1], | ||||
| 			InReadWriteMode: false, | ||||
| 			InReadWriteMode: true, | ||||
| 		}) | ||||
| 
 | ||||
| 		op := twc.Delete() | ||||
|  | @ -91,7 +91,7 @@ func TestTableWriteController_Delete(t *testing.T) { | |||
| 		ctx = controllers.ContextWithState(ctx, controllers.State{ | ||||
| 			ResultSet:       resultSet, | ||||
| 			SelectedItem:    resultSet.Items[1], | ||||
| 			InReadWriteMode: false, | ||||
| 			InReadWriteMode: true, | ||||
| 		}) | ||||
| 
 | ||||
| 		op := twc.Delete() | ||||
|  |  | |||
|  | @ -2,9 +2,7 @@ package modexpr | |||
| 
 | ||||
| import ( | ||||
| 	"github.com/alecthomas/participle/v2" | ||||
| 	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| type astExpr struct { | ||||
|  | @ -12,19 +10,17 @@ type astExpr struct { | |||
| } | ||||
| 
 | ||||
| type astAttribute struct { | ||||
| 	Name  string `parser:"@Ident '='"` | ||||
| 	Value string `parser:"@String"` | ||||
| 	Names *astKeyList      `parser:"@@ '='"` | ||||
| 	Value *astLiteralValue `parser:"@@"` | ||||
| } | ||||
| 
 | ||||
| func (a astAttribute) dynamoValue() (types.AttributeValue, error) { | ||||
| 	// TODO: should be based on type
 | ||||
| 	s, err := strconv.Unquote(a.Value) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "cannot unquote string") | ||||
| 	} | ||||
| 	return &types.AttributeValueMemberS{Value: s}, nil | ||||
| type astKeyList struct { | ||||
| 	Names []string `parser:"@Ident ('/' @Ident)*"` | ||||
| } | ||||
| 
 | ||||
| type astLiteralValue struct { | ||||
| 	String string `parser:"@String"` | ||||
| } | ||||
| 
 | ||||
| var parser = participle.MustBuild(&astExpr{}) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										31
									
								
								internal/dynamo-browse/models/modexpr/astmods.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								internal/dynamo-browse/models/modexpr/astmods.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| package modexpr | ||||
| 
 | ||||
| import "github.com/lmika/awstools/internal/dynamo-browse/models" | ||||
| 
 | ||||
| func (a *astExpr) calcPatchMods(item models.Item) ([]patchMod, error) { | ||||
| 	patchMods := make([]patchMod, 0) | ||||
| 
 | ||||
| 	for _, attr := range a.Attributes { | ||||
| 		attrPatchMods, err := attr.calcPatchMods(item) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		patchMods = append(patchMods, attrPatchMods...) | ||||
| 	} | ||||
| 
 | ||||
| 	return patchMods, nil | ||||
| } | ||||
| 
 | ||||
| func (a *astAttribute) calcPatchMods(item models.Item) ([]patchMod, error) { | ||||
| 	value, err := a.Value.dynamoValue() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	patchMods := make([]patchMod, 0) | ||||
| 	for _, key := range a.Names.Names { | ||||
| 		patchMods = append(patchMods, setAttributeMod{key: key, to: value}) | ||||
| 	} | ||||
| 
 | ||||
| 	return patchMods, nil | ||||
| } | ||||
|  | @ -7,15 +7,14 @@ type ModExpr struct { | |||
| } | ||||
| 
 | ||||
| func (me *ModExpr) Patch(item models.Item) (models.Item, error) { | ||||
| 	newItem := item.Clone() | ||||
| 	mods, err := me.ast.calcPatchMods(item) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, attribute := range me.ast.Attributes { | ||||
| 		var err error | ||||
| 		name := attribute.Name | ||||
| 		newItem[name], err = attribute.dynamoValue() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	newItem := item.Clone() | ||||
| 	for _, mod := range mods { | ||||
| 		mod.Apply(newItem) | ||||
| 	} | ||||
| 
 | ||||
| 	return newItem, nil | ||||
|  |  | |||
|  | @ -36,4 +36,20 @@ func TestModExpr_Patch(t *testing.T) { | |||
| 		assert.Equal(t, "new value", newItem["alpha"].(*types.AttributeValueMemberS).Value) | ||||
| 		assert.Equal(t, "another new value", newItem["beta"].(*types.AttributeValueMemberS).Value) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("patch with key tuple", func(t *testing.T) { | ||||
| 		modExpr, err := modexpr.Parse(`alpha/beta="new value"`) | ||||
| 		assert.NoError(t, err) | ||||
| 
 | ||||
| 		oldItem := models.Item{ | ||||
| 			"old": &types.AttributeValueMemberS{Value: "before"}, | ||||
| 			"beta": &types.AttributeValueMemberS{Value: "before beta"}, | ||||
| 		} | ||||
| 		newItem, err := modExpr.Patch(oldItem) | ||||
| 		assert.NoError(t, err) | ||||
| 
 | ||||
| 		assert.Equal(t, "before", newItem["old"].(*types.AttributeValueMemberS).Value) | ||||
| 		assert.Equal(t, "new value", newItem["alpha"].(*types.AttributeValueMemberS).Value) | ||||
| 		assert.Equal(t, "new value", newItem["beta"].(*types.AttributeValueMemberS).Value) | ||||
| 	}) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										19
									
								
								internal/dynamo-browse/models/modexpr/mods.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								internal/dynamo-browse/models/modexpr/mods.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| package modexpr | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" | ||||
| 	"github.com/lmika/awstools/internal/dynamo-browse/models" | ||||
| ) | ||||
| 
 | ||||
| type patchMod interface { | ||||
| 	Apply(item models.Item) | ||||
| } | ||||
| 
 | ||||
| type setAttributeMod struct { | ||||
| 	key  string | ||||
| 	to   types.AttributeValue | ||||
| } | ||||
| 
 | ||||
| func (sa setAttributeMod) Apply(item models.Item) { | ||||
| 	item[sa.key] = sa.to | ||||
| } | ||||
							
								
								
									
										15
									
								
								internal/dynamo-browse/models/modexpr/values.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/dynamo-browse/models/modexpr/values.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| package modexpr | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| func (a *astLiteralValue) dynamoValue() (types.AttributeValue, error) { | ||||
| 	s, err := strconv.Unquote(a.String) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "cannot unquote string") | ||||
| 	} | ||||
| 	return &types.AttributeValueMemberS{Value: s}, nil | ||||
| } | ||||
|  | @ -155,10 +155,14 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||
| 	// Tea events
 | ||||
| 	case tea.WindowSizeMsg: | ||||
| 		fixedViewsHeight := lipgloss.Height(m.headerView()) + lipgloss.Height(m.splitterView()) + lipgloss.Height(m.footerView()) | ||||
| 		tableHeight := msg.Height / 2 | ||||
| 		viewportHeight := msg.Height / 2		// TODO: make this dynamic
 | ||||
| 		if viewportHeight > 15 { | ||||
| 			viewportHeight = 15 | ||||
| 		} | ||||
| 		tableHeight := msg.Height - fixedViewsHeight - viewportHeight | ||||
| 
 | ||||
| 		if !m.ready { | ||||
| 			m.viewport = viewport.New(msg.Width, msg.Height-tableHeight-fixedViewsHeight) | ||||
| 			m.viewport = viewport.New(msg.Width, viewportHeight) | ||||
| 			m.viewport.SetContent("(no message selected)") | ||||
| 			m.ready = true | ||||
| 		} else { | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ package models | |||
| import "time" | ||||
| 
 | ||||
| type Message struct { | ||||
| 	ID       uint64 | ||||
| 	ExtID string | ||||
| 	Queue    string | ||||
| 	ID       uint64		`storm:"id,increment"` | ||||
| 	ExtID    string		`storm:"unique"` | ||||
| 	Queue    string		`storm:"index"` | ||||
| 	Received time.Time | ||||
| 	Data     string | ||||
| } | ||||
|  |  | |||
|  | @ -1,31 +0,0 @@ | |||
| package memstore | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/models" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| type Store struct { | ||||
| 	messages []models.Message | ||||
| 
 | ||||
| 	mtx *sync.Mutex | ||||
| 	currSeqNo uint64 | ||||
| } | ||||
| 
 | ||||
| func (s *Store) Save(ctx context.Context, msg *models.Message) error { | ||||
| 	s.mtx.Lock() | ||||
| 	defer s.mtx.Unlock() | ||||
| 
 | ||||
| 	s.currSeqNo++ | ||||
| 	msg.ID = s.currSeqNo | ||||
| 	s.messages = append(s.messages, *msg) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func NewStore() *Store { | ||||
| 	return &Store{ | ||||
| 		messages: make([]models.Message, 0), | ||||
| 		mtx: new(sync.Mutex), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										30
									
								
								internal/sqs-browse/providers/stormstore/memstore.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/sqs-browse/providers/stormstore/memstore.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| package stormstore | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/asdine/storm" | ||||
| 	"github.com/lmika/awstools/internal/sqs-browse/models" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| type Store struct { | ||||
| 	db *storm.DB | ||||
| } | ||||
| 
 | ||||
| // TODO: should probably be a workspace provider
 | ||||
| func NewStore(filename string) (*Store, error) { | ||||
| 	db, err := storm.Open(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrapf(err, "cannot open store %v", filename) | ||||
| 	} | ||||
| 
 | ||||
| 	return &Store{db: db}, nil | ||||
| } | ||||
| 
 | ||||
| func (s *Store) Close() { | ||||
| 	s.db.Close() | ||||
| } | ||||
| 
 | ||||
| func (s *Store) Save(ctx context.Context, msg *models.Message) error { | ||||
| 	return s.db.Save(msg) | ||||
| } | ||||
							
								
								
									
										74
									
								
								test/cmd/load-test-table/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								test/cmd/load-test-table/main.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/aws/aws-sdk-go-v2/aws" | ||||
| 	"github.com/aws/aws-sdk-go-v2/config" | ||||
| 	"github.com/aws/aws-sdk-go-v2/service/dynamodb" | ||||
| 	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" | ||||
| 	"github.com/brianvoe/gofakeit/v6" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/lmika/awstools/internal/dynamo-browse/models" | ||||
| 	"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo" | ||||
| 	"github.com/lmika/awstools/internal/dynamo-browse/services/tables" | ||||
| 	"github.com/lmika/gopkgs/cli" | ||||
| 	"log" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	ctx := context.Background() | ||||
| 	tableName := "awstools-test" | ||||
| 	totalItems := 300 | ||||
| 
 | ||||
| 	cfg, err := config.LoadDefaultConfig(ctx) | ||||
| 	if err != nil { | ||||
| 		cli.Fatalf("cannot load AWS config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	dynamoClient := dynamodb.NewFromConfig(cfg, | ||||
| 		dynamodb.WithEndpointResolver(dynamodb.EndpointResolverFromURL("http://localhost:8000"))) | ||||
| 
 | ||||
| 	if _, err = dynamoClient.DeleteTable(ctx, &dynamodb.DeleteTableInput{ | ||||
| 		TableName: aws.String(tableName), | ||||
| 	}); err != nil { | ||||
| 		log.Printf("warn: cannot delete table: %v", tableName) | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = dynamoClient.CreateTable(ctx, &dynamodb.CreateTableInput{ | ||||
| 		TableName: aws.String(tableName), | ||||
| 		KeySchema: []types.KeySchemaElement{ | ||||
| 			{AttributeName: aws.String("pk"), KeyType: types.KeyTypeHash}, | ||||
| 			{AttributeName: aws.String("sk"), KeyType: types.KeyTypeRange}, | ||||
| 		}, | ||||
| 		AttributeDefinitions: []types.AttributeDefinition{ | ||||
| 			{AttributeName: aws.String("pk"), AttributeType: types.ScalarAttributeTypeS}, | ||||
| 			{AttributeName: aws.String("sk"), AttributeType: types.ScalarAttributeTypeS}, | ||||
| 		}, | ||||
| 		ProvisionedThroughput: &types.ProvisionedThroughput{ | ||||
| 			ReadCapacityUnits:  aws.Int64(100), | ||||
| 			WriteCapacityUnits: aws.Int64(100), | ||||
| 		}, | ||||
| 	}); err != nil { | ||||
| 		log.Fatalf("warn: cannot create table: %v", tableName) | ||||
| 	} | ||||
| 
 | ||||
| 	dynamoProvider := dynamo.NewProvider(dynamoClient) | ||||
| 	tableService := tables.NewService(dynamoProvider) | ||||
| 
 | ||||
| 	for i := 0; i < totalItems; i++ { | ||||
| 		key := uuid.New().String() | ||||
| 		if err := tableService.Put(ctx, tableName, models.Item{ | ||||
| 			"pk":      &types.AttributeValueMemberS{Value: key}, | ||||
| 			"sk":      &types.AttributeValueMemberS{Value: key}, | ||||
| 			"name":    &types.AttributeValueMemberS{Value: gofakeit.Name()}, | ||||
| 			"address": &types.AttributeValueMemberS{Value: gofakeit.Address().Address}, | ||||
| 			"city":    &types.AttributeValueMemberS{Value: gofakeit.Address().City}, | ||||
| 			"phone":   &types.AttributeValueMemberS{Value: gofakeit.Phone()}, | ||||
| 			"web":     &types.AttributeValueMemberS{Value: gofakeit.URL()}, | ||||
| 		}); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	log.Printf("table '%v' created with %v items", tableName, totalItems) | ||||
| } | ||||
		Loading…
	
		Reference in a new issue