diff --git a/functions.go b/functions.go new file mode 100644 index 00000000..b3829367 --- /dev/null +++ b/functions.go @@ -0,0 +1,110 @@ +package godo + +import ( + "context" + "fmt" + "net/http" + "time" +) + +const ( + functionsBasePath = "/v2/functions/namespaces" + functionsNamespacePath = functionsBasePath + "/%s" +) + +type FunctionsService interface { + ListNamespaces(context.Context) ([]FunctionsNamespace, *Response, error) + GetNamespace(context.Context, string) (*FunctionsNamespace, *Response, error) + CreateNamespace(context.Context, *FunctionsNamespaceCreateRequest) (*FunctionsNamespace, *Response, error) + DeleteNamespace(context.Context, string) (*Response, error) +} + +type FunctionsServiceOp struct { + client *Client +} + +var _ FunctionsService = &FunctionsServiceOp{} + +type namespacesRoot struct { + Namespaces []FunctionsNamespace `json:"namespaces,omitempty"` +} + +type namespaceRoot struct { + Namespace *FunctionsNamespace `json:"namespace,omitempty"` +} + +type FunctionsNamespace struct { + ApiHost string `json:"api_host,omitempty"` + Namespace string `json:"namespace,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + Label string `json:"label,omitempty"` + Region string `json:"region,omitempty"` + UUID string `json:"uuid,omitempty"` + Key string `json:"key,omitempty"` +} + +type FunctionsNamespaceCreateRequest struct { + Label string `json:"label"` + Region string `json:"region"` +} + +// Gets a list of namespaces +func (s *FunctionsServiceOp) ListNamespaces(ctx context.Context) ([]FunctionsNamespace, *Response, error) { + req, err := s.client.NewRequest(ctx, http.MethodGet, functionsBasePath, nil) + if err != nil { + return nil, nil, err + } + nsRoot := new(namespacesRoot) + resp, err := s.client.Do(ctx, req, nsRoot) + if err != nil { + return nil, resp, err + } + return nsRoot.Namespaces, resp, nil +} + +// Gets a single namespace +func (s *FunctionsServiceOp) GetNamespace(ctx context.Context, namespace string) (*FunctionsNamespace, *Response, error) { + path := fmt.Sprintf(functionsNamespacePath, namespace) + + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + nsRoot := new(namespaceRoot) + resp, err := s.client.Do(ctx, req, nsRoot) + if err != nil { + return nil, resp, err + } + return nsRoot.Namespace, resp, nil +} + +// Creates a namespace +func (s *FunctionsServiceOp) CreateNamespace(ctx context.Context, opts *FunctionsNamespaceCreateRequest) (*FunctionsNamespace, *Response, error) { + req, err := s.client.NewRequest(ctx, http.MethodPost, functionsBasePath, opts) + if err != nil { + return nil, nil, err + } + nsRoot := new(namespaceRoot) + resp, err := s.client.Do(ctx, req, nsRoot) + if err != nil { + return nil, resp, err + } + return nsRoot.Namespace, resp, nil +} + +// Delete a namespace +func (s *FunctionsServiceOp) DeleteNamespace(ctx context.Context, namespace string) (*Response, error) { + path := fmt.Sprintf(functionsNamespacePath, namespace) + + 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 +} diff --git a/functions_test.go b/functions_test.go new file mode 100644 index 00000000..3662cb2d --- /dev/null +++ b/functions_test.go @@ -0,0 +1,157 @@ +package godo + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFunctions_ListNamespaces(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/v2/functions/namespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{ + "namespaces": [ + { + "api_host": "https://faas.do.com", + "namespace": "123-abc", + "created_at": "2022-06-16T12:09:13Z", + "updated_at": "2022-06-16T12:09:13Z", + "label": "my-namespace-1", + "region": "nyc1", + "uuid": "", + "key": "" + }, + { + "api_host": "https://faas.do.com", + "namespace": "456-abc", + "created_at": "2022-11-02T18:33:44Z", + "updated_at": "2022-11-02T18:33:44Z", + "label": "my-namespace-2", + "region": "nyc3", + "uuid": "", + "key": "" + } + ] + }`) + }) + + namespaces, _, err := client.Functions.ListNamespaces(ctx) + require.NoError(t, err) + + expectedNamespaces := []FunctionsNamespace{ + { + ApiHost: "https://faas.do.com", + Namespace: "123-abc", + CreatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + UpdatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + Label: "my-namespace-1", + Region: "nyc1", + UUID: "", + Key: "", + }, + { + ApiHost: "https://faas.do.com", + Namespace: "456-abc", + CreatedAt: time.Date(2022, 11, 2, 18, 33, 44, 0, time.UTC), + UpdatedAt: time.Date(2022, 11, 2, 18, 33, 44, 0, time.UTC), + Label: "my-namespace-2", + Region: "nyc3", + UUID: "", + Key: "", + }, + } + assert.Equal(t, expectedNamespaces, namespaces) +} + +func TestFunctions_GetNamespace(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/v2/functions/namespaces/123-abc", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{ + "namespace": { + "api_host": "https://faas.do.com", + "namespace": "123-abc", + "created_at": "2022-06-16T12:09:13Z", + "updated_at": "2022-06-16T12:09:13Z", + "label": "my-namespace-1", + "region": "nyc1", + "uuid": "123-456", + "key": "abc-123" + } + }`) + }) + + namespace, _, err := client.Functions.GetNamespace(ctx, "123-abc") + require.NoError(t, err) + + expectedNamespace := &FunctionsNamespace{ + ApiHost: "https://faas.do.com", + Namespace: "123-abc", + CreatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + UpdatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + Label: "my-namespace-1", + Region: "nyc1", + UUID: "123-456", + Key: "abc-123", + } + assert.Equal(t, expectedNamespace, namespace) +} + +func TestFunctions_CreateNamespace(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/v2/functions/namespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{ + "namespace": { + "api_host": "https://faas.do.com", + "namespace": "123-abc", + "created_at": "2022-06-16T12:09:13Z", + "updated_at": "2022-06-16T12:09:13Z", + "label": "my-namespace-1", + "region": "nyc1", + "uuid": "123-456", + "key": "abc-123" + } + }`) + }) + + opts := FunctionsNamespaceCreateRequest{Label: "my-namespace-1", Region: "nyc1"} + namespace, _, err := client.Functions.CreateNamespace(ctx, &opts) + require.NoError(t, err) + + expectedNamespace := &FunctionsNamespace{ + ApiHost: "https://faas.do.com", + Namespace: "123-abc", + CreatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + UpdatedAt: time.Date(2022, 6, 16, 12, 9, 13, 0, time.UTC), + Label: "my-namespace-1", + Region: "nyc1", + UUID: "123-456", + Key: "abc-123", + } + assert.Equal(t, expectedNamespace, namespace) +} + +func TestFunctions_DeleteNamespace(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/v2/functions/namespaces/123-abc", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + }) + + _, err := client.Functions.DeleteNamespace(ctx, "123-abc") + + assert.NoError(t, err) +} diff --git a/godo.go b/godo.go index f1b151d5..91853657 100644 --- a/godo.go +++ b/godo.go @@ -81,6 +81,7 @@ type Client struct { VPCs VPCsService OneClick OneClickService Monitoring MonitoringService + Functions FunctionsService // Optional function called after every successful request made to the DO APIs onRequestCompleted RequestCompletionCallback @@ -248,7 +249,7 @@ func NewClient(httpClient *http.Client) *Client { c.VPCs = &VPCsServiceOp{client: c} c.OneClick = &OneClickServiceOp{client: c} c.Monitoring = &MonitoringServiceOp{client: c} - + c.Functions = &FunctionsServiceOp{client: c} c.headers = make(map[string]string) return c