From 4f470e8348b1364a87a6780faaade58f5dab4423 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Thu, 22 Aug 2024 12:08:35 -0500 Subject: [PATCH] Add basic dynamodb service --- .github/workflows/ci.yml | 5 ++++ aws/dynamo/service.go | 49 ++++++++++++++++++++++++++++++++++++++ aws/dynamo/service_test.go | 40 +++++++++++++++++++++++++++++++ aws/s3x/service.go | 4 ++-- go.mod | 3 +++ go.sum | 12 ++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 aws/dynamo/service.go create mode 100644 aws/dynamo/service_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d2b53a..3b321f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,11 @@ jobs: - name: Install Linux packages run: sudo apt install -y --no-install-recommends pandoc + - name: Install and start DynamoDB + uses: rrainn/dynamodb-action@v2.0.1 + with: + port: 6000 + - name: Install Go uses: actions/setup-go@v5 with: diff --git a/aws/dynamo/service.go b/aws/dynamo/service.go new file mode 100644 index 0000000..d33ea6d --- /dev/null +++ b/aws/dynamo/service.go @@ -0,0 +1,49 @@ +package dynamo + +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/credentials" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" +) + +// Service is simple abstraction layer to work with a DynamoDB-compatible database +type Service struct { + Client *dynamodb.Client + tablePrefix string +} + +// NewService creates a new S3 service with the given credentials and configuration +func NewService(accessKey, secretKey, region, endpoint, tablePrefix string) (*Service, error) { + opts := []func(*config.LoadOptions) error{config.WithRegion(region)} + + if accessKey != "" && secretKey != "" { + opts = append(opts, config.WithCredentialsProvider(credentials.StaticCredentialsProvider{Value: aws.Credentials{ + AccessKeyID: accessKey, SecretAccessKey: secretKey, + }})) + } + + cfg, err := config.LoadDefaultConfig(context.TODO(), opts...) + if err != nil { + return nil, err + } + + client := dynamodb.NewFromConfig(cfg, func(o *dynamodb.Options) { + if endpoint != "" { + o.BaseEndpoint = aws.String(endpoint) + } + }) + + return &Service{Client: client, tablePrefix: tablePrefix}, nil +} + +func (s *Service) Test(ctx context.Context, table string) error { + _, err := s.Client.DescribeTable(ctx, &dynamodb.DescribeTableInput{TableName: aws.String(s.TableName(table))}) + return err +} + +func (s *Service) TableName(base string) string { + return s.tablePrefix + base +} diff --git a/aws/dynamo/service_test.go b/aws/dynamo/service_test.go new file mode 100644 index 0000000..11cd960 --- /dev/null +++ b/aws/dynamo/service_test.go @@ -0,0 +1,40 @@ +package dynamo_test + +import ( + "context" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/nyaruka/gocommon/aws/dynamo" + "github.com/stretchr/testify/assert" +) + +func TestService(t *testing.T) { + ctx := context.Background() + + svc, err := dynamo.NewService("root", "tembatemba", "us-east-1", "http://localhost:6000", "Test") + assert.NoError(t, err) + + err = svc.Test(ctx, "Things") + assert.ErrorContains(t, err, "ResourceNotFoundException") + + _, err = svc.Client.CreateTable(ctx, &dynamodb.CreateTableInput{ + TableName: aws.String("TestThings"), + KeySchema: []types.KeySchemaElement{ + {AttributeName: aws.String("UUID"), KeyType: types.KeyTypeHash}, + }, + AttributeDefinitions: []types.AttributeDefinition{ + {AttributeName: aws.String("UUID"), AttributeType: types.ScalarAttributeTypeS}, + }, + BillingMode: types.BillingModePayPerRequest, + }) + assert.NoError(t, err) + + err = svc.Test(ctx, "Things") + assert.NoError(t, err) + + _, err = svc.Client.DeleteTable(ctx, &dynamodb.DeleteTableInput{TableName: aws.String("TestThings")}) + assert.NoError(t, err) +} diff --git a/aws/s3x/service.go b/aws/s3x/service.go index f63bf10..2360343 100644 --- a/aws/s3x/service.go +++ b/aws/s3x/service.go @@ -44,11 +44,11 @@ func NewService(accessKey, secretKey, region, endpoint string, minio bool) (*Ser } client := s3.NewFromConfig(cfg, func(o *s3.Options) { - o.UsePathStyle = minio // urls as endpoint/bucket/key instead of bucket.endpoint/key - if endpoint != "" { o.BaseEndpoint = aws.String(endpoint) } + + o.UsePathStyle = minio // urls as endpoint/bucket/key instead of bucket.endpoint/key }) return &Service{Client: client, urler: urler}, nil diff --git a/go.mod b/go.mod index 00661c9..b89daf8 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.30.4 github.com/aws/aws-sdk-go-v2/config v1.27.28 github.com/aws/aws-sdk-go-v2/credentials v1.17.28 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.5 github.com/aws/aws-sdk-go-v2/service/s3 v1.60.0 github.com/gabriel-vasile/mimetype v1.4.5 github.com/go-chi/chi/v5 v5.1.0 @@ -35,6 +36,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect @@ -44,6 +46,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/crypto v0.25.0 // indirect diff --git a/go.sum b/go.sum index ffc44e9..4228a5b 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,14 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvK github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 h1:mimdLQkIX1zr8GIPY1ZtALdBQGxcASiBd2MOp8m/dMc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16/go.mod h1:YHk6owoSwrIsok+cAH9PENCOGoH5PU2EllX4vLtSrsY= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.5 h1:Cm77yt+/CV7A6DglkENsWA3H1hq8+4ItJnFKrhxHkvg= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.5/go.mod h1:s2fYaueBuCnwv1XQn6T8TfShxJWusv5tWPMcL+GY6+g= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 h1:GckUnpm4EJOAio1c8o25a+b3lVfwVzC9gnSBqiiNmZM= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18/go.mod h1:Br6+bxfG33Dk3ynmkhsW2Z/t9D4+lRqdLDNCKi85w0U= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.17 h1:HDJGz1jlV7RokVgTPfx1UHBHANC0N5Uk++xgyYgz5E0= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.17/go.mod h1:5szDu6TWdRDytfDxUQVv2OYfpTQMKApVFyqpm+TcA98= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 h1:jg16PhLPUiHIj8zYIW6bqzeQSuHVEiWnGA0Brz5Xv2I= @@ -36,6 +40,7 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 h1:iAckBT2OeEK/kBDyN/jDtpEExhje github.com/aws/aws-sdk-go-v2/service/sts v1.30.4/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= @@ -60,6 +65,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= +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 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -78,6 +87,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -98,5 +108,7 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=