Skip to content

Commit

Permalink
feat(github-13): Implement environment, api-keys, project and identit…
Browse files Browse the repository at this point in the history
…y endpoints (#14)

* Add tests for project endpoint

* Add tests cases for api key

* Add tests for identities

* Add tests for environment

* Add tests for traits
  • Loading branch information
gagantrivedi authored Apr 25, 2024
1 parent cdb9d1f commit c29d85f
Show file tree
Hide file tree
Showing 11 changed files with 1,189 additions and 114 deletions.
57 changes: 57 additions & 0 deletions api_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package flagsmithapi

import (
"fmt"
)

func (c *Client) GetServerSideEnvKeys(environmentKey string) ([]ServerSideEnvKey, error) {
url := fmt.Sprintf("%s/environments/%s/api-keys/", c.baseURL, environmentKey)
keys := []ServerSideEnvKey{}
resp, err := c.client.R().SetResult(&keys).Get(url)
if err != nil {
return nil, err
}

if !resp.IsSuccess() {
return nil, fmt.Errorf("flagsmithapi: Error fetching server side keys: %v", resp)
}
return keys, nil
}
func (c *Client) CreateServerSideEnvKey(environmentKey string, key *ServerSideEnvKey) error {
url := fmt.Sprintf("%s/environments/%s/api-keys/", c.baseURL, environmentKey)

resp, err := c.client.R().SetBody(key).SetResult(&key).Post(url)
if err != nil {
return err
}
if !resp.IsSuccess() {
return fmt.Errorf("flagsmithapi: Error creating server side environment key: %s", resp)
}
return nil

}
func (c *Client) UpdateServerSideEnvKey(environmentKey string, key *ServerSideEnvKey) error {
url := fmt.Sprintf("%s/environments/%s/api-keys/%d/", c.baseURL, environmentKey, key.ID)
resp, err := c.client.R().SetBody(key).SetResult(&key).Put(url)
if err != nil {
return nil
}

if !resp.IsSuccess() {
return fmt.Errorf("flagsmithapi: Error updating server side environment key: %s", resp)
}
return nil
}
func (c *Client) DeleteServerSideEnvKey(environmentKey string, keyID int64) error {
url := fmt.Sprintf("%s/environments/%s/api-keys/%d/", c.baseURL, environmentKey, keyID)
resp, err := c.client.R().Delete(url)
if err != nil {
return err
}

if !resp.IsSuccess() {
return fmt.Errorf("flagsmithapi: Error deleting server side environment key: %s", resp)
}
return nil

}
193 changes: 193 additions & 0 deletions api_keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package flagsmithapi_test

import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"

flagsmithapi "github.com/Flagsmith/flagsmith-go-api-client"
)

const GetAPIKeysResponseJson = `
[
{
"id": 1,
"key": "ser.UiYoRr6zUjiFBUXaRwo7b5",
"active": true,
"created_at": "2022-02-16T12:09:30.349955Z",
"name": "key1",
"expires_at": null
},
{
"id": 2,
"key": "ser.g5N5Q4L8E832cA3iU4u4td",
"active": true,
"created_at": "2022-02-16T12:09:21.300028Z",
"name": "key2",
"expires_at": null
}
]
`
const KeyOneID int64 = 1
const KeyTwoID int64 = 2
const KeyOneName = "key1"
const KeyTwoName = "key2"
const KeyOneKey = "ser.UiYoRr6zUjiFBUXaRwo7b5"
const KeyTwoKey = "ser.g5N5Q4L8E832cA3iU4u4td"

const CreateAPIKeyResponseJson = `
{
"id": 1,
"key": "ser.UiYoRr6zUjiFBUXaRwo7b5",
"active": true,
"created_at": "2024-04-16T07:53:50.808415Z",
"name": "key1",
"expires_at": null
}
`

func TestGetServerSideEnvKeys(t *testing.T) {
// Given
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, fmt.Sprintf("/api/v1/environments/%s/api-keys/", EnvironmentAPIKey), req.URL.Path)
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))

rw.Header().Set("Content-Type", "application/json")
_, err := io.WriteString(rw, GetAPIKeysResponseJson)
assert.NoError(t, err)
}))
defer server.Close()

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
keys, err := client.GetServerSideEnvKeys(EnvironmentAPIKey)

// Then
assert.NoError(t, err)
assert.Len(t, keys, 2)

// Check the first key
assert.Equal(t, KeyOneID, (keys)[0].ID)
assert.Equal(t, KeyOneName, (keys)[0].Name)
assert.Equal(t, KeyOneKey, (keys)[0].Key)
assert.True(t, (keys)[0].Active)
assert.Equal(t, "2022-02-16T12:09:30.349955Z", (keys)[0].CreatedAt.Format(time.RFC3339Nano))
assert.Nil(t, (keys)[0].ExpiresAt)

// Check the second key
assert.Equal(t, KeyTwoID, (keys)[1].ID)
assert.Equal(t, KeyTwoName, (keys)[1].Name)
assert.Equal(t, KeyTwoKey, (keys)[1].Key)
assert.True(t, (keys)[1].Active)
assert.Equal(t, "2022-02-16T12:09:21.300028Z", (keys)[1].CreatedAt.Format(time.RFC3339Nano))
assert.Nil(t, (keys)[1].ExpiresAt)

}

func TestCreateServerSideEnvKey(t *testing.T) {
// Given
expectedRequestBody := `{"active":true,"name":"` + KeyOneName + `"}`

server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, fmt.Sprintf("/api/v1/environments/%s/api-keys/", EnvironmentAPIKey), req.URL.Path)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))

// Test that we sent the correct body
rawBody, err := io.ReadAll(req.Body)
assert.NoError(t, err)
assert.Equal(t, expectedRequestBody, string(rawBody))

rw.Header().Set("Content-Type", "application/json")
_, err = io.WriteString(rw, CreateAPIKeyResponseJson)
assert.NoError(t, err)
}))

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
key := flagsmithapi.ServerSideEnvKey{
Active: true,
Name: KeyOneName,
}
err := client.CreateServerSideEnvKey(EnvironmentAPIKey, &key)

// Then
assert.NoError(t, err)
assert.Equal(t, int64(KeyOneID), key.ID)
assert.Equal(t, true, key.Active)
assert.Equal(t, KeyOneName, key.Name)
}

func TestUpdateServerSideEnvKey(t *testing.T) {
// Given
expectedRequestBody := fmt.Sprintf(`{"id":%d,"active":false,"name":"%s"}`, KeyOneID, KeyOneName)

server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, fmt.Sprintf("/api/v1/environments/%s/api-keys/%d/", EnvironmentAPIKey, KeyOneID), req.URL.Path)
assert.Equal(t, "PUT", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))

// Test that we sent the correct body
rawBody, err := io.ReadAll(req.Body)
assert.NoError(t, err)
assert.Equal(t, expectedRequestBody, string(rawBody))

rw.Header().Set("Content-Type", "application/json")
_, err = io.WriteString(rw, fmt.Sprintf(`{"id":%d,"active":false,"name":"%s"}`, KeyOneID, KeyOneName))
assert.NoError(t, err)
}))

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
key := flagsmithapi.ServerSideEnvKey{
ID: KeyOneID,
Active: false,
Name: KeyOneName,
}
err := client.UpdateServerSideEnvKey(EnvironmentAPIKey, &key)

// Then
assert.NoError(t, err)
assert.Equal(t, int64(KeyOneID), key.ID)
assert.Equal(t, false, key.Active)
assert.Equal(t, KeyOneName, key.Name)
}

func TestDeleteServerSideEnvKey(t *testing.T) {
// Given
requestReceived := struct {
mu sync.Mutex
isRequestReceived bool
}{}

server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
requestReceived.mu.Lock()
requestReceived.isRequestReceived = true
requestReceived.mu.Unlock()

assert.Equal(t, fmt.Sprintf("/api/v1/environments/%s/api-keys/%d/", EnvironmentAPIKey, KeyOneID), req.URL.Path)
assert.Equal(t, "DELETE", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))
}))

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
err := client.DeleteServerSideEnvKey(EnvironmentAPIKey, KeyOneID)

// Then
requestReceived.mu.Lock()
assert.True(t, requestReceived.isRequestReceived)
requestReceived.mu.Unlock()
assert.NoError(t, err)
}
49 changes: 0 additions & 49 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,38 +111,6 @@ func (c *Client) UpdateFeatureState(featureState *FeatureState, updateSegmentPri
return nil
}

