diff --git a/cmd/flags/update_test.go b/cmd/flags/update_test.go index deccc722..e5336ecf 100644 --- a/cmd/flags/update_test.go +++ b/cmd/flags/update_test.go @@ -40,7 +40,7 @@ func TestUpdate(t *testing.T) { "--projKey", "test-proj-key", } - output, err := cmd.CallCmd(t, &client, nil, args) + output, err := cmd.CallCmd(t, &client, nil, nil, args) require.NoError(t, err) assert.JSONEq(t, `{"valid": true}`, string(output)) @@ -60,7 +60,7 @@ func TestUpdate(t *testing.T) { "--projKey", "test-proj-key", } - _, err := cmd.CallCmd(t, &client, nil, args) + _, err := cmd.CallCmd(t, &client, nil, nil, args) require.EqualError(t, err, "An error") }) @@ -70,7 +70,7 @@ func TestUpdate(t *testing.T) { "flags", "update", } - _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, args) + _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) assert.EqualError(t, err, `required flag(s) "accessToken", "data", "key", "projKey" not set`) }) @@ -84,7 +84,7 @@ func TestUpdate(t *testing.T) { "--projKey", "test-proj-key", } - _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, args) + _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) assert.EqualError(t, err, "baseUri is invalid") }) diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 00000000..47c9b3bd --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,14 @@ +package client + +import ldapi "github.com/launchdarkly/api-client-go/v14" + +// New creates an LD API client. It's not set as a field on the struct because the CLI flags +// are evaluated when running the command, not when executing the program. That means we don't have +// the flag values until the command's RunE method is called. +func New(accessToken string, baseURI string) *ldapi.APIClient { + config := ldapi.NewConfiguration() + config.AddDefaultHeader("Authorization", accessToken) + config.Servers[0].URL = baseURI + + return ldapi.NewAPIClient(config) +} diff --git a/internal/flags/flags.go b/internal/flags/flags.go index b9f8587d..22a090ee 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -6,6 +6,7 @@ import ( ldapi "github.com/launchdarkly/api-client-go/v14" + "ldcli/internal/client" "ldcli/internal/errors" ) @@ -37,7 +38,7 @@ func (c FlagsClient) Create( key, projectKey string, ) ([]byte, error) { - client := c.client(accessToken, baseURI) + client := client.New(accessToken, baseURI) post := ldapi.NewFeatureFlagBody(name, key) flag, _, err := client.FeatureFlagsApi.PostFeatureFlag(ctx, projectKey).FeatureFlagBody(*post).Execute() if err != nil { @@ -61,7 +62,7 @@ func (c FlagsClient) Update( projKey string, patch []ldapi.PatchOperation, ) ([]byte, error) { - client := c.client(accessToken, baseURI) + client := client.New(accessToken, baseURI) flag, _, err := client.FeatureFlagsApi. PatchFeatureFlag(ctx, projKey, key). PatchWithComment(*ldapi.NewPatchWithComment(patch)). @@ -77,14 +78,3 @@ func (c FlagsClient) Update( return responseJSON, nil } - -// client creates an LD API client. It's not set as a field on the struct because the CLI flags -// are evaluated when running the command, not when executing the program. That means we don't have -// the flag values until the command's RunE method is called. -func (c FlagsClient) client(accessToken string, baseURI string) *ldapi.APIClient { - config := ldapi.NewConfiguration() - config.AddDefaultHeader("Authorization", accessToken) - config.Servers[0].URL = baseURI - - return ldapi.NewAPIClient(config) -} diff --git a/internal/members/members.go b/internal/members/members.go index 99c42754..e050dddf 100644 --- a/internal/members/members.go +++ b/internal/members/members.go @@ -6,6 +6,7 @@ import ( ldapi "github.com/launchdarkly/api-client-go/v14" + "ldcli/internal/client" "ldcli/internal/errors" ) @@ -20,7 +21,7 @@ func NewClient() Client { } func (c MembersClient) Create(ctx context.Context, accessToken, baseURI, email, role string) ([]byte, error) { - client := c.client(accessToken, baseURI) + client := client.New(accessToken, baseURI) memberForm := ldapi.NewMemberForm{Email: email, Role: &role} members, _, err := client.AccountMembersApi.PostMembers(ctx).NewMemberForm([]ldapi.NewMemberForm{memberForm}).Execute() if err != nil { @@ -33,11 +34,3 @@ func (c MembersClient) Create(ctx context.Context, accessToken, baseURI, email, return memberJson, nil } - -func (c MembersClient) client(accessToken string, baseURI string) *ldapi.APIClient { - config := ldapi.NewConfiguration() - config.AddDefaultHeader("Authorization", accessToken) - config.Servers[0].URL = baseURI - - return ldapi.NewAPIClient(config) -} diff --git a/internal/members/members_test.go b/internal/members/members_test.go deleted file mode 100644 index 67d266b7..00000000 --- a/internal/members/members_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package members_test - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "ldcli/internal/errors" -) - -type MockClient struct { - hasForbiddenErr bool - hasUnauthorizedErr bool -} - -func (c MockClient) Create(ctx context.Context, email string, role string) ([]byte, error) { - if c.hasForbiddenErr { - return nil, errors.NewError("You do not have permission to make this request") - } - if c.hasUnauthorizedErr { - return nil, errors.NewError("You are not authorized to make this request") - } - - return []byte(fmt.Sprintf(`{ - "_id":"000000000000000000000001", - "_lastSeen":0, - "_pendingInvite":true, - "_verified":true, - "creationDate":1711469912302, - "customRoles":[], - "email":%q, - "excludedDashboards":[], - "mfa":"disabled", - "role":%q - }`, - email, - role, - )), nil -} - -func TestCreateMember(t *testing.T) { - t.Run("return a new member", func(t *testing.T) { - expected := `{ - "_id":"000000000000000000000001", - "_lastSeen":0, - "_pendingInvite":true, - "_verified":true, - "creationDate":1711469912302, - "customRoles":[], - "email":"testemail@test.com", - "excludedDashboards":[], - "mfa":"disabled", - "role":"writer" - }` - - mockClient := MockClient{} - - response, err := mockClient.Create(context.Background(), "testemail@test.com", "writer") - - require.NoError(t, err) - assert.JSONEq(t, expected, string(response)) - }) - - t.Run("without access is forbidden", func(t *testing.T) { - mockClient := MockClient{ - hasForbiddenErr: true, - } - - _, err := mockClient.Create(context.Background(), "email@test.com", "reader") - - assert.EqualError(t, err, "You do not have permission to make this request") - }) - t.Run("with invalid accessToken is unauthorized", func(t *testing.T) { - mockClient := MockClient{ - hasUnauthorizedErr: true, - } - - _, err := mockClient.Create(context.Background(), "email@test.com", "reader") - - assert.EqualError(t, err, "You are not authorized to make this request") - }) -} diff --git a/internal/projects/projects.go b/internal/projects/projects.go index 2a611ef5..f0c3eb11 100644 --- a/internal/projects/projects.go +++ b/internal/projects/projects.go @@ -6,6 +6,7 @@ import ( ldapi "github.com/launchdarkly/api-client-go/v14" + "ldcli/internal/client" "ldcli/internal/errors" ) @@ -29,7 +30,7 @@ func (c ProjectsClient) Create( name, key string, ) ([]byte, error) { - client := c.client(accessToken, baseURI) + client := client.New(accessToken, baseURI) projectPost := ldapi.NewProjectPost(name, key) project, _, err := client.ProjectsApi.PostProject(ctx).ProjectPost(*projectPost).Execute() if err != nil { @@ -48,7 +49,7 @@ func (c ProjectsClient) List( accessToken, baseURI string, ) ([]byte, error) { - client := c.client(accessToken, baseURI) + client := client.New(accessToken, baseURI) projects, _, err := client.ProjectsApi. GetProjects(ctx). Limit(2). @@ -64,14 +65,3 @@ func (c ProjectsClient) List( return projectsJSON, nil } - -// client creates an LD API client. It's not set as a field on the struct because the CLI flags -// are evaluated when running the command, not when executing the program. That means we don't have -// the flag values until the command's RunE method is called. -func (c ProjectsClient) client(accessToken string, baseURI string) *ldapi.APIClient { - config := ldapi.NewConfiguration() - config.AddDefaultHeader("Authorization", accessToken) - config.Servers[0].URL = baseURI - - return ldapi.NewAPIClient(config) -} diff --git a/internal/projects/projects_test.go b/internal/projects/projects_test.go deleted file mode 100644 index 2d421f1b..00000000 --- a/internal/projects/projects_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package projects_test - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "ldcli/internal/errors" -) - -type GetProjectsResponse struct{} - -type MockClient struct { - hasForbiddenErr bool - hasUnauthorizedErr bool -} - -func (c MockClient) Create(ctx context.Context, name string, key string) ([]byte, error) { - return []byte(fmt.Sprintf(`{ - "_id": "000000000000000000000001", - "_links": null, - "environments": null, - "includeInSnippetByDefault": false, - "key": %q, - "name": %q, - "tags": null - }`, - key, - name, - )), nil -} - -func (c MockClient) List(ctx context.Context) ([]byte, error) { - if c.hasForbiddenErr { - return nil, errors.NewError("You do not have permission to make this request") - } - if c.hasUnauthorizedErr { - return nil, errors.NewError("You are not authorized to make this request") - } - - return []byte(`{ - "_links": { - "last": { - "href": "/api/v2/projects?expand=environments&limit=1&offset=1", - "type": "application/json" - }, - "next": { - "href": "/api/v2/projects?expand=environments&limit=1&offset=0", - "type": "application/json" - }, - "self": { - "href": "/api/v2/projects?expand=environments&limit=1", - "type": "application/json" - } - }, - "items": [ - { - "_id": "000000000000000000000001", - "_links": null, - "includeInSnippetByDefault": false, - "key": "test-project", - "name": "", - "tags": null - } - ], - "totalCount": 1 - }`), nil -} - -func TestCreateProject(t *testing.T) { - t.Run("return a new project", func(t *testing.T) { - expected := `{ - "_id": "000000000000000000000001", - "_links": null, - "environments": null, - "includeInSnippetByDefault": false, - "key": "test-key", - "name": "test-name", - "tags": null - }` - - mockClient := MockClient{} - - response, err := mockClient.Create(context.Background(), "test-name", "test-key") - - require.NoError(t, err) - assert.JSONEq(t, expected, string(response)) - }) -} - -func TestListProjects(t *testing.T) { - t.Run("returns a paginated list of projects", func(t *testing.T) { - expected := `{ - "_links": { - "last": { - "href": "/api/v2/projects?expand=environments&limit=1&offset=1", - "type": "application/json" - }, - "next": { - "href": "/api/v2/projects?expand=environments&limit=1&offset=0", - "type": "application/json" - }, - "self": { - "href": "/api/v2/projects?expand=environments&limit=1", - "type": "application/json" - } - }, - "items": [ - { - "_id": "000000000000000000000001", - "_links": null, - "includeInSnippetByDefault": false, - "key": "test-project", - "name": "", - "tags": null - } - ], - "totalCount": 1 - }` - mockClient := MockClient{} - - response, err := mockClient.List(context.Background()) - - require.NoError(t, err) - assert.JSONEq(t, expected, string(response)) - }) - - t.Run("without access is forbidden", func(t *testing.T) { - mockClient := MockClient{ - hasForbiddenErr: true, - } - - _, err := mockClient.List(context.Background()) - - assert.EqualError(t, err, "You do not have permission to make this request") - }) - - t.Run("with invalid accessToken is unauthorized", func(t *testing.T) { - mockClient := MockClient{ - hasUnauthorizedErr: true, - } - - _, err := mockClient.List(context.Background()) - - assert.EqualError(t, err, "You are not authorized to make this request") - }) -}