Skip to content

Commit

Permalink
Added support for service transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
ezilber-akamai committed Nov 25, 2024
1 parent 675470b commit 433772d
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 0 deletions.
100 changes: 100 additions & 0 deletions account_service_transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package linodego

import (
"context"
"encoding/json"
"github.com/linode/linodego/internal/parseabletime"
"time"
)

// AccountServiceTransferStatus constants start with AccountServiceTransfer and
// include Linode API Account Service Transfer Status values.
type AccountServiceTransferStatus string

// AccountServiceTransferStatus constants reflect the current status of an AccountServiceTransfer
const (
AccountServiceTransferAccepted AccountServiceTransferStatus = "accepted"
AccountServiceTransferCanceled AccountServiceTransferStatus = "canceled"
AccountServiceTransferCompleted AccountServiceTransferStatus = "completed"
AccountServiceTransferFailed AccountServiceTransferStatus = "failed"
AccountServiceTransferPending AccountServiceTransferStatus = "pending"
AccountServiceTransferStale AccountServiceTransferStatus = "stale"
)

// AccountServiceTransfer represents a request to transfer a service on an Account
type AccountServiceTransfer struct {
Created *time.Time `json:"-"`
Entities AccountServiceTransferEntity `json:"entities"`
Expiry *time.Time `json:"-"`
IsSender bool `json:"is_sender"`
Status AccountServiceTransferStatus `json:"status"`
Token string `json:"token"`
Updated *time.Time `json:"-"`
}

// AccountServiceTransferEntity represents a collection of the services to include
// in a transfer request, separated by type.
// Note: At this time, only Linodes can be transferred.
type AccountServiceTransferEntity struct {
Linodes []int `json:"linodes"`
}

type AccountServiceTransferRequestOptions struct {
Entities AccountServiceTransferEntity `json:"entities"`
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (ast *AccountServiceTransfer) UnmarshalJSON(b []byte) error {
type Mask AccountServiceTransfer

p := struct {
*Mask
Created *parseabletime.ParseableTime `json:"created"`
Expiry *parseabletime.ParseableTime `json:"expiry"`
Updated *parseabletime.ParseableTime `json:"updated"`
}{
Mask: (*Mask)(ast),
}

if err := json.Unmarshal(b, &p); err != nil {
return err
}

ast.Created = (*time.Time)(p.Created)
ast.Expiry = (*time.Time)(p.Expiry)
ast.Updated = (*time.Time)(p.Updated)

return nil
}

// ListAccountServiceTransfer gets a paginated list of AccountServiceTransfer for the Account.
func (c *Client) ListAccountServiceTransfer(ctx context.Context, opts *ListOptions) ([]AccountServiceTransfer, error) {
e := "account/service-transfers"
return getPaginatedResults[AccountServiceTransfer](ctx, c, e, opts)
}

// GetAccountServiceTransfer gets the details of the AccountServiceTransfer for the provided token.
func (c *Client) GetAccountServiceTransfer(ctx context.Context, token string) (*AccountServiceTransfer, error) {
e := formatAPIPath("account/service-transfers/%s", token)
return doGETRequest[AccountServiceTransfer](ctx, c, e)
}

// RequestAccountServiceTransfer creates a transfer request for the specified services.
func (c *Client) RequestAccountServiceTransfer(ctx context.Context, opts AccountServiceTransferRequestOptions) (*AccountServiceTransfer, error) {
e := "account/service-transfers"
return doPOSTRequest[AccountServiceTransfer](ctx, c, e, opts)
}

// AcceptAccountServiceTransfer accepts an AccountServiceTransfer for the provided token to
// receive the services included in the transfer to the Account.
func (c *Client) AcceptAccountServiceTransfer(ctx context.Context, token string) error {
e := formatAPIPath("account/service-transfers/%s/accept", token)
_, err := doPOSTRequest[AccountServiceTransfer, any](ctx, c, e)
return err
}

// CancelAccountServiceTransfer cancels the AccountServiceTransfer for the provided token.
func (c *Client) CancelAccountServiceTransfer(ctx context.Context, token string) error {
e := formatAPIPath("account/service-transfers/%s", token)
return doDELETERequest(ctx, c, e)
}
111 changes: 111 additions & 0 deletions test/unit/account_service_transfer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package unit

import (
"context"
"github.com/jarcoal/httpmock"
"github.com/linode/linodego"
"github.com/stretchr/testify/assert"
"testing"
"time"
)

func TestAccountServiceTransfer_List(t *testing.T) {
fixtureData, err := fixtures.GetFixture("account_service_transfers_list")
assert.NoError(t, err)

var base ClientBaseCase
base.SetUp(t)
defer base.TearDown(t)

base.MockGet("account/service-transfers", fixtureData)

transfers, err := base.Client.ListAccountServiceTransfer(context.Background(), nil)
assert.NoError(t, err)

assert.Equal(t, 1, len(transfers))
ast := transfers[0]
assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Created)
assert.Equal(t, time.Time(time.Date(2021, time.February, 12, 16, 37, 3, 0, time.UTC)), *ast.Expiry)
assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Updated)
assert.Equal(t, 111, ast.Entities.Linodes[0])
assert.Equal(t, 222, ast.Entities.Linodes[1])
assert.Equal(t, true, ast.IsSender)
assert.Equal(t, linodego.AccountServiceTransferStatus("pending"), ast.Status)
assert.Equal(t, "123E4567-E89B-12D3-A456-426614174000", ast.Token)
}

