From 06f90028f731554673755dba72c1382b28e26df9 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 28 Jan 2026 01:07:11 -0800 Subject: [PATCH] s3tables test: refactor to eliminate duplicate definitions - Move all client methods to client.go - Remove duplicate types/constants from s3tables_integration_test.go - Keep setup.go for test infrastructure - Keep integration test logic in s3tables_integration_test.go - Clean up unused imports - Test compiles successfully --- test/s3tables/client.go | 559 +++++++++++++++------ test/s3tables/s3tables_integration_test.go | 517 ------------------- 2 files changed, 403 insertions(+), 673 deletions(-) diff --git a/test/s3tables/client.go b/test/s3tables/client.go index 913f404fe..48294ff29 100644 --- a/test/s3tables/client.go +++ b/test/s3tables/client.go @@ -9,227 +9,474 @@ import ( "github.com/seaweedfs/seaweedfs/weed/s3api/s3tables" ) -// doRequest sends a request to the S3 Tables API func (c *S3TablesClient) doRequest(operation string, body interface{}) (*http.Response, error) { -var bodyBytes []byte -var err error + var bodyBytes []byte + var err error -if body != nil { -bodyBytes, err = json.Marshal(body) -if err != nil { -return nil, fmt.Errorf("failed to marshal request body: %w", err) -} -} + if body != nil { + bodyBytes, err = json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("failed to marshal request body: %w", err) + } + } -req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(bodyBytes)) -if err != nil { -return nil, fmt.Errorf("failed to create request: %w", err) -} + req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(bodyBytes)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } -req.Header.Set("Content-Type", "application/x-amz-json-1.1") -req.Header.Set("X-Amz-Target", "S3Tables."+operation) + req.Header.Set("Content-Type", "application/x-amz-json-1.1") + req.Header.Set("X-Amz-Target", "S3Tables."+operation) -// For testing, we use basic auth or skip auth (the test cluster uses anonymous for simplicity) -// In production, AWS Signature V4 would be used + // For testing, we use basic auth or skip auth (the test cluster uses anonymous for simplicity) + // In production, AWS Signature V4 would be used -return c.client.Do(req) + return c.client.Do(req) } // Table Bucket operations func (c *S3TablesClient) CreateTableBucket(name string, tags map[string]string) (*s3tables.CreateTableBucketResponse, error) { -req := &s3tables.CreateTableBucketRequest{ -Name: name, -Tags: tags, -} + req := &s3tables.CreateTableBucketRequest{ + Name: name, + Tags: tags, + } -resp, err := c.doRequest("CreateTableBucket", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("CreateTableBucket", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("CreateTableBucket failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("CreateTableBucket failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.CreateTableBucketResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.CreateTableBucketResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) GetTableBucket(arn string) (*s3tables.GetTableBucketResponse, error) { -req := &s3tables.GetTableBucketRequest{ -TableBucketARN: arn, -} + req := &s3tables.GetTableBucketRequest{ + TableBucketARN: arn, + } -resp, err := c.doRequest("GetTableBucket", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("GetTableBucket", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("GetTableBucket failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("GetTableBucket failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.GetTableBucketResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.GetTableBucketResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) ListTableBuckets(prefix string) (*s3tables.ListTableBucketsResponse, error) { -req := &s3tables.ListTableBucketsRequest{ -Prefix: prefix, -} + req := &s3tables.ListTableBucketsRequest{ + Prefix: prefix, + } -resp, err := c.doRequest("ListTableBuckets", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("ListTableBuckets", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("ListTableBuckets failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("ListTableBuckets failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.ListTableBucketsResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.ListTableBucketsResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) DeleteTableBucket(arn string) error { -req := &s3tables.DeleteTableBucketRequest{ -TableBucketARN: arn, -} + req := &s3tables.DeleteTableBucketRequest{ + TableBucketARN: arn, + } -resp, err := c.doRequest("DeleteTableBucket", req) -if err != nil { -return err -} -defer resp.Body.Close() + resp, err := c.doRequest("DeleteTableBucket", req) + if err != nil { + return err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return fmt.Errorf("DeleteTableBucket failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("DeleteTableBucket failed: %s - %s", errResp.Type, errResp.Message) + } -return nil + return nil } // Namespace operations func (c *S3TablesClient) CreateNamespace(bucketARN string, namespace []string) (*s3tables.CreateNamespaceResponse, error) { -req := &s3tables.CreateNamespaceRequest{ -TableBucketARN: bucketARN, -Namespace: namespace, -} + req := &s3tables.CreateNamespaceRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + } -resp, err := c.doRequest("CreateNamespace", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("CreateNamespace", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("CreateNamespace failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("CreateNamespace failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.CreateNamespaceResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.CreateNamespaceResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) GetNamespace(bucketARN, namespace string) (*s3tables.GetNamespaceResponse, error) { -req := &s3tables.GetNamespaceRequest{ -TableBucketARN: bucketARN, -Namespace: namespace, -} + req := &s3tables.GetNamespaceRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + } -resp, err := c.doRequest("GetNamespace", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("GetNamespace", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("GetNamespace failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("GetNamespace failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.GetNamespaceResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.GetNamespaceResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) ListNamespaces(bucketARN, prefix string) (*s3tables.ListNamespacesResponse, error) { -req := &s3tables.ListNamespacesRequest{ -TableBucketARN: bucketARN, -Prefix: prefix, -} + req := &s3tables.ListNamespacesRequest{ + TableBucketARN: bucketARN, + Prefix: prefix, + } -resp, err := c.doRequest("ListNamespaces", req) -if err != nil { -return nil, err -} -defer resp.Body.Close() + resp, err := c.doRequest("ListNamespaces", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return nil, fmt.Errorf("ListNamespaces failed: %s - %s", errResp.Type, errResp.Message) -} + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("ListNamespaces failed: %s - %s", errResp.Type, errResp.Message) + } -var result s3tables.ListNamespacesResponse -if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -return nil, fmt.Errorf("failed to decode response: %w", err) -} + var result s3tables.ListNamespacesResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } -return &result, nil + return &result, nil } func (c *S3TablesClient) DeleteNamespace(bucketARN, namespace string) error { -req := &s3tables.DeleteNamespaceRequest{ -TableBucketARN: bucketARN, -Namespace: namespace, + req := &s3tables.DeleteNamespaceRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + } + + resp, err := c.doRequest("DeleteNamespace", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("DeleteNamespace failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil +} + +// Table operations + +func (c *S3TablesClient) CreateTable(bucketARN, namespace, name, format string, metadata *s3tables.TableMetadata, tags map[string]string) (*s3tables.CreateTableResponse, error) { + req := &s3tables.CreateTableRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Name: name, + Format: format, + Metadata: metadata, + Tags: tags, + } + + resp, err := c.doRequest("CreateTable", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("CreateTable failed: %s - %s", errResp.Type, errResp.Message) + } + + var result s3tables.CreateTableResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + return &result, nil +} + +func (c *S3TablesClient) GetTable(bucketARN, namespace, name string) (*s3tables.GetTableResponse, error) { + req := &s3tables.GetTableRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Name: name, + } + + resp, err := c.doRequest("GetTable", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("GetTable failed: %s - %s", errResp.Type, errResp.Message) + } + + var result s3tables.GetTableResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + return &result, nil +} + +func (c *S3TablesClient) ListTables(bucketARN, namespace, prefix string) (*s3tables.ListTablesResponse, error) { + req := &s3tables.ListTablesRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Prefix: prefix, + } + + resp, err := c.doRequest("ListTables", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("ListTables failed: %s - %s", errResp.Type, errResp.Message) + } + + var result s3tables.ListTablesResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + return &result, nil +} + +func (c *S3TablesClient) DeleteTable(bucketARN, namespace, name string) error { + req := &s3tables.DeleteTableRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Name: name, + } + + resp, err := c.doRequest("DeleteTable", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("DeleteTable failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil +} + +// Policy operations + +func (c *S3TablesClient) PutTableBucketPolicy(bucketARN, policy string) error { + req := &s3tables.PutTableBucketPolicyRequest{ + TableBucketARN: bucketARN, + ResourcePolicy: policy, + } + + resp, err := c.doRequest("PutTableBucketPolicy", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("PutTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil +} + +func (c *S3TablesClient) GetTableBucketPolicy(bucketARN string) (*s3tables.GetTableBucketPolicyResponse, error) { + req := &s3tables.GetTableBucketPolicyRequest{ + TableBucketARN: bucketARN, + } + + resp, err := c.doRequest("GetTableBucketPolicy", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("GetTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) + } + + var result s3tables.GetTableBucketPolicyResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + return &result, nil } -resp, err := c.doRequest("DeleteNamespace", req) -if err != nil { -return err +func (c *S3TablesClient) DeleteTableBucketPolicy(bucketARN string) error { + req := &s3tables.DeleteTableBucketPolicyRequest{ + TableBucketARN: bucketARN, + } + + resp, err := c.doRequest("DeleteTableBucketPolicy", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("DeleteTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil } -defer resp.Body.Close() -if resp.StatusCode != http.StatusOK { -var errResp s3tables.S3TablesError -json.NewDecoder(resp.Body).Decode(&errResp) -return fmt.Errorf("DeleteNamespace failed: %s - %s", errResp.Type, errResp.Message) +// Tagging operations + +func (c *S3TablesClient) TagResource(resourceARN string, tags map[string]string) error { + req := &s3tables.TagResourceRequest{ + ResourceARN: resourceARN, + Tags: tags, + } + + resp, err := c.doRequest("TagResource", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("TagResource failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil +} + +func (c *S3TablesClient) ListTagsForResource(resourceARN string) (*s3tables.ListTagsForResourceResponse, error) { + req := &s3tables.ListTagsForResourceRequest{ + ResourceARN: resourceARN, + } + + resp, err := c.doRequest("ListTagsForResource", req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return nil, fmt.Errorf("ListTagsForResource failed: %s - %s", errResp.Type, errResp.Message) + } + + var result s3tables.ListTagsForResourceResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + return &result, nil } -return nil +func (c *S3TablesClient) UntagResource(resourceARN string, tagKeys []string) error { + req := &s3tables.UntagResourceRequest{ + ResourceARN: resourceARN, + TagKeys: tagKeys, + } + + resp, err := c.doRequest("UntagResource", req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp s3tables.S3TablesError + json.NewDecoder(resp.Body).Decode(&errResp) + return fmt.Errorf("UntagResource failed: %s - %s", errResp.Type, errResp.Message) + } + + return nil } + +// Integration tests + diff --git a/test/s3tables/s3tables_integration_test.go b/test/s3tables/s3tables_integration_test.go index 62dafc993..dffdc972b 100644 --- a/test/s3tables/s3tables_integration_test.go +++ b/test/s3tables/s3tables_integration_test.go @@ -1,9 +1,7 @@ package s3tables import ( - "bytes" "context" - "encoding/json" "fmt" "math/rand" "net" @@ -11,7 +9,6 @@ import ( "os" "path/filepath" "strconv" - "sync" "testing" "time" @@ -24,520 +21,6 @@ import ( flag "github.com/seaweedfs/seaweedfs/weed/util/fla9" ) -const ( - testRegion = "us-west-2" - testAccessKey = "admin" - testSecretKey = "admin" - testAccountID = "111122223333" -) - -// TestCluster manages the weed mini instance for integration testing -type TestCluster struct { - dataDir string - ctx context.Context - cancel context.CancelFunc - isRunning bool - startOnce sync.Once - wg sync.WaitGroup - masterPort int - volumePort int - filerPort int - s3Port int - s3Endpoint string -} - -// S3TablesClient is a simple client for S3 Tables API -type S3TablesClient struct { - endpoint string - region string - accessKey string - secretKey string - client *http.Client -} - -func NewS3TablesClient(endpoint, region, accessKey, secretKey string) *S3TablesClient { - return &S3TablesClient{ - endpoint: endpoint, - region: region, - accessKey: accessKey, - secretKey: secretKey, - client: &http.Client{Timeout: 30 * time.Second}, - } -} - -// doRequest sends a request to the S3 Tables API -func (c *S3TablesClient) doRequest(operation string, body interface{}) (*http.Response, error) { - var bodyBytes []byte - var err error - - if body != nil { - bodyBytes, err = json.Marshal(body) - if err != nil { - return nil, fmt.Errorf("failed to marshal request body: %w", err) - } - } - - req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(bodyBytes)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/x-amz-json-1.1") - req.Header.Set("X-Amz-Target", "S3Tables."+operation) - - // For testing, we use basic auth or skip auth (the test cluster uses anonymous for simplicity) - // In production, AWS Signature V4 would be used - - return c.client.Do(req) -} - -// Table Bucket operations - -func (c *S3TablesClient) CreateTableBucket(name string, tags map[string]string) (*s3tables.CreateTableBucketResponse, error) { - req := &s3tables.CreateTableBucketRequest{ - Name: name, - Tags: tags, - } - - resp, err := c.doRequest("CreateTableBucket", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("CreateTableBucket failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.CreateTableBucketResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) GetTableBucket(arn string) (*s3tables.GetTableBucketResponse, error) { - req := &s3tables.GetTableBucketRequest{ - TableBucketARN: arn, - } - - resp, err := c.doRequest("GetTableBucket", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("GetTableBucket failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.GetTableBucketResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) ListTableBuckets(prefix string) (*s3tables.ListTableBucketsResponse, error) { - req := &s3tables.ListTableBucketsRequest{ - Prefix: prefix, - } - - resp, err := c.doRequest("ListTableBuckets", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("ListTableBuckets failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.ListTableBucketsResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) DeleteTableBucket(arn string) error { - req := &s3tables.DeleteTableBucketRequest{ - TableBucketARN: arn, - } - - resp, err := c.doRequest("DeleteTableBucket", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("DeleteTableBucket failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -// Namespace operations - -func (c *S3TablesClient) CreateNamespace(bucketARN string, namespace []string) (*s3tables.CreateNamespaceResponse, error) { - req := &s3tables.CreateNamespaceRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - } - - resp, err := c.doRequest("CreateNamespace", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("CreateNamespace failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.CreateNamespaceResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) GetNamespace(bucketARN, namespace string) (*s3tables.GetNamespaceResponse, error) { - req := &s3tables.GetNamespaceRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - } - - resp, err := c.doRequest("GetNamespace", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("GetNamespace failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.GetNamespaceResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) ListNamespaces(bucketARN, prefix string) (*s3tables.ListNamespacesResponse, error) { - req := &s3tables.ListNamespacesRequest{ - TableBucketARN: bucketARN, - Prefix: prefix, - } - - resp, err := c.doRequest("ListNamespaces", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("ListNamespaces failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.ListNamespacesResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) DeleteNamespace(bucketARN, namespace string) error { - req := &s3tables.DeleteNamespaceRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - } - - resp, err := c.doRequest("DeleteNamespace", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("DeleteNamespace failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -// Table operations - -func (c *S3TablesClient) CreateTable(bucketARN, namespace, name, format string, metadata *s3tables.TableMetadata, tags map[string]string) (*s3tables.CreateTableResponse, error) { - req := &s3tables.CreateTableRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - Name: name, - Format: format, - Metadata: metadata, - Tags: tags, - } - - resp, err := c.doRequest("CreateTable", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("CreateTable failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.CreateTableResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) GetTable(bucketARN, namespace, name string) (*s3tables.GetTableResponse, error) { - req := &s3tables.GetTableRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - Name: name, - } - - resp, err := c.doRequest("GetTable", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("GetTable failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.GetTableResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) ListTables(bucketARN, namespace, prefix string) (*s3tables.ListTablesResponse, error) { - req := &s3tables.ListTablesRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - Prefix: prefix, - } - - resp, err := c.doRequest("ListTables", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("ListTables failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.ListTablesResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) DeleteTable(bucketARN, namespace, name string) error { - req := &s3tables.DeleteTableRequest{ - TableBucketARN: bucketARN, - Namespace: namespace, - Name: name, - } - - resp, err := c.doRequest("DeleteTable", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("DeleteTable failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -// Policy operations - -func (c *S3TablesClient) PutTableBucketPolicy(bucketARN, policy string) error { - req := &s3tables.PutTableBucketPolicyRequest{ - TableBucketARN: bucketARN, - ResourcePolicy: policy, - } - - resp, err := c.doRequest("PutTableBucketPolicy", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("PutTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -func (c *S3TablesClient) GetTableBucketPolicy(bucketARN string) (*s3tables.GetTableBucketPolicyResponse, error) { - req := &s3tables.GetTableBucketPolicyRequest{ - TableBucketARN: bucketARN, - } - - resp, err := c.doRequest("GetTableBucketPolicy", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("GetTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.GetTableBucketPolicyResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) DeleteTableBucketPolicy(bucketARN string) error { - req := &s3tables.DeleteTableBucketPolicyRequest{ - TableBucketARN: bucketARN, - } - - resp, err := c.doRequest("DeleteTableBucketPolicy", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("DeleteTableBucketPolicy failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -// Tagging operations - -func (c *S3TablesClient) TagResource(resourceARN string, tags map[string]string) error { - req := &s3tables.TagResourceRequest{ - ResourceARN: resourceARN, - Tags: tags, - } - - resp, err := c.doRequest("TagResource", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("TagResource failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -func (c *S3TablesClient) ListTagsForResource(resourceARN string) (*s3tables.ListTagsForResourceResponse, error) { - req := &s3tables.ListTagsForResourceRequest{ - ResourceARN: resourceARN, - } - - resp, err := c.doRequest("ListTagsForResource", req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return nil, fmt.Errorf("ListTagsForResource failed: %s - %s", errResp.Type, errResp.Message) - } - - var result s3tables.ListTagsForResourceResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -func (c *S3TablesClient) UntagResource(resourceARN string, tagKeys []string) error { - req := &s3tables.UntagResourceRequest{ - ResourceARN: resourceARN, - TagKeys: tagKeys, - } - - resp, err := c.doRequest("UntagResource", req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp s3tables.S3TablesError - json.NewDecoder(resp.Body).Decode(&errResp) - return fmt.Errorf("UntagResource failed: %s - %s", errResp.Type, errResp.Message) - } - - return nil -} - -// Integration tests - -// TestS3TablesIntegration tests S3 Tables API operations func TestS3TablesIntegration(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode")