From 730a8e54c06d86b33273b7cffd0ed6884f3d0ddd Mon Sep 17 00:00:00 2001 From: vvatanabe Date: Mon, 30 Oct 2023 18:24:43 +0900 Subject: [PATCH] refactor: refactor AWS authentication process and remove redundant code --- cmd/dynamomq/main.go | 30 ++++---- internal/cli/cli.go | 61 +--------------- internal/cli/cli_test.go | 47 ------------ sdk.go | 151 ++++++++++++--------------------------- sdk_test.go | 94 ++++++++++++++++++++---- 5 files changed, 144 insertions(+), 239 deletions(-) diff --git a/cmd/dynamomq/main.go b/cmd/dynamomq/main.go index 766400b..6f4a0a7 100644 --- a/cmd/dynamomq/main.go +++ b/cmd/dynamomq/main.go @@ -8,9 +8,9 @@ import ( "os" "strings" - "github.com/vvatanabe/dynamomq/internal/cli" - + "github.com/aws/aws-sdk-go-v2/config" "github.com/vvatanabe/dynamomq" + "github.com/vvatanabe/dynamomq/internal/cli" ) func main() { @@ -24,24 +24,24 @@ func main() { fmt.Println("") executionPath, _ := os.Getwd() - fmt.Printf("current directory is: [%s]\n", executionPath) - - region := flag.String("region", dynamomq.AwsRegionDefault, "AWS region") - credentialsProfile := flag.String("profile", dynamomq.AwsProfileDefault, "AWS credentials profile") tableName := flag.String("table", dynamomq.DefaultTableName, "AWS DynamoDB table name") endpoint := flag.String("endpoint-url", "", "AWS DynamoDB base endpoint url") flag.Parse() - fmt.Printf("profile is: [%s]\n", *credentialsProfile) - fmt.Printf("region is: [%s]\n", *region) + cfg, err := config.LoadDefaultConfig(context.Background()) + if err != nil { + fmt.Printf("failed to load aws config: %s\n", err) + os.Exit(1) + } + + fmt.Printf("current dir is: [%s]\n", executionPath) + fmt.Printf("region is: [%s]\n", cfg.Region) fmt.Printf("table is: [%s]\n", *tableName) fmt.Printf("endpoint is: [%s]\n", *endpoint) fmt.Println("") - client, err := dynamomq.NewFromConfig[any](context.Background(), - dynamomq.WithAWSRegion(*region), - dynamomq.WithAWSCredentialsProfileName(*credentialsProfile), + client, err := dynamomq.NewFromConfig[any](cfg, dynamomq.WithTableName(*tableName), dynamomq.WithAWSBaseEndpoint(*endpoint)) if err != nil { @@ -51,11 +51,9 @@ func main() { } c := cli.CLI{ - Region: *region, - CredentialsProfile: *credentialsProfile, - TableName: *tableName, - Client: client, - Message: nil, + TableName: *tableName, + Client: client, + Message: nil, } // 1. Create a Scanner using the InputStream available. diff --git a/internal/cli/cli.go b/internal/cli/cli.go index e8af327..534ad06 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -18,21 +18,15 @@ const ( ) type CLI struct { - Region string - BaseEndpoint string - CredentialsProfile string - TableName string - - Client dynamomq.Client[any] - Message *dynamomq.Message[any] + TableName string + Client dynamomq.Client[any] + Message *dynamomq.Message[any] } func (c *CLI) Run(ctx context.Context, command string, params []string) { switch command { case "h", "?", "help": c.help(ctx, params) - case "aws": - c.aws(ctx, params) case "qstat", "qstats": c.qstat(ctx, params) case "dlq": @@ -70,7 +64,6 @@ func (c *CLI) Run(ctx context.Context, command string, params []string) { func (c *CLI) help(_ context.Context, _ []string) { fmt.Println(`... this is CLI HELP! - > aws --profile --region --table --endpoint-url [Establish connection with AWS; Default profile name: 'default' and region: 'us-east-1'] > qstat | qstats [Retrieves the Queue statistics (no need to be in App mode)] > dlq [Retrieves the Dead Letter Queue (DLQ) statistics] > enqueue-test | et [SendMessage test Message records in DynamoDB: A-101, A-202, A-303 and A-404; if already exists, it will overwrite it] @@ -89,54 +82,6 @@ func (c *CLI) help(_ context.Context, _ []string) { > id`) } -func (c *CLI) aws(ctx context.Context, params []string) { - if len(params) == 0 { - printError("aws --profile --region --table --endpoint-url [Establish connection with AWS; Default profile: 'default' and region: 'us-east-1']") - return - } - profile, region, table, endpoint := parseParams(params) - if region != "" { - c.Region = region - } - if table != "" { - c.TableName = table - } - if profile != "" { - c.CredentialsProfile = profile - } - if endpoint != "" { - c.BaseEndpoint = endpoint - } - client, err := dynamomq.NewFromConfig[any](ctx, - dynamomq.WithAWSRegion(c.Region), - dynamomq.WithAWSCredentialsProfileName(profile), - dynamomq.WithTableName(c.TableName), - dynamomq.WithAWSBaseEndpoint(c.BaseEndpoint)) - if err != nil { - fmt.Printf(" ... AWS session could not be established!: %v\n", err) - } else { - c.Client = client - fmt.Println(" ... AWS session is properly established!") - } -} - -func parseParams(params []string) (profile, region, table, endpoint string) { - // Map to store parsed values - for i := 0; i < len(params)-1; i++ { - switch params[i] { - case "--profile", "-profile": - profile = params[i+1] - case "--region", "-region": - region = params[i+1] - case "--table", "-table": - table = params[i+1] - case "--endpoint-url", "-endpoint-url": - endpoint = params[i+1] - } - } - return -} - func (c *CLI) ls(ctx context.Context, _ []string) { if c.Client == nil { fmt.Println(needAWSMessage) diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index e6d4f0f..7f1e458 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -1,48 +1 @@ package cli - -import "testing" - -func TestParseParams(t *testing.T) { - tests := []struct { - input []string - profile, region, table, endpoint string - }{ - { - input: []string{"--profile", "myProfile", "--region", "us-west-1", "--table", "myTable", "--endpoint-url", "http://localhost:8000"}, - profile: "myProfile", - region: "us-west-1", - table: "myTable", - endpoint: "http://localhost:8000", - }, - { - input: []string{"--profile", "myProfile", "--region", "us-west-1"}, - profile: "myProfile", - region: "us-west-1", - table: "", - endpoint: "", - }, - { - input: []string{"--table", "myTable", "--endpoint-url", "http://localhost:8000"}, - profile: "", - region: "", - table: "myTable", - endpoint: "http://localhost:8000", - }, - } - - for _, tt := range tests { - profile, region, table, endpoint := parseParams(tt.input) - if profile != tt.profile { - t.Errorf("Expected profile %s, got %s", tt.profile, profile) - } - if region != tt.region { - t.Errorf("Expected region %s, got %s", tt.region, region) - } - if table != tt.table { - t.Errorf("Expected table %s, got %s", tt.table, table) - } - if endpoint != tt.endpoint { - t.Errorf("Expected endpoint %s, got %s", tt.endpoint, endpoint) - } - } -} diff --git a/sdk.go b/sdk.go index 089674b..e63046a 100644 --- a/sdk.go +++ b/sdk.go @@ -3,13 +3,9 @@ package dynamomq import ( "context" "errors" - "fmt" - "os" "time" "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/feature/dynamodb/attributevalue" "github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression" "github.com/aws/aws-sdk-go-v2/service/dynamodb" @@ -18,10 +14,8 @@ import ( ) const ( - AwsRegionDefault = "us-east-1" - AwsProfileDefault = "default" DefaultTableName = "dynamo-mq-table" - QueueingIndexName = "dynamo-mq-index-queue_type-queue_add_timestamp" + DefaultQueueingIndexName = "dynamo-mq-index-queue_type-queue_add_timestamp" DefaultRetryMaxAttempts = 10 DefaultVisibilityTimeoutInMinutes = 1 ) @@ -41,145 +35,94 @@ type Client[T any] interface { GetDynamodbClient() *dynamodb.Client } -type client[T any] struct { - dynamoDB *dynamodb.Client - - tableName string - awsRegion string - awsCredentialsProfileName string - baseEndpoint string - credentialsProvider aws.CredentialsProvider - - retryMaxAttempts int - visibilityTimeoutInMinutes int - maximumReceives int - useFIFO bool - - clock clock.Clock -} - type ClientOptions struct { - tableName string - awsRegion string - awsCredentialsProfileName string - credentialsProvider aws.CredentialsProvider - baseEndpoint string - retryMaxAttempts int - visibilityTimeoutInMinutes int - maximumReceives int - useFIFO bool - dynamoDB *dynamodb.Client - clock clock.Clock + DynamoDB *dynamodb.Client + TableName string + VisibilityTimeoutInMinutes int + MaximumReceives int + UseFIFO bool + Clock clock.Clock + + BaseEndpoint string + RetryMaxAttempts int } -func WithTableName(tableName string) func(*ClientOptions) { +func WithAWSDynamoDBClient(client *dynamodb.Client) func(*ClientOptions) { return func(s *ClientOptions) { - s.tableName = tableName + s.DynamoDB = client } } -func WithAWSRegion(awsRegion string) func(*ClientOptions) { +func WithTableName(tableName string) func(*ClientOptions) { return func(s *ClientOptions) { - s.awsRegion = awsRegion + s.TableName = tableName } } -func WithAWSCredentialsProfileName(awsCredentialsProfileName string) func(*ClientOptions) { +func WithAWSVisibilityTimeout(minutes int) func(*ClientOptions) { return func(s *ClientOptions) { - s.awsCredentialsProfileName = awsCredentialsProfileName + s.VisibilityTimeoutInMinutes = minutes } } -func WithAWSCredentialsProvider(credentialsProvider aws.CredentialsProvider) func(*ClientOptions) { +func WithUseFIFO(useFIFO bool) func(*ClientOptions) { return func(s *ClientOptions) { - s.credentialsProvider = credentialsProvider + s.UseFIFO = useFIFO } } func WithAWSBaseEndpoint(baseEndpoint string) func(*ClientOptions) { return func(s *ClientOptions) { - s.baseEndpoint = baseEndpoint + s.BaseEndpoint = baseEndpoint } } func WithAWSRetryMaxAttempts(retryMaxAttempts int) func(*ClientOptions) { return func(s *ClientOptions) { - s.retryMaxAttempts = retryMaxAttempts - } -} - -func WithAWSVisibilityTimeout(minutes int) func(*ClientOptions) { - return func(s *ClientOptions) { - s.visibilityTimeoutInMinutes = minutes - } -} - -func WithUseFIFO(useFIFO bool) func(*ClientOptions) { - return func(s *ClientOptions) { - s.useFIFO = useFIFO - } -} - -func WithAWSDynamoDBClient(client *dynamodb.Client) func(*ClientOptions) { - return func(s *ClientOptions) { - s.dynamoDB = client + s.RetryMaxAttempts = retryMaxAttempts } } -func NewFromConfig[T any](ctx context.Context, optFns ...func(*ClientOptions)) (Client[T], error) { +func NewFromConfig[T any](cfg aws.Config, optFns ...func(*ClientOptions)) (Client[T], error) { o := &ClientOptions{ - tableName: DefaultTableName, - awsRegion: AwsRegionDefault, - awsCredentialsProfileName: AwsProfileDefault, - retryMaxAttempts: DefaultRetryMaxAttempts, - visibilityTimeoutInMinutes: DefaultVisibilityTimeoutInMinutes, - useFIFO: false, - clock: &clock.RealClock{}, + TableName: DefaultTableName, + RetryMaxAttempts: DefaultRetryMaxAttempts, + VisibilityTimeoutInMinutes: DefaultVisibilityTimeoutInMinutes, + UseFIFO: false, + Clock: &clock.RealClock{}, } for _, opt := range optFns { opt(o) } c := &client[T]{ - tableName: o.tableName, - awsRegion: o.awsRegion, - awsCredentialsProfileName: o.awsCredentialsProfileName, - credentialsProvider: o.credentialsProvider, - baseEndpoint: o.baseEndpoint, - retryMaxAttempts: o.retryMaxAttempts, - visibilityTimeoutInMinutes: o.visibilityTimeoutInMinutes, - maximumReceives: o.maximumReceives, - useFIFO: o.useFIFO, - dynamoDB: o.dynamoDB, - clock: o.clock, + tableName: o.TableName, + visibilityTimeoutInMinutes: o.VisibilityTimeoutInMinutes, + maximumReceives: o.MaximumReceives, + useFIFO: o.UseFIFO, + dynamoDB: o.DynamoDB, + clock: o.Clock, } if c.dynamoDB != nil { return c, nil } - if c.credentialsProvider == nil { - accessKey := os.Getenv("AWS_ACCESS_KEY_ID") - secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY") - sessionToken := os.Getenv("AWS_SESSION_TOKEN") - creds := credentials.NewStaticCredentialsProvider(accessKey, secretKey, sessionToken) - c.credentialsProvider = &creds - } - cfg, err := config.LoadDefaultConfig( - ctx, - config.WithRegion(c.awsRegion), - config.WithCredentialsProvider(c.credentialsProvider), - config.WithSharedConfigProfile(c.awsCredentialsProfileName), - ) - if err != nil { - return nil, fmt.Errorf("failed to load aws config: %w", err) - } c.dynamoDB = dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { - options.RetryMaxAttempts = c.retryMaxAttempts - if c.baseEndpoint != "" { - options.BaseEndpoint = aws.String(c.baseEndpoint) + options.RetryMaxAttempts = o.RetryMaxAttempts + if o.BaseEndpoint != "" { + options.BaseEndpoint = aws.String(o.BaseEndpoint) } }) return c, nil } +type client[T any] struct { + dynamoDB *dynamodb.Client + tableName string + visibilityTimeoutInMinutes int + maximumReceives int + useFIFO bool + clock clock.Clock +} + type SendMessageInput[T any] struct { ID string Data T @@ -247,7 +190,7 @@ func (c *client[T]) ReceiveMessage(ctx context.Context, params *ReceiveMessageIn visibilityTimeout := time.Duration(c.visibilityTimeoutInMinutes) * time.Minute for { queryResult, err := c.dynamoDB.Query(ctx, &dynamodb.QueryInput{ - IndexName: aws.String(QueueingIndexName), + IndexName: aws.String(DefaultQueueingIndexName), TableName: aws.String(c.tableName), KeyConditionExpression: expr.KeyCondition(), ExpressionAttributeNames: expr.Names(), @@ -546,7 +489,7 @@ func (c *client[T]) GetQueueStats(ctx context.Context, params *GetQueueStatsInpu processingIDs := make([]string, 0) for { queryOutput, err := c.dynamoDB.Query(ctx, &dynamodb.QueryInput{ - IndexName: aws.String(QueueingIndexName), + IndexName: aws.String(DefaultQueueingIndexName), TableName: aws.String(c.tableName), ExpressionAttributeNames: expr.Names(), KeyConditionExpression: expr.KeyCondition(), @@ -612,7 +555,7 @@ func (c *client[T]) GetDLQStats(ctx context.Context, params *GetDLQStatsInput) ( listBANs := make([]string, 0) for { resp, err := c.dynamoDB.Query(ctx, &dynamodb.QueryInput{ - IndexName: aws.String(QueueingIndexName), + IndexName: aws.String(DefaultQueueingIndexName), TableName: aws.String(c.tableName), ExpressionAttributeNames: expr.Names(), ExpressionAttributeValues: expr.Values(), diff --git a/sdk_test.go b/sdk_test.go index 2df3deb..5f2fc59 100644 --- a/sdk_test.go +++ b/sdk_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/aws/aws-sdk-go-v2/config" "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" @@ -29,7 +30,7 @@ func (m mockClock) Now() time.Time { func withClock(clock clock.Clock) func(s *ClientOptions) { return func(s *ClientOptions) { if clock != nil { - s.clock = clock + s.Clock = clock } } } @@ -181,7 +182,12 @@ func TestDynamoMQClientSendMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -324,7 +330,12 @@ func TestDynamoMQClientReceiveMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -365,7 +376,12 @@ func TestDynamoMQClientReceiveMessageUseFIFO(t *testing.T) { now := time.Date(2023, 12, 1, 0, 0, 10, 0, time.UTC) ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(mockClock{ t: now, @@ -534,7 +550,12 @@ func TestDynamoMQClientReceiveMessageNotUseFIFO(t *testing.T) { now := time.Date(2023, 12, 1, 0, 0, 10, 0, time.UTC) ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(mockClock{ t: now, @@ -730,7 +751,12 @@ func TestDynamoMQClientUpdateMessageAsVisible(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -816,7 +842,12 @@ func TestDynamoMQClientDeleteMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -940,7 +971,12 @@ func TestDynamoMQClientMoveMessageToDLQ(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1035,7 +1071,12 @@ func TestDynamoMQClientRedriveMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1147,7 +1188,12 @@ func TestDynamoMQClientGetQueueStats(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1226,7 +1272,12 @@ func TestDynamoMQClientGetDLQStats(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1310,7 +1361,12 @@ func TestDynamoMQClientGetMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1398,7 +1454,12 @@ func TestDynamoMQClientReplaceMessage(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return @@ -1487,7 +1548,12 @@ func TestDynamoMQClientListMessages(t *testing.T) { raw, clean := tt.setup(t) defer clean() ctx := context.Background() - client, err := NewFromConfig[test.MessageData](ctx, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + t.Fatalf("failed to load aws config: %s\n", err) + return + } + client, err := NewFromConfig[test.MessageData](cfg, WithAWSDynamoDBClient(raw), withClock(tt.sdkClock), WithAWSVisibilityTimeout(1)) if err != nil { t.Fatalf("NewFromConfig() error = %v", err) return