From 28df764d5558cd4c8e63e5838ce7aed84c4252ce Mon Sep 17 00:00:00 2001 From: James O'Gorman Date: Wed, 29 Sep 2021 09:32:55 +0100 Subject: [PATCH] feat(account_members): Add CreateAccountMemberWithStatus The API call for adding members to an account optionally allows a status field, which can be used to immediately accept an invitation. The CreateAccountMemberWithStatus method has been added, replacing CreateAccountMember and taking the extra status parameter, and CreateAccountMember has been recreated as a wrapper for backwards compatibility. --- account_members.go | 24 ++++++++++---- account_members_test.go | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/account_members.go b/account_members.go index ce1a209f002..ba2c5544783 100644 --- a/account_members.go +++ b/account_members.go @@ -50,8 +50,9 @@ type AccountMemberDetailResponse struct { // AccountMemberInvitation represents the invitation for a new member to // the account. type AccountMemberInvitation struct { - Email string `json:"email"` - Roles []string `json:"roles"` + Email string `json:"email"` + Roles []string `json:"roles"` + Status string `json:"status,omitempty"` } // AccountMembers returns all members of an account. @@ -89,10 +90,12 @@ func (api *API) AccountMembers(ctx context.Context, accountID string, pageOpts P return accountMemberListresponse.Result, accountMemberListresponse.ResultInfo, nil } -// CreateAccountMember invites a new member to join an account. +// CreateAccountMemberWithStatus invites a new member to join an account, allowing setting the status. +// +// Refer to the API reference for valid statuses. // // 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) { +func (api *API) CreateAccountMemberWithStatus(ctx context.Context, accountID string, emailAddress string, roles []string, status string) (AccountMember, error) { if accountID == "" { return AccountMember{}, errors.New(errMissingAccountID) } @@ -100,8 +103,9 @@ func (api *API) CreateAccountMember(ctx context.Context, accountID string, email uri := fmt.Sprintf("/accounts/%s/members", accountID) var newMember = AccountMemberInvitation{ - Email: emailAddress, - Roles: roles, + Email: emailAddress, + Roles: roles, + Status: status, } res, err := api.makeRequestContext(ctx, http.MethodPost, uri, newMember) if err != nil { @@ -117,6 +121,14 @@ func (api *API) CreateAccountMember(ctx context.Context, accountID string, email return accountMemberListResponse.Result, nil } +// CreateAccountMember invites a new member to join an account. +// The member will be placed into "pending" status and receive an email confirmation. +// +// 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) { + return api.CreateAccountMemberWithStatus(ctx, accountID, emailAddress, roles, "") +} + // 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 95224052fb9..418b62284d3 100644 --- a/account_members_test.go +++ b/account_members_test.go @@ -54,6 +54,27 @@ var expectedNewAccountMemberStruct = AccountMember{ }, } +var expectedNewAccountMemberAcceptedStruct = AccountMember{ + ID: "4536bcfad5faccb111b47003c79917fa", + Code: "05dd05cce12bbed97c0d87cd78e89bc2fd41a6cee72f27f6fc84af2e45c0fac0", + User: AccountMemberUserDetails{ + Email: "user@example.com", + TwoFactorAuthenticationEnabled: false, + }, + Status: "accepted", + Roles: []AccountRole{ + { + ID: "3536bcfad5faccb999b47003c79917fb", + Name: "Account Administrator", + Description: "Administrative access to the entire Account", + Permissions: map[string]AccountRolePermission{ + "analytics": {Read: true, Edit: true}, + "billing": {Read: true, Edit: true}, + }, + }, + }, +} + var newUpdatedAccountMemberStruct = AccountMember{ ID: "4536bcfad5faccb111b47003c79917fa", Code: "05dd05cce12bbed97c0d87cd78e89bc2fd41a6cee72f27f6fc84af2e45c0fac0", @@ -151,6 +172,57 @@ func TestAccountMembersWithoutAccountID(t *testing.T) { } } +func TestCreateAccountMemberWithStatus(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "4536bcfad5faccb111b47003c79917fa", + "code": "05dd05cce12bbed97c0d87cd78e89bc2fd41a6cee72f27f6fc84af2e45c0fac0", + "user": { + "id": null, + "first_name": null, + "last_name": null, + "email": "user@example.com", + "two_factor_authentication_enabled": false + }, + "status": "accepted", + "roles": [{ + "id": "3536bcfad5faccb999b47003c79917fb", + "name": "Account Administrator", + "description": "Administrative access to the entire Account", + "permissions": { + "analytics": { + "read": true, + "edit": true + }, + "billing": { + "read": true, + "edit": true + } + } + }] + } + } + `) + } + + mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/members", handler) + + actual, err := client.CreateAccountMemberWithStatus(context.Background(), "01a7362d577a6c3019a474fd6f485823", "user@example.com", []string{"3536bcfad5faccb999b47003c79917fb"}, "accepted") + + if assert.NoError(t, err) { + assert.Equal(t, expectedNewAccountMemberAcceptedStruct, actual) + } +} + func TestCreateAccountMember(t *testing.T) { setup() defer teardown()