-
Notifications
You must be signed in to change notification settings - Fork 307
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package godo | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
) | ||
|
||
const spacesKeysBasePath = "v2/spaces/keys" | ||
|
||
// SpacesKeysService is an interface for managing Spaces keys with the DigitalOcean API. | ||
type SpacesKeysService interface { | ||
ListSpacesKeys(context.Context, *ListOptions) ([]*SpacesKey, *Response, error) | ||
UpdateSpacesKey(context.Context, string, *SpacesKeyUpdateRequest) (*SpacesKey, *Response, error) | ||
CreateSpacesKey(context.Context, *SpacesKeyCreateRequest) (*SpacesKey, *Response, error) | ||
DeleteSpacesKey(context.Context, string) (*Response, error) | ||
} | ||
|
||
// SpacesKeysServiceOp handles communication with the Spaces key related methods of the | ||
// DigitalOcean API. | ||
type SpacesKeysServiceOp struct { | ||
client *Client | ||
} | ||
|
||
var _ SpacesKeysService = &SpacesKeysServiceOp{} | ||
|
||
// Permission represents a permission for a Spaces grant | ||
type Permission string | ||
|
||
const ( | ||
// PermissionRead grants read-only access to the Spaces bucket | ||
PermissionRead Permission = "read" | ||
// PermissionReadWrite grants read and write access to the Spaces bucket | ||
PermissionReadWrite Permission = "readwrite" | ||
// PermissionFullAccess grants full access to the Spaces bucket | ||
PermissionFullAccess Permission = "fullaccess" | ||
) | ||
|
||
// Grant represents a Grant for a Spaces key | ||
type Grant struct { | ||
Bucket string `json:"bucket"` | ||
Permission Permission `json:"permission"` | ||
} | ||
|
||
// SpacesKey represents a DigitalOcean Spaces key | ||
type SpacesKey struct { | ||
Name string `json:"name"` | ||
AccessKey string `json:"access_key"` | ||
SecretKey string `json:"secret_key"` | ||
CreatedAt string `json:"created_at"` | ||
} | ||
|
||
// SpacesKeyRoot represents a response from the DigitalOcean API | ||
type spacesKeyRoot struct { | ||
Key *SpacesKey `json:"key"` | ||
} | ||
|
||
// SpacesKeyCreateRequest represents a request to create a Spaces key. | ||
type SpacesKeyCreateRequest struct { | ||
Name string `json:"name"` | ||
Grants []*Grant `json:"grants"` | ||
} | ||
|
||
// SpacesKeyUpdateRequest represents a request to update a Spaces key. | ||
type SpacesKeyUpdateRequest struct { | ||
Name string `json:"name"` | ||
Grants []*Grant `json:"grants"` | ||
} | ||
|
||
// spacesListKeysRoot represents a response from the DigitalOcean API | ||
type spacesListKeysRoot struct { | ||
Keys []*SpacesKey `json:"keys,omitempty"` | ||
Links *Links `json:"links,omitempty"` | ||
Meta *Meta `json:"meta"` | ||
} | ||
|
||
// CreateSpacesKey implements SpacesKeysService. | ||
func (s *SpacesKeysServiceOp) CreateSpacesKey(ctx context.Context, createRequest *SpacesKeyCreateRequest) (*SpacesKey, *Response, error) { | ||
if createRequest == nil { | ||
return nil, nil, NewArgError("createRequest", "cannot be nil") | ||
} | ||
|
||
req, err := s.client.NewRequest(ctx, http.MethodPost, spacesKeysBasePath, createRequest) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
root := new(spacesKeyRoot) | ||
resp, err := s.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return root.Key, resp, nil | ||
} | ||
|
||
// DeleteSpacesKey implements SpacesKeysService. | ||
func (s *SpacesKeysServiceOp) DeleteSpacesKey(ctx context.Context, accessKey string) (*Response, error) { | ||
if accessKey == "" { | ||
return nil, NewArgError("accessKey", "cannot be empty") | ||
} | ||
|
||
path := fmt.Sprintf("%s/%s", spacesKeysBasePath, accessKey) | ||
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
resp, err := s.client.Do(ctx, req, nil) | ||
if err != nil { | ||
return resp, err | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
// UpdateSpacesKey | ||
func (s *SpacesKeysServiceOp) UpdateSpacesKey(ctx context.Context, accessKey string, updateRequest *SpacesKeyUpdateRequest) (*SpacesKey, *Response, error) { | ||
if accessKey == "" { | ||
return nil, nil, NewArgError("accessKey", "cannot be empty") | ||
} | ||
if updateRequest == nil { | ||
return nil, nil, NewArgError("updateRequest", "cannot be nil") | ||
} | ||
|
||
path := fmt.Sprintf("%s/%s", spacesKeysBasePath, accessKey) | ||
req, err := s.client.NewRequest(ctx, http.MethodPut, path, updateRequest) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
root := new(spacesKeyRoot) | ||
resp, err := s.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return root.Key, resp, nil | ||
} | ||
|
||
// ListSpacesKeys returns a list of Spaces keys. | ||
func (s *SpacesKeysServiceOp) ListSpacesKeys(ctx context.Context, opts *ListOptions) ([]*SpacesKey, *Response, error) { | ||
path, err := addOptions(spacesKeysBasePath, opts) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
root := new(spacesListKeysRoot) | ||
resp, err := s.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
if root.Links != nil { | ||
resp.Links = root.Links | ||
} | ||
if root.Meta != nil { | ||
resp.Meta = root.Meta | ||
} | ||
|
||
return root.Keys, resp, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package godo | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestSpacesKeyCreate(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
createRequest := &SpacesKeyCreateRequest{ | ||
Name: "test-key", | ||
Grants: []*Grant{ | ||
{ | ||
Bucket: "test-bucket", | ||
Permission: PermissionRead, | ||
}, | ||
}, | ||
} | ||
|
||
mux.HandleFunc("/v2/spaces/keys", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, http.MethodPost, r.Method) | ||
w.WriteHeader(http.StatusCreated) | ||
fmt.Fprint(w, `{"key":{"name":"test-key","access_key":"test-access-key","secret_key":"test-secret-key","created_at":"2023-10-01T00:00:00Z"}}`) | ||
}) | ||
|
||
key, resp, err := client.SpacesKeys.CreateSpacesKey(context.Background(), createRequest) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
assert.Equal(t, "test-key", key.Name) | ||
assert.Equal(t, "test-access-key", key.AccessKey) | ||
assert.Equal(t, "test-secret-key", key.SecretKey) | ||
} | ||
|
||
func TestSpacesKeyDelete(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/spaces/keys/test-access-key", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, http.MethodDelete, r.Method) | ||
w.WriteHeader(http.StatusNoContent) | ||
}) | ||
|
||
resp, err := client.SpacesKeys.DeleteSpacesKey(context.Background(), "test-access-key") | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
} | ||
|
||
func TestSpacesKeyUpdate(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
updateRequest := &SpacesKeyUpdateRequest{ | ||
Name: "updated-key", | ||
Grants: []*Grant{ | ||
{ | ||
Bucket: "updated-bucket", | ||
Permission: PermissionReadWrite, | ||
}, | ||
}, | ||
} | ||
|
||
mux.HandleFunc("/v2/spaces/keys/test-access-key", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, http.MethodPut, r.Method) | ||
w.WriteHeader(http.StatusOK) | ||
fmt.Fprint(w, `{"key":{"name":"updated-key","access_key":"test-access-key","created_at":"2023-10-01T00:00:00Z"}}`) | ||
}) | ||
|
||
key, resp, err := client.SpacesKeys.UpdateSpacesKey(context.Background(), "test-access-key", updateRequest) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
assert.Equal(t, "updated-key", key.Name) | ||
assert.Equal(t, "test-access-key", key.AccessKey) | ||
} | ||
|
||
func TestSpacesKeyList(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/spaces/keys", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, http.MethodGet, r.Method) | ||
w.WriteHeader(http.StatusOK) | ||
fmt.Fprint(w, `{"keys":[{"name":"test-key","access_key":"test-access-key","created_at":"2023-10-01T00:00:00Z"}]}`) | ||
}) | ||
|
||
keys, resp, err := client.SpacesKeys.ListSpacesKeys(context.Background(), nil) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
assert.Len(t, keys, 1) | ||
assert.Equal(t, "test-key", keys[0].Name) | ||
assert.Equal(t, "test-access-key", keys[0].AccessKey) | ||
} | ||
|
||
func TestSpacesKeyList_Pagination(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/spaces/keys", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, http.MethodGet, r.Method) | ||
page := r.URL.Query().Get("page") | ||
if page == "2" { | ||
w.WriteHeader(http.StatusOK) | ||
fmt.Fprint(w, `{"keys":[{"name":"test-key-2","access_key":"test-access-key-2","created_at":"2023-10-02T00:00:00Z"}]}`) | ||
} else { | ||
w.WriteHeader(http.StatusOK) | ||
fmt.Fprint(w, `{"keys":[{"name":"test-key-1","access_key":"test-access-key-1","created_at":"2023-10-01T00:00:00Z"}]}`) | ||
} | ||
}) | ||
|
||
// Test first page | ||
keys, resp, err := client.SpacesKeys.ListSpacesKeys(context.Background(), &ListOptions{Page: 1}) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
assert.Len(t, keys, 1) | ||
assert.Equal(t, "test-key-1", keys[0].Name) | ||
assert.Equal(t, "test-access-key-1", keys[0].AccessKey) | ||
|
||
// Test second page | ||
keys, resp, err = client.SpacesKeys.ListSpacesKeys(context.Background(), &ListOptions{Page: 2}) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, resp) | ||
assert.Len(t, keys, 1) | ||
assert.Equal(t, "test-key-2", keys[0].Name) | ||
assert.Equal(t, "test-access-key-2", keys[0].AccessKey) | ||
} |