diff --git a/account_members.go b/account_members.go index 2a0e4c3b4c..1934064372 100644 --- a/account_members.go +++ b/account_members.go @@ -58,6 +58,14 @@ const errMissingMemberRolesOrPolicies = "account member must be created with rol var ErrMissingMemberRolesOrPolicies = errors.New(errMissingMemberRolesOrPolicies) +type CreateAccountMemberParams struct { + AccountId string + EmailAddress string + Roles []string + Policies []Policy + Status string +} + // AccountMembers returns all members of an account. // // API reference: https://api.cloudflare.com/#accounts-list-accounts @@ -103,55 +111,40 @@ func (api *API) CreateAccountMemberWithStatus(ctx context.Context, accountID str // upon member invitation. We recommend upgrading to CreateAccountMemberWithPolicies to use policies. // // API reference: https://api.cloudflare.com/#account-members-add-member -func (api *API) CreateAccountMember(ctx context.Context, accountID string, emailAddress string, roles []string) (AccountMember, error) { - invite := AccountMemberInvitation{ - Email: emailAddress, - Roles: roles, - Policies: nil, - Status: "", +func (api *API) CreateAccountMember(ctx context.Context, rc *ResourceContainer, params CreateAccountMemberParams) (AccountMember, error) { + if rc.Level != AccountRouteLevel { + return AccountMember{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level) } - return api.CreateAccountMemberInternal(ctx, accountID, invite) -} - -// CreateAccountMemberWithRoles is a terse wrapper around the CreateAccountMember method -// for clarity on what permissions you're granting an AccountMember. -// -// API reference: https://api.cloudflare.com/#account-members-add-member -func (api *API) CreateAccountMemberWithRoles(ctx context.Context, accountID string, emailAddress string, roles []string) (AccountMember, error) { - return api.CreateAccountMember(ctx, accountID, emailAddress, roles) -} -// CreateAccountMemberWithPolicies invites a new member to join your account with policies. -// Policies are the replacement to legacy "roles", which enables the newest feature Domain Scoped Roles. -// -// API documentation will be coming shortly. Blog post: https://blog.cloudflare.com/domain-scoped-roles-ga/ -func (api *API) CreateAccountMemberWithPolicies(ctx context.Context, accountID string, emailAddress string, policies []Policy) (AccountMember, error) { - invite := AccountMemberInvitation{ - Email: emailAddress, - Roles: nil, - Policies: policies, - Status: "", + if params.AccountId == "" { + if rc.Identifier == "" { + return AccountMember{}, ErrMissingAccountID + } else { + params.AccountId = rc.Identifier + } } - return api.CreateAccountMemberInternal(ctx, accountID, invite) -} -// CreateAccountMemberInternal allows you to provide a raw AccountMemberInvitation to be processed -// and contains the logic for other CreateAccountMember* methods. -func (api *API) CreateAccountMemberInternal(ctx context.Context, accountID string, invite AccountMemberInvitation) (AccountMember, error) { - if accountID == "" { - return AccountMember{}, ErrMissingAccountID + invite := AccountMemberInvitation{ + Email: params.EmailAddress, + Status: params.Status, } roles := []AccountRole{} - for i := 0; i < len(invite.Roles); i++ { - roles = append(roles, AccountRole{ID: invite.Roles[i]}) + for i := 0; i < len(params.Roles); i++ { + roles = append(roles, AccountRole{ID: params.Roles[i]}) } - err := validateRolesAndPolicies(roles, invite.Policies) + err := validateRolesAndPolicies(roles, params.Policies) if err != nil { return AccountMember{}, err } - uri := fmt.Sprintf("/accounts/%s/members", accountID) + if params.Roles != nil { + invite.Roles = params.Roles + } else if params.Policies != nil { + invite.Policies = params.Policies + } + + uri := fmt.Sprintf("/accounts/%s/members", rc.Identifier) res, err := api.makeRequestContext(ctx, http.MethodPost, uri, invite) if err != nil { return AccountMember{}, err @@ -166,6 +159,30 @@ func (api *API) CreateAccountMemberInternal(ctx context.Context, accountID strin return accountMemberListResponse.Result, nil } +// CreateAccountMemberWithRoles is a terse wrapper around the CreateAccountMember method +// for clarity on what permissions you're granting an AccountMember. +// +// API reference: https://api.cloudflare.com/#account-members-add-member +func (api *API) CreateAccountMemberWithRoles(ctx context.Context, rc *ResourceContainer, accountID string, emailAddress string, roles []string) (AccountMember, error) { + return api.CreateAccountMember(ctx, rc, CreateAccountMemberParams{ + AccountId: accountID, + EmailAddress: emailAddress, + Roles: roles, + }) +} + +// CreateAccountMemberWithPolicies invites a new member to join your account with policies. +// Policies are the replacement to legacy "roles", which enables the newest feature Domain Scoped Roles. +// +// API documentation will be coming shortly. Blog post: https://blog.cloudflare.com/domain-scoped-roles-ga/ +func (api *API) CreateAccountMemberWithPolicies(ctx context.Context, rc *ResourceContainer, accountID string, emailAddress string, policies []Policy) (AccountMember, error) { + return api.CreateAccountMember(ctx, rc, CreateAccountMemberParams{ + AccountId: accountID, + EmailAddress: emailAddress, + Policies: policies, + }) +} + // DeleteAccountMember removes a member from an account. // // API reference: https://api.cloudflare.com/#account-members-remove-member diff --git a/account_members_test.go b/account_members_test.go index 88b3a60bcf..65eb61a44e 100644 --- a/account_members_test.go +++ b/account_members_test.go @@ -252,8 +252,12 @@ func TestCreateAccountMemberWithStatus(t *testing.T) { } mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/members", handler) + accountResource := &ResourceContainer{ + Level: AccountRouteLevel, + Identifier: "01a7362d577a6c3019a474fd6f485823", + } - actual, err := client.CreateAccountMemberWithStatus(context.Background(), "01a7362d577a6c3019a474fd6f485823", "user@example.com", []string{"3536bcfad5faccb999b47003c79917fb"}, "accepted") + actual, err := client.CreateAccountMemberWithStatus(context.Background(), accountResource, "01a7362d577a6c3019a474fd6f485823", "user@example.com", []string{"3536bcfad5faccb999b47003c79917fb"}, "accepted") if assert.NoError(t, err) { assert.Equal(t, expectedNewAccountMemberAcceptedStruct, actual) @@ -303,8 +307,17 @@ func TestCreateAccountMember(t *testing.T) { } mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/members", handler) + accountResource := &ResourceContainer{ + Level: AccountRouteLevel, + Identifier: "01a7362d577a6c3019a474fd6f485823", + } + createAccountParams := CreateAccountMemberParams{ + AccountId: "01a7362d577a6c3019a474fd6f485823", + EmailAddress: "user@example.com", + Roles: []string{"3536bcfad5faccb999b47003c79917fb"}, + } - actual, err := client.CreateAccountMember(context.Background(), "01a7362d577a6c3019a474fd6f485823", "user@example.com", []string{"3536bcfad5faccb999b47003c79917fb"}) + actual, err := client.CreateAccountMember(context.Background(), accountResource, createAccountParams) if assert.NoError(t, err) { assert.Equal(t, expectedNewAccountMemberStruct, actual) @@ -361,8 +374,12 @@ func TestCreateAccountMemberWithPolicies(t *testing.T) { } mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/members", handler) + accountResource := &ResourceContainer{ + Level: AccountRouteLevel, + Identifier: "01a7362d577a6c3019a474fd6f485823", + } - actual, err := client.CreateAccountMemberWithPolicies(context.Background(), "01a7362d577a6c3019a474fd6f485823", "user@example.com", []Policy{mockPolicy}) + actual, err := client.CreateAccountMemberWithPolicies(context.Background(), accountResource, "01a7362d577a6c3019a474fd6f485823", "user@example.com", []Policy{mockPolicy}) if assert.NoError(t, err) { assert.Equal(t, expectedNewAccountMemberWithPoliciesStruct, actual) @@ -373,8 +390,12 @@ func TestCreateAccountMemberWithRolesAndPoliciesErr(t *testing.T) { setup() defer teardown() - _, err := client.CreateAccountMemberInternal(context.Background(), "fake", AccountMemberInvitation{ - Email: "user@example.com", + accountResource := &ResourceContainer{ + Level: AccountRouteLevel, + Identifier: "01a7362d577a6c3019a474fd6f485823", + } + _, err := client.CreateAccountMember(context.Background(), accountResource, CreateAccountMemberParams{ + EmailAddress: "user@example.com", Roles: []string{"fake-role-id"}, Policies: []Policy{mockPolicy}, Status: "active", @@ -389,7 +410,15 @@ func TestCreateAccountMemberWithoutAccountID(t *testing.T) { setup() defer teardown() - _, err := client.CreateAccountMember(context.Background(), "", "user@example.com", []string{"3536bcfad5faccb999b47003c79917fb"}) + accountResource := &ResourceContainer{ + Level: AccountRouteLevel, + Identifier: "", + } + _, err := client.CreateAccountMember(context.Background(), accountResource, CreateAccountMemberParams{ + EmailAddress: "user@example.com", + Roles: []string{"fake-role-id"}, + Status: "active", + }) if assert.Error(t, err) { assert.Equal(t, err.Error(), errMissingAccountID)