Skip to content

Commit

Permalink
Added support for missing Account-related endpoints (linode#598)
Browse files Browse the repository at this point in the history
* Added support for missing account endpoints

* Addressed PR comments

* Fix lint
  • Loading branch information
ezilber-akamai authored Nov 5, 2024
1 parent 551a48a commit b6c358f
Show file tree
Hide file tree
Showing 14 changed files with 492 additions and 9 deletions.
51 changes: 42 additions & 9 deletions account.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,56 @@ type Account struct {
ActiveSince *time.Time `json:"active_since"`
}

// AccountUpdateOptions fields are those accepted by UpdateAccount
type AccountUpdateOptions struct {
Address1 string `json:"address_1,omitempty"`
Address2 string `json:"address_2,omitempty"`
City string `json:"city,omitempty"`
Company string `json:"company,omitempty"`
Country string `json:"country,omitempty"`
Email string `json:"email,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Phone string `json:"phone,omitempty"`
State string `json:"state,omitempty"`
TaxID string `json:"tax_id,omitempty"`
Zip string `json:"zip,omitempty"`
}

// GetUpdateOptions converts an Account to AccountUpdateOptions for use in UpdateAccount
func (i Account) GetUpdateOptions() (o AccountUpdateOptions) {
o.Address1 = i.Address1
o.Address2 = i.Address2
o.City = i.City
o.Company = i.Company
o.Country = i.Country
o.Email = i.Email
o.FirstName = i.FirstName
o.LastName = i.LastName
o.Phone = i.Phone
o.State = i.State
o.TaxID = i.TaxID
o.Zip = i.Zip

return
}

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

p := struct {
*Mask
ActiveSince *parseabletime.ParseableTime `json:"active_since"`
}{
Mask: (*Mask)(account),
Mask: (*Mask)(i),
}

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

account.ActiveSince = (*time.Time)(p.ActiveSince)
i.ActiveSince = (*time.Time)(p.ActiveSince)

return nil
}
Expand All @@ -59,11 +93,10 @@ type CreditCard struct {

// GetAccount gets the contact and billing information related to the Account.
func (c *Client) GetAccount(ctx context.Context) (*Account, error) {
e := "account"
response, err := doGETRequest[Account](ctx, c, e)
if err != nil {
return nil, err
}
return doGETRequest[Account](ctx, c, "account")
}

return response, nil
// UpdateAccount updates the Account
func (c *Client) UpdateAccount(ctx context.Context, opts AccountUpdateOptions) (*Account, error) {
return doPUTRequest[Account](ctx, c, "account", opts)
}
37 changes: 37 additions & 0 deletions account_agreements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package linodego

import "context"

// AccountAgreements represents the agreements and their acceptance status for an Account
type AccountAgreements struct {
EUModel bool `json:"eu_model"`
MasterServiceAgreement bool `json:"master_service_agreement"`
PrivacyPolicy bool `json:"privacy_policy"`
}

// AccountAgreementsUpdateOptions fields are those accepted by UpdateAccountAgreements
type AccountAgreementsUpdateOptions struct {
EUModel bool `json:"eu_model,omitempty"`
MasterServiceAgreement bool `json:"master_service_agreement,omitempty"`
PrivacyPolicy bool `json:"privacy_policy,omitempty"`
}

// GetUpdateOptions converts an AccountAgreements to AccountAgreementsUpdateOptions for use in UpdateAccountAgreements
func (i AccountAgreements) GetUpdateOptions() (o AccountAgreementsUpdateOptions) {
o.EUModel = i.EUModel
o.MasterServiceAgreement = i.MasterServiceAgreement
o.PrivacyPolicy = i.PrivacyPolicy

return
}

// GetAccountAgreements gets all agreements and their acceptance status for the Account.
func (c *Client) GetAccountAgreements(ctx context.Context) (*AccountAgreements, error) {
return doGETRequest[AccountAgreements](ctx, c, "account/agreements")
}

// AcknowledgeAccountAgreements acknowledges account agreements for the Account
func (c *Client) AcknowledgeAccountAgreements(ctx context.Context, opts AccountAgreementsUpdateOptions) error {
_, err := doPOSTRequest[AccountAgreements](ctx, c, "account/agreements", opts)
return err
}
51 changes: 51 additions & 0 deletions account_maintenance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package linodego

import (
"context"
"encoding/json"
"time"

"github.com/linode/linodego/internal/parseabletime"
)

// AccountMaintenance represents a Maintenance object for any entity a user has permissions to view
type AccountMaintenance struct {
Entity *Entity `json:"entity"`
Reason string `json:"reason"`
Status string `json:"status"`
Type string `json:"type"`
When *time.Time `json:"when"`
}

// The entity being affected by maintenance
type Entity struct {
ID int `json:"id"`
Label string `json:"label"`
Type string `json:"type"`
URL string `json:"url"`
}

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

p := struct {
*Mask
When *parseabletime.ParseableTime `json:"when"`
}{
Mask: (*Mask)(accountMaintenance),
}

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

accountMaintenance.When = (*time.Time)(p.When)

return nil
}

// ListMaintenances lists Account Maintenance objects for any entity a user has permissions to view
func (c *Client) ListMaintenances(ctx context.Context, opts *ListOptions) ([]AccountMaintenance, error) {
return getPaginatedResults[AccountMaintenance](ctx, c, "account/maintenance", opts)
}
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
Expand Down
16 changes: 16 additions & 0 deletions test/integration/account_agreements_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package integration

import (
"context"
"testing"
)

func TestAccountAgreements_Get(t *testing.T) {
client, fixtureTeardown := createTestClient(t, "fixtures/TestAccountAgreements_List")
defer fixtureTeardown()

_, err := client.GetAccountAgreements(context.Background())
if err != nil {
t.Errorf("Error getting agreements, expected struct, got error %v", err)
}
}
19 changes: 19 additions & 0 deletions test/integration/account_maintenance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package integration

import (
"context"
"testing"

"github.com/linode/linodego"
)

func TestAccountMaintenances_List(t *testing.T) {
client, fixtureTeardown := createTestClient(t, "fixtures/TestAccountMaintenances_List")
defer fixtureTeardown()

listOpts := linodego.NewListOptions(0, "")
_, err := client.ListMaintenances(context.Background(), listOpts)
if err != nil {
t.Errorf("Error listing maintenances, expected array, got error %v", err)
}
}
66 changes: 66 additions & 0 deletions test/integration/fixtures/TestAccountAgreements_List.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
version: 1
interactions:
- request:
body: ""
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
User-Agent:
- linodego/dev https://github.com/linode/linodego
url: https://api.linode.com/v4beta/account/agreements
method: GET
response:
body: '{"privacy_policy": true, "eu_model": false, "master_service_agreement":
false}'
headers:
Access-Control-Allow-Credentials:
- "true"
Access-Control-Allow-Headers:
- Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter
Access-Control-Allow-Methods:
- HEAD, GET, OPTIONS, POST, PUT, DELETE
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Akamai-Internal-Account:
- '*'
Cache-Control:
- max-age=0, no-cache, no-store
Connection:
- keep-alive
Content-Length:
- "78"
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json
Expires:
- Wed, 30 Oct 2024 14:07:33 GMT
Pragma:
- no-cache
Strict-Transport-Security:
- max-age=31536000
Vary:
- Authorization, X-Filter
- Authorization, X-Filter
X-Accepted-Oauth-Scopes:
- account:read_only
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
- DENY
X-Oauth-Scopes:
- '*'
X-Ratelimit-Limit:
- "800"
X-Xss-Protection:
- 1; mode=block
status: 200 OK
code: 200
duration: ""
65 changes: 65 additions & 0 deletions test/integration/fixtures/TestAccountMaintenances_List.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
version: 1
interactions:
- request:
body: ""
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
User-Agent:
- linodego/dev https://github.com/linode/linodego
url: https://api.linode.com/v4beta/account/maintenance?page=1
method: GET
response:
body: '{"data": [], "page": 1, "pages": 1, "results": 0}'
headers:
Access-Control-Allow-Credentials:
- "true"
Access-Control-Allow-Headers:
- Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter
Access-Control-Allow-Methods:
- HEAD, GET, OPTIONS, POST, PUT, DELETE
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Akamai-Internal-Account:
- '*'
Cache-Control:
- max-age=0, no-cache, no-store
Connection:
- keep-alive
Content-Length:
- "49"
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json
Expires:
- Wed, 30 Oct 2024 14:06:55 GMT
Pragma:
- no-cache
Strict-Transport-Security:
- max-age=31536000
Vary:
- Authorization, X-Filter
- Authorization, X-Filter
X-Accepted-Oauth-Scopes:
- '*'
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
- DENY
X-Oauth-Scopes:
- unknown
X-Ratelimit-Limit:
- "800"
X-Xss-Protection:
- 1; mode=block
status: 200 OK
code: 200
duration: ""
44 changes: 44 additions & 0 deletions test/unit/account_agreements_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package unit

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

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

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

base.MockGet("account/agreements", fixtureData)

agreements, err := base.Client.GetAccountAgreements(context.Background())
assert.NoError(t, err)

assert.Equal(t, true, agreements.EUModel)
assert.Equal(t, true, agreements.PrivacyPolicy)
assert.Equal(t, true, agreements.MasterServiceAgreement)
}

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

requestData := linodego.AccountAgreementsUpdateOptions{
EUModel: true,
MasterServiceAgreement: true,
PrivacyPolicy: true,
}

httpmock.RegisterRegexpResponder("POST", mockRequestURL(t, "account/agreements"),
mockRequestBodyValidate(t, requestData, nil))

if err := client.AcknowledgeAccountAgreements(context.Background(), requestData); err != nil {
t.Fatal(err)
}
}
Loading

0 comments on commit b6c358f

Please sign in to comment.