Skip to content

Commit a86ec45

Browse files
Merge pull request #513 from linode/proj/parent-child
new: Add support for Parent/Child account switching
2 parents e7939e8 + d9e8d01 commit a86ec45

23 files changed

+970
-652
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ run_fixtures:
7777
LINODE_API_VERSION="v4beta" \
7878
LINODE_URL="$(LINODE_URL)" \
7979
GO111MODULE="on" \
80-
go test -timeout=$(TEST_TIMEOUT) -v $(ARGS)
80+
go test --tags $(TEST_TAGS) -timeout=$(TEST_TIMEOUT) -v $(ARGS)
8181

8282
sanitize:
8383
@echo "* Sanitizing fixtures"

account.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package linodego
22

3-
import "context"
3+
import (
4+
"context"
5+
"encoding/json"
6+
"time"
7+
8+
"github.com/linode/linodego/internal/parseabletime"
9+
)
410

511
// Account associated with the token in use.
612
type Account struct {
@@ -20,6 +26,29 @@ type Account struct {
2026
Phone string `json:"phone"`
2127
CreditCard *CreditCard `json:"credit_card"`
2228
EUUID string `json:"euuid"`
29+
BillingSource string `json:"billing_source"`
30+
Capabilities []string `json:"capabilities"`
31+
ActiveSince *time.Time `json:"-"`
32+
}
33+
34+
// UnmarshalJSON implements the json.Unmarshaler interface
35+
func (account *Account) UnmarshalJSON(b []byte) error {
36+
type Mask Account
37+
38+
p := struct {
39+
*Mask
40+
ActiveSince *parseabletime.ParseableTime `json:"active_since"`
41+
}{
42+
Mask: (*Mask)(account),
43+
}
44+
45+
if err := json.Unmarshal(b, &p); err != nil {
46+
return err
47+
}
48+
49+
account.ActiveSince = (*time.Time)(p.ActiveSince)
50+
51+
return nil
2352
}
2453

2554
// CreditCard information associated with the Account.

account_child.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package linodego
2+
3+
import (
4+
"context"
5+
)
6+
7+
// ChildAccount represents an account under the current account.
8+
// NOTE: This is an alias to prevent any future breaking changes.
9+
type ChildAccount = Account
10+
11+
// ChildAccountToken represents a short-lived token created using
12+
// the CreateChildAccountToken(...) function.
13+
// NOTE: This is an alias to prevent any future breaking changes.
14+
type ChildAccountToken = Token
15+
16+
// ListChildAccounts lists child accounts under the current account.
17+
func (c *Client) ListChildAccounts(ctx context.Context, opts *ListOptions) ([]ChildAccount, error) {
18+
return getPaginatedResults[ChildAccount](
19+
ctx,
20+
c,
21+
"account/child-accounts",
22+
opts,
23+
)
24+
}
25+
26+
// GetChildAccount gets a single child accounts under the current account.
27+
func (c *Client) GetChildAccount(ctx context.Context, euuid string) (*ChildAccount, error) {
28+
return doGETRequest[ChildAccount](
29+
ctx,
30+
c,
31+
formatAPIPath("account/child-accounts/%s", euuid),
32+
)
33+
}
34+
35+
// CreateChildAccountToken creates a short-lived token that can be used to
36+
// access the Linode API under a child account.
37+
// The attributes of this token are not currently configurable.
38+
func (c *Client) CreateChildAccountToken(ctx context.Context, euuid string) (*ChildAccountToken, error) {
39+
return doPOSTRequest[ChildAccountToken, any](
40+
ctx,
41+
c,
42+
formatAPIPath("account/child-accounts/%s/token", euuid),
43+
)
44+
}

account_users.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,20 @@ import (
1111
"github.com/linode/linodego/internal/parseabletime"
1212
)
1313

14+
type UserType string
15+
16+
const (
17+
UserTypeProxy UserType = "proxy"
18+
UserTypeParent UserType = "parent"
19+
UserTypeChild UserType = "child"
20+
UserTypeDefault UserType = "default"
21+
)
22+
1423
// User represents a User object
1524
type User struct {
1625
Username string `json:"username"`
1726
Email string `json:"email"`
27+
UserType UserType `json:"user_type"`
1828
Restricted bool `json:"restricted"`
1929
TFAEnabled bool `json:"tfa_enabled"`
2030
SSHKeys []string `json:"ssh_keys"`

go.work.sum

Lines changed: 3 additions & 277 deletions
Large diffs are not rendered by default.

test/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ smoketest:
1919
LINODE_TOKEN="awesometokenawesometokenawesometoken" \
2020
LINODE_API_VERSION="v4beta" \
2121
GO111MODULE="on" \
22-
go test -v -run smoke ./integration/...
22+
go test -v -run smoke ./integration/...
23+
24+
25+
.PHONY: unit-test
26+
unit-test:
27+
go test -v ./unit $(ARGS)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//go:build parent_child
2+
3+
package integration
4+
5+
import (
6+
"context"
7+
"reflect"
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
// NOTE: These fixtures are expected to be run under a parent account.
15+
func TestAccountChild_basic(t *testing.T) {
16+
client, teardown := createTestClient(t, "fixtures/TestAccountChild_basic")
17+
defer teardown()
18+
19+
childAccounts, err := client.ListChildAccounts(context.Background(), nil)
20+
require.NoError(t, err)
21+
require.Greater(
22+
t,
23+
len(childAccounts),
24+
0,
25+
"number of child accounts should be > 0",
26+
)
27+
28+
childAccount, err := client.GetChildAccount(context.Background(), childAccounts[0].EUUID)
29+
require.NoError(t, err)
30+
require.True(
31+
t,
32+
reflect.DeepEqual(*childAccount, childAccounts[0]),
33+
"child accounts should be equal",
34+
cmp.Diff(*childAccount, childAccounts[0]),
35+
)
36+
37+
token, err := client.CreateChildAccountToken(context.Background(), childAccount.EUUID)
38+
require.NoError(t, err)
39+
require.Greater(t, len(token.Token), 0)
40+
}

test/integration/account_users_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ func TestUser_Get_smoke(t *testing.T) {
6767
if user.VerifiedPhoneNumber != nil {
6868
t.Error("expected phone number is not set")
6969
}
70+
if user.UserType == "" {
71+
t.Errorf("expected user type, got none")
72+
}
7073
}
7174

7275
func TestUser_Update(t *testing.T) {
@@ -150,6 +153,9 @@ func TestUsers_List(t *testing.T) {
150153
if newUser.VerifiedPhoneNumber != nil {
151154
t.Error("expected phone number is not set")
152155
}
156+
if newUser.UserType == "" {
157+
t.Errorf("expected user type, got none")
158+
}
153159
}
154160

155161
func createUser(t *testing.T, client *linodego.Client, userModifiers ...userModifier) (*User, func()) {

test/integration/fixtures/ExampleGetAccount.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ interactions:
1414
url: https://api.linode.com/v4beta/account
1515
method: GET
1616
response:
17-
body: '{"company": "Linode", "email": "lgarber@linode.com", "first_name": "Lena",
18-
"last_name": "Garber", "address_1": "2228 Lenox Ridge Ct NE", "address_2": "NA",
19-
"city": "Atlanta", "state": "GA", "zip": "30319", "country": "US", "phone":
20-
"6787613864", "balance": 0.0, "tax_id": "", "billing_source": "linode", "credit_card":
21-
{"last_four": "1488", "expiry": "02/2022"}, "balance_uninvoiced": 0.0, "active_since":
17+
body: '{"company": "Linode", "email": "foo@linode.com", "first_name": "foo",
18+
"last_name": "bar", "address_1": "123 Street Street", "address_2": "NA",
19+
"city": "Philadelphia", "state": "PA", "zip": "30000", "country": "US", "phone":
20+
"1234567891", "balance": 0.0, "tax_id": "", "billing_source": "linode", "credit_card":
21+
{"last_four": "1234", "expiry": "02/2020"}, "balance_uninvoiced": 0.0, "active_since":
2222
"2018-01-02T03:04:05", "capabilities": ["Linodes", "NodeBalancers", "Block Storage",
2323
"Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", "LKE HA Control Planes",
24-
"Machine Images", "Managed Databases"], "active_promotions": [], "euuid": "590F6313-2E4D-47CE-90943D2F724A87CB"}'
24+
"Machine Images", "Managed Databases"], "active_promotions": [], "euuid": "FFFFFFFF-2E4D-47CE-FFFFFFFFFFFFFFFFF"}'
2525
headers:
2626
Access-Control-Allow-Credentials:
2727
- "true"

0 commit comments

Comments
 (0)