func TestAccountServiceTransfer_Get(t *testing.T) {
fixtureData, err := fixtures.GetFixture("account_service_transfers_get")
assert.NoError(t, err)

var base ClientBaseCase
base.SetUp(t)
defer base.TearDown(t)

base.MockGet("account/service-transfers/123E4567-E89B-12D3-A456-426614174000", fixtureData)

ast, err := base.Client.GetAccountServiceTransfer(context.Background(), "123E4567-E89B-12D3-A456-426614174000")
assert.NoError(t, err)

assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Created)
assert.Equal(t, time.Time(time.Date(2021, time.February, 12, 16, 37, 3, 0, time.UTC)), *ast.Expiry)
assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Updated)
assert.Equal(t, 111, ast.Entities.Linodes[0])
assert.Equal(t, 222, ast.Entities.Linodes[1])
assert.Equal(t, true, ast.IsSender)
assert.Equal(t, linodego.AccountServiceTransferStatus("pending"), ast.Status)
assert.Equal(t, "123E4567-E89B-12D3-A456-426614174000", ast.Token)
}

func TestAccountServiceTransfer_Request(t *testing.T) {
fixtureData, err := fixtures.GetFixture("account_service_transfers_request")
assert.NoError(t, err)

var base ClientBaseCase
base.SetUp(t)
defer base.TearDown(t)

requestData := linodego.AccountServiceTransferRequestOptions{
Entities: linodego.AccountServiceTransferEntity{
Linodes: []int{111, 222},
},
}

base.MockPost("account/service-transfers", fixtureData)

ast, err := base.Client.RequestAccountServiceTransfer(context.Background(), requestData)
assert.NoError(t, err)

assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Created)
assert.Equal(t, time.Time(time.Date(2021, time.February, 12, 16, 37, 3, 0, time.UTC)), *ast.Expiry)
assert.Equal(t, time.Time(time.Date(2021, time.February, 11, 16, 37, 3, 0, time.UTC)), *ast.Updated)
assert.Equal(t, 111, ast.Entities.Linodes[0])
assert.Equal(t, 222, ast.Entities.Linodes[1])
assert.Equal(t, true, ast.IsSender)
assert.Equal(t, linodego.AccountServiceTransferStatus("pending"), ast.Status)
assert.Equal(t, "123E4567-E89B-12D3-A456-426614174000", ast.Token)
}

func TestAccountServiceTransfer_Accept(t *testing.T) {
client := createMockClient(t)

httpmock.RegisterRegexpResponder("POST",
mockRequestURL(t, "account/service-transfers/123E4567-E89B-12D3-A456-426614174000/accept"),
httpmock.NewStringResponder(200, "{}"))

if err := client.AcceptAccountServiceTransfer(context.Background(), "123E4567-E89B-12D3-A456-426614174000"); err != nil {
t.Fatal(err)
}
}

func TestAccountServiceTransfer_Cancel(t *testing.T) {
client := createMockClient(t)

httpmock.RegisterRegexpResponder("DELETE",
mockRequestURL(t, "account/service-transfers/123E4567-E89B-12D3-A456-426614174000"),
httpmock.NewStringResponder(200, "{}"))

if err := client.CancelAccountServiceTransfer(context.Background(), "123E4567-E89B-12D3-A456-426614174000"); err != nil {
t.Fatal(err)
}
}
14 changes: 14 additions & 0 deletions test/unit/fixtures/account_service_transfers_get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"created": "2021-02-11T16:37:03",
"entities": {
"linodes": [
111,
222
]
},
"expiry": "2021-02-12T16:37:03",
"is_sender": true,
"status": "pending",
"token": "123E4567-E89B-12D3-A456-426614174000",
"updated": "2021-02-11T16:37:03"
}
21 changes: 21 additions & 0 deletions test/unit/fixtures/account_service_transfers_list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"data": [
{
"created": "2021-02-11T16:37:03",
"entities": {
"linodes": [
111,
222
]
},
"expiry": "2021-02-12T16:37:03",
"is_sender": true,
"status": "pending",
"token": "123E4567-E89B-12D3-A456-426614174000",
"updated": "2021-02-11T16:37:03"
}
],
"page": 1,
"pages": 1,
"results": 1
}
14 changes: 14 additions & 0 deletions test/unit/fixtures/account_service_transfers_request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"created": "2021-02-11T16:37:03",
"entities": {
"linodes": [
111,
222
]
},
"expiry": "2021-02-12T16:37:03",
"is_sender": true,
"status": "pending",
"token": "123E4567-E89B-12D3-A456-426614174000",
"updated": "2021-02-11T16:37:03"
}

0 comments on commit 433772d

Please sign in to comment.