diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14758a4..f192526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,10 +9,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5.0.0 + uses: actions/checkout@v6.0.1 - name: Install Go - uses: actions/setup-go@v6.0.0 + uses: actions/setup-go@v6.1.0 with: go-version-file: go.mod cache: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 05a7e95..c49fdc5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5.0.0 + uses: actions/checkout@v6.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/vulnerability.yml b/.github/workflows/vulnerability.yml index 9c81783..c106052 100644 --- a/.github/workflows/vulnerability.yml +++ b/.github/workflows/vulnerability.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5.0.0 + uses: actions/checkout@v6.0.1 - name: Install Go - uses: actions/setup-go@v6.0.0 + uses: actions/setup-go@v6.1.0 with: go-version-file: go.mod cache: true diff --git a/CHANGELOG.md b/CHANGELOG.md index c7c26f9..58453fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/). +## 0.44.0 (5th December 2025) + +### Added: +* Added Transit Gateway invitation support: `ListInvitations()`, `AcceptInvitation()`, and `RejectInvitation()` methods for both Pro and Active-Active subscriptions + ## 0.43.0 (11th November 2025) ### Added: diff --git a/go.mod b/go.mod index 4ed36f9..fb5ef5e 100644 --- a/go.mod +++ b/go.mod @@ -2,21 +2,21 @@ module github.com/RedisLabs/rediscloud-go-api go 1.24.0 -toolchain go1.25.3 +toolchain go1.25.5 require ( github.com/avast/retry-go/v4 v4.7.0 github.com/stretchr/testify v1.11.1 - golang.org/x/tools v0.38.0 + golang.org/x/tools v0.39.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4e3a8b0..ec42da9 100644 --- a/go.sum +++ b/go.sum @@ -10,16 +10,16 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= +golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/service/transit_gateway/attachments/model.go b/service/transit_gateway/attachments/model.go index 64a25c7..a412828 100644 --- a/service/transit_gateway/attachments/model.go +++ b/service/transit_gateway/attachments/model.go @@ -38,6 +38,32 @@ type updateCidrs struct { Cidrs *[]*string `json:"cidrs,omitempty"` } +type TransitGatewayInvitation struct { + Id *int `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ResourceShareUid *string `json:"resourceShareUid,omitempty"` + AwsAccountId *string `json:"awsAccountId,omitempty"` + Status *string `json:"status,omitempty"` + SharedDate *string `json:"sharedDate,omitempty"` +} + +type InvitationsResource struct { + Resources []*TransitGatewayInvitation `json:"resources,omitempty"` +} + +type InvitationsResponse struct { + CommandType *string `json:"commandType,omitempty"` + Description *string `json:"description,omitempty"` + Status *string `json:"status,omitempty"` + ID *string `json:"taskId,omitempty"` + Response *InvitationResponseData `json:"response,omitempty"` +} + +type InvitationResponseData struct { + ResourceId *int `json:"resourceId,omitempty"` + Resource *InvitationsResource `json:"resource,omitempty"` +} + type NotFound struct { subId int } diff --git a/service/transit_gateway/attachments/service.go b/service/transit_gateway/attachments/service.go index c0bfe60..661a60e 100644 --- a/service/transit_gateway/attachments/service.go +++ b/service/transit_gateway/attachments/service.go @@ -114,6 +114,66 @@ func (a *API) DeleteActiveActive(ctx context.Context, subscription int, regionId return nil } +func (a *API) ListInvitations(ctx context.Context, subscription int) ([]*TransitGatewayInvitation, error) { + message := fmt.Sprintf("list TGw invitations for subscription %d", subscription) + address := fmt.Sprintf("/subscriptions/%d/transitGateways/invitations", subscription) + invitations, err := a.listInvitations(ctx, message, address) + if err != nil { + return nil, wrap404Error(subscription, err) + } + return invitations, nil +} + +func (a *API) ListInvitationsActiveActive(ctx context.Context, subscription int, regionId int) ([]*TransitGatewayInvitation, error) { + message := fmt.Sprintf("list TGw invitations for subscription %d in region %d", subscription, regionId) + address := fmt.Sprintf("/subscriptions/%d/regions/%d/transitGateways/invitations", subscription, regionId) + invitations, err := a.listInvitations(ctx, message, address) + if err != nil { + return nil, wrap404ErrorActiveActive(subscription, regionId, err) + } + return invitations, nil +} + +func (a *API) AcceptInvitation(ctx context.Context, subscription int, tgwInvitationId int) error { + message := fmt.Sprintf("accept TGw invitation %d for subscription %d", tgwInvitationId, subscription) + address := fmt.Sprintf("/subscriptions/%d/transitGateways/invitations/%d/accept", subscription, tgwInvitationId) + err := a.acceptInvitation(ctx, message, address) + if err != nil { + return wrap404Error(subscription, err) + } + return nil +} + +func (a *API) AcceptInvitationActiveActive(ctx context.Context, subscription int, regionId int, tgwInvitationId int) error { + message := fmt.Sprintf("accept TGw invitation %d for subscription %d in region %d", tgwInvitationId, subscription, regionId) + address := fmt.Sprintf("/subscriptions/%d/regions/%d/transitGateways/invitations/%d/accept", subscription, regionId, tgwInvitationId) + err := a.acceptInvitation(ctx, message, address) + if err != nil { + return wrap404ErrorActiveActive(subscription, regionId, err) + } + return nil +} + +func (a *API) RejectInvitation(ctx context.Context, subscription int, tgwInvitationId int) error { + message := fmt.Sprintf("reject TGw invitation %d for subscription %d", tgwInvitationId, subscription) + address := fmt.Sprintf("/subscriptions/%d/transitGateways/invitations/%d/reject", subscription, tgwInvitationId) + err := a.rejectInvitation(ctx, message, address) + if err != nil { + return wrap404Error(subscription, err) + } + return nil +} + +func (a *API) RejectInvitationActiveActive(ctx context.Context, subscription int, regionId int, tgwInvitationId int) error { + message := fmt.Sprintf("reject TGw invitation %d for subscription %d in region %d", tgwInvitationId, subscription, regionId) + address := fmt.Sprintf("/subscriptions/%d/regions/%d/transitGateways/invitations/%d/reject", subscription, regionId, tgwInvitationId) + err := a.rejectInvitation(ctx, message, address) + if err != nil { + return wrap404ErrorActiveActive(subscription, regionId, err) + } + return nil +} + func (a *API) get(ctx context.Context, message string, address string) (*GetAttachmentsTask, error) { var task internal.TaskResponse err := a.client.Get(ctx, message, address, &task) @@ -124,8 +184,11 @@ func (a *API) get(ctx context.Context, message string, address string) (*GetAtta a.logger.Printf("Waiting for tgwGetRequest %d to complete", task.ID) err = a.taskWaiter.Wait(ctx, *task.ID) + if err != nil { + return nil, err + } - a.logger.Printf("tgwGetRequest %d completed, possibly with error", task.ID, err) + a.logger.Printf("tgwGetRequest %d completed", task.ID) var getAttachmentsTask *GetAttachmentsTask err = a.client.Get(ctx, @@ -194,6 +257,70 @@ func (a *API) delete(ctx context.Context, message string, address string) error return nil } +func (a *API) listInvitations(ctx context.Context, message string, address string) ([]*TransitGatewayInvitation, error) { + var task internal.TaskResponse + err := a.client.Get(ctx, message, address, &task) + if err != nil { + return nil, err + } + + a.logger.Printf("Waiting for tgwListInvitationsRequest %d to complete", task.ID) + + err = a.taskWaiter.Wait(ctx, *task.ID) + if err != nil { + return nil, err + } + + a.logger.Printf("tgwListInvitationsRequest %d completed", task.ID) + + var invitationsResponse *InvitationsResponse + err = a.client.Get(ctx, + fmt.Sprintf("retrieve completed tgwListInvitationsRequest task %d", task.ID), + "/tasks/"+*task.ID, + &invitationsResponse, + ) + + if err != nil { + return nil, fmt.Errorf("failed to retrieve completed tgwListInvitationsRequest %d: %w", task.ID, err) + } + + return invitationsResponse.Response.Resource.Resources, nil +} + +func (a *API) acceptInvitation(ctx context.Context, message string, address string) error { + var task internal.TaskResponse + err := a.client.Put(ctx, message, address, nil, &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for task %s to finish accepting the TGw invitation", task) + + err = a.taskWaiter.Wait(ctx, *task.ID) + if err != nil { + return fmt.Errorf("failed when accepting TGw invitation %w", err) + } + + return nil +} + +func (a *API) rejectInvitation(ctx context.Context, message string, address string) error { + var task internal.TaskResponse + err := a.client.Put(ctx, message, address, nil, &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for task %s to finish rejecting the TGw invitation", task) + + err = a.taskWaiter.Wait(ctx, *task.ID) + if err != nil { + return fmt.Errorf("failed when rejecting TGw invitation %w", err) + } + + return nil +} + func wrap404Error(subId int, err error) error { if v, ok := err.(*internal.HTTPError); ok && v.StatusCode == http.StatusNotFound { return &NotFound{subId: subId} diff --git a/transit_gateway_attachments_test.go b/transit_gateway_attachments_test.go index de80f96..110561a 100644 --- a/transit_gateway_attachments_test.go +++ b/transit_gateway_attachments_test.go @@ -668,3 +668,457 @@ func TestDeleteActiveActiveAttachment(t *testing.T) { err = subject.TransitGatewayAttachments.DeleteActiveActive(context.TODO(), 113991, 1, 35) require.NoError(t, err) } + +func TestListInvitations(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + getRequest( + t, + "/subscriptions/114019/transitGateways/invitations", + `{ + "taskId": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "commandType": "tgwInvitationsGetRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T09:26:40.929904847Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + `{ + "taskId": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "commandType": "tgwInvitationsGetRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T09:26:49.847808891Z", + "response": { + "resourceId": 114019, + "resource": { + "resources": [ + { + "id": 1, + "name": "tf-test-invitation-1", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/abc123", + "awsAccountId": "620187402834", + "status": "pending", + "sharedDate": "2024-07-16T00:00:00.000+00:00" + }, + { + "id": 2, + "name": "tf-test-invitation-2", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/def456", + "awsAccountId": "620187402834", + "status": "accepted", + "sharedDate": "2024-07-15T00:00:00.000+00:00" + } + ] + } + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + `{ + "taskId": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "commandType": "tgwInvitationsGetRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T09:26:49.847808891Z", + "response": { + "resourceId": 114019, + "resource": { + "resources": [ + { + "id": 1, + "name": "tf-test-invitation-1", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/abc123", + "awsAccountId": "620187402834", + "status": "pending", + "sharedDate": "2024-07-16T00:00:00.000+00:00" + }, + { + "id": 2, + "name": "tf-test-invitation-2", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/def456", + "awsAccountId": "620187402834", + "status": "accepted", + "sharedDate": "2024-07-15T00:00:00.000+00:00" + } + ] + } + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + actual, err := subject.TransitGatewayAttachments.ListInvitations(context.TODO(), 114019) + require.NoError(t, err) + + assert.Equal(t, []*attachments.TransitGatewayInvitation{ + { + Id: redis.Int(1), + Name: redis.String("tf-test-invitation-1"), + ResourceShareUid: redis.String("arn:aws:ram:us-east-1:620187402834:resource-share/abc123"), + AwsAccountId: redis.String("620187402834"), + Status: redis.String("pending"), + SharedDate: redis.String("2024-07-16T00:00:00.000+00:00"), + }, + { + Id: redis.Int(2), + Name: redis.String("tf-test-invitation-2"), + ResourceShareUid: redis.String("arn:aws:ram:us-east-1:620187402834:resource-share/def456"), + AwsAccountId: redis.String("620187402834"), + Status: redis.String("accepted"), + SharedDate: redis.String("2024-07-15T00:00:00.000+00:00"), + }, + }, actual) +} + +func TestListInvitationsActiveActive(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + getRequest( + t, + "/subscriptions/114019/regions/1/transitGateways/invitations", + `{ + "taskId": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "commandType": "tgwInvitationsGetRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T09:26:40.929904847Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + `{ + "taskId": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "commandType": "tgwInvitationsGetRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T09:26:49.847808891Z", + "response": { + "resourceId": 114019, + "resource": { + "resources": [ + { + "id": 3, + "name": "tf-test-invitation-3", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/ghi789", + "awsAccountId": "620187402834", + "status": "pending", + "sharedDate": "2024-07-16T00:00:00.000+00:00" + } + ] + } + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + `{ + "taskId": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "commandType": "tgwInvitationsGetRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T09:26:49.847808891Z", + "response": { + "resourceId": 114019, + "resource": { + "resources": [ + { + "id": 3, + "name": "tf-test-invitation-3", + "resourceShareUid": "arn:aws:ram:us-east-1:620187402834:resource-share/ghi789", + "awsAccountId": "620187402834", + "status": "pending", + "sharedDate": "2024-07-16T00:00:00.000+00:00" + } + ] + } + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + actual, err := subject.TransitGatewayAttachments.ListInvitationsActiveActive(context.TODO(), 114019, 1) + require.NoError(t, err) + + assert.Equal(t, []*attachments.TransitGatewayInvitation{ + { + Id: redis.Int(3), + Name: redis.String("tf-test-invitation-3"), + ResourceShareUid: redis.String("arn:aws:ram:us-east-1:620187402834:resource-share/ghi789"), + AwsAccountId: redis.String("620187402834"), + Status: redis.String("pending"), + SharedDate: redis.String("2024-07-16T00:00:00.000+00:00"), + }, + }, actual) +} + +func TestAcceptInvitation(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + putRequestWithNoRequest( + t, + "/subscriptions/114019/transitGateways/invitations/1/accept", + `{ + "taskId": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "commandType": "tgwAcceptInvitationRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T10:00:00.000000000Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + `{ + "taskId": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "commandType": "tgwAcceptInvitationRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T10:00:10.000000000Z", + "response": { + "resourceId": 1 + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + err = subject.TransitGatewayAttachments.AcceptInvitation(context.TODO(), 114019, 1) + require.NoError(t, err) +} + +func TestAcceptInvitationActiveActive(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + putRequestWithNoRequest( + t, + "/subscriptions/114019/regions/1/transitGateways/invitations/3/accept", + `{ + "taskId": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a", + "commandType": "tgwAcceptInvitationRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T10:00:00.000000000Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a", + `{ + "taskId": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a", + "commandType": "tgwAcceptInvitationRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T10:00:10.000000000Z", + "response": { + "resourceId": 3 + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + err = subject.TransitGatewayAttachments.AcceptInvitationActiveActive(context.TODO(), 114019, 1, 3) + require.NoError(t, err) +} + +func TestRejectInvitation(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + putRequestWithNoRequest( + t, + "/subscriptions/114019/transitGateways/invitations/2/reject", + `{ + "taskId": "e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b", + "commandType": "tgwRejectInvitationRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T10:05:00.000000000Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b", + `{ + "taskId": "e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b", + "commandType": "tgwRejectInvitationRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T10:05:10.000000000Z", + "response": { + "resourceId": 2 + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + err = subject.TransitGatewayAttachments.RejectInvitation(context.TODO(), 114019, 2) + require.NoError(t, err) +} + +func TestRejectInvitationActiveActive(t *testing.T) { + server := httptest.NewServer( + testServer( + "key", + "secret", + putRequestWithNoRequest( + t, + "/subscriptions/114019/regions/1/transitGateways/invitations/3/reject", + `{ + "taskId": "f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c", + "commandType": "tgwRejectInvitationRequest", + "status": "received", + "description": "Task request received and is being queued for processing.", + "timestamp": "2024-07-16T10:05:00.000000000Z", + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c", + "rel": "task", + "type": "GET" + } + ] + }`, + ), + getRequest( + t, + "/tasks/f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c", + `{ + "taskId": "f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c", + "commandType": "tgwRejectInvitationRequest", + "status": "processing-completed", + "description": "Request processing completed successfully and its resources are now being provisioned / de-provisioned.", + "timestamp": "2024-07-16T10:05:10.000000000Z", + "response": { + "resourceId": 3 + }, + "links": [ + { + "href": "https://api-staging.qa.redislabs.com/v1/tasks/f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c", + "rel": "self", + "type": "GET" + } + ] + }`, + ), + )) + + subject, err := clientFromTestServer(server, "key", "secret") + require.NoError(t, err) + + err = subject.TransitGatewayAttachments.RejectInvitationActiveActive(context.TODO(), 114019, 1, 3) + require.NoError(t, err) +} diff --git a/util_test.go b/util_test.go index 817a761..639fead 100644 --- a/util_test.go +++ b/util_test.go @@ -186,6 +186,16 @@ func putRequest(t *testing.T, path string, request string, body string) endpoint } } +func putRequestWithNoRequest(t *testing.T, path string, body string) endpointRequest { + return endpointRequest{ + method: http.MethodPut, + path: path, + body: body, + status: http.StatusOK, + t: t, + } +} + // taskFlow returns the two endpointRequests needed for a "POST/PUT/DELETE -> GET /tasks/{id}" flow func taskFlow(t *testing.T, method, path, requestBody, taskID, commandType string) []endpointRequest { now := time.Now().UTC().Format(time.RFC3339) // e.g. "2025-08-11T14:33:21Z"