func (c *Client) GetProject(projectUUID string) (*Project, error) {
url := fmt.Sprintf("%s/projects/get-by-uuid/%s/", c.baseURL, projectUUID)
project := Project{}
resp, err := c.client.R().
SetResult(&project).
Get(url)

if err != nil {
return nil, err
}
if !resp.IsSuccess() {
return nil, fmt.Errorf("flagsmithapi: Error getting project: %s", resp)
}
return &project, nil

}
func (c *Client) GetProjectByID(projectID int64) (*Project, error) {
url := fmt.Sprintf("%s/projects/%d/", c.baseURL, projectID)
project := Project{}
resp, err := c.client.R().
SetResult(&project).
Get(url)

if err != nil {
return nil, err
}
if !resp.IsSuccess() {
return nil, fmt.Errorf("flagsmithapi: Error getting project: %s", resp)
}
return &project, nil

}
func (c *Client) GetFeature(featureUUID string) (*Feature, error) {
url := fmt.Sprintf("%s/features/get-by-uuid/%s/", c.baseURL, featureUUID)
feature := Feature{}
Expand Down Expand Up @@ -434,23 +402,6 @@ func (c *Client) UpdateSegment(segment *Segment) error {
return nil
}

func (c *Client) GetEnvironment(apiKey string) (*Environment, error) {
url := fmt.Sprintf("%s/environments/%s/", c.baseURL, apiKey)
environment := Environment{}
resp, err := c.client.R().
SetResult(&environment).Get(url)

if err != nil {
return nil, err
}

if !resp.IsSuccess() {
return nil, fmt.Errorf("flagsmithapi: Error getting environment: %s", resp)
}

return &environment, nil
}

func (c *Client) GetFeatureSegmentByID(featureSegmentID int64) (*FeatureSegment, error) {
url := fmt.Sprintf("%s/features/feature-segments/%d/", c.baseURL, featureSegmentID)
featureSegment := FeatureSegment{}
Expand Down
64 changes: 8 additions & 56 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,32 +239,8 @@ const GetProjectResponseJson = `
`
const ProjectID int64 = 10
const ProjectUUID = "cba035f8-d801-416f-a985-ce6e05acbe13"

func TestGetProject(t *testing.T) {
// Given
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, fmt.Sprintf("/api/v1/projects/get-by-uuid/%s/", ProjectUUID), req.URL.Path)
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))

rw.Header().Set("Content-Type", "application/json")
_, err := io.WriteString(rw, GetProjectResponseJson)
assert.NoError(t, err)
}))
defer server.Close()

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
project, err := client.GetProject(ProjectUUID)

// Then
assert.NoError(t, err)

assert.Equal(t, ProjectID, project.ID)
assert.Equal(t, ProjectUUID, project.UUID)
assert.Equal(t, "project-1", project.Name)
}
const ProjectName = "project-1"
const OrganisationID = 10

const CreateFeatureResponseJson = `
{
Expand Down Expand Up @@ -1307,38 +1283,14 @@ const EnvironmentJson = `{
"description": null,
"project": 10,
"minimum_change_request_approvals": 0,
"allow_client_traits": true
"allow_client_traits": true,
"banner_colour": null,
"banner_text": null,
"hide_disabled_flags": null,
"hide_sensitive_data": false,
"use_identity_composite_key_for_hashing": true
}`

func TestGetEnvironment(t *testing.T) {
// Given
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, fmt.Sprintf("/api/v1/environments/%s/", EnvironmentAPIKey), req.URL.Path)
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "Api-Key "+MasterAPIKey, req.Header.Get("Authorization"))

rw.Header().Set("Content-Type", "application/json")
_, err := io.WriteString(rw, EnvironmentJson)
assert.NoError(t, err)
}))
defer server.Close()

client := flagsmithapi.NewClient(MasterAPIKey, server.URL+"/api/v1")

// When
environment, err := client.GetEnvironment(EnvironmentAPIKey)

// Then
// assert that we did not receive an error
assert.NoError(t, err)

// assert that the environment is as expected
assert.Equal(t, EnvironmentID, environment.ID)
assert.Equal(t, "Development", environment.Name)
assert.Equal(t, EnvironmentAPIKey, environment.APIKey)
assert.Equal(t, ProjectID, environment.Project)
}

// 400 is arbitrarily chosen to avoid collision with other ids
const FeatureSegmentID = int64(400)
const GetFeatureSegmentJson = `{
Expand Down
Loading

0 comments on commit c29d85f

Please sign in to comment.