diff --git a/CHANGELOG.md b/CHANGELOG.md index be141a3..c7e4ebc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.3.0] - 2023-10-26 + +### Changed + +- The interface `Client` is removed and the output of the `NewClient` is the pointer to the struct now. It will facilitate stub of the SDK client. +- [**BREAKING**] `NewClient` requires `Config` to initialise a Neon client. It's meant to improve security by eliminating + support of environment variables for authentication by default. It also simplifies the codebase. + + **Example** + ```go + package main + + import ( + "log" + + neon "github.com/kislerdm/neon-sdk-go" + ) + + func main() { + client, err := neon.NewClient(neon.Config{Key: "{{.NeonApiKey}}"}) + if err != nil { + panic(err) + } + + v, err := client.ListProjects() + if err != nil { + panic(err) + } + + log.Printf("%d projects found", len(v.Projects)) + } + ``` + ## [v0.2.5] - 2023-10-22 The release incorporates the up-to-date [API contract](openAPIDefinition.json) as of 2023-10-11 00:08:16 GMT. diff --git a/README.md b/README.md index 7e0db6e..8b7b567 100644 --- a/README.md +++ b/README.md @@ -51,19 +51,9 @@ Where `{{.Ver}}` is the release version. ### Code Snippets -#### Authentication +### Default HTTP Client -Authentication with the Neon Platform is implemented -using [variadic functions](https://gobyexample.com/variadic-functions) and environment variables evaluation in the -following order: - -1. Variadic function client's argument; -2. Environment variable `NEON_API_KEY`. - -Note that if the API key is provided as the variadic function argument, key from the environment variable `NEON_API_KEY` -will be ignored. - -##### Variadic Function +The following snippet demonstrates how to initialize SDK which will use default HTTP client. ```go package main @@ -75,7 +65,7 @@ import ( ) func main() { - client, err := neon.NewClient(neon.WithAPIKey("{{.NeonApiKey}}")) + client, err := neon.NewClient(neon.Config{Key: "{{.NeonApiKey}}"}) if err != nil { panic(err) } @@ -89,22 +79,25 @@ func main() { } ``` -##### Environment Variables Evaluation +### Custom HTTP client -**_Requirement_**: a valid Neon [API key](https://neon.tech/docs/manage/api-keys/) must be exported as the environment -variable `NEON_API_KEY`. +The SDK can initialized with a custom HTTP client. ```go package main import ( + "net/http" "log" + "time" neon "github.com/kislerdm/neon-sdk-go" ) func main() { - client, err := neon.NewClient() + myHTTPClient := &http.Client{Timeout: 30 * time.Second} + + client, err := neon.NewClient(neon.Config{Key: "{{.NeonApiKey}}", HTTPClient: myHTTPClient}) if err != nil { panic(err) } @@ -132,7 +125,7 @@ import ( ) func main() { - client, err := neon.NewClient(neon.WithHTTPClient(neon.NewMockHTTPClient())) + client, err := neon.NewClient(neon.Config{HTTPClient: neon.NewMockHTTPClient()}) if err != nil { panic(err) } diff --git a/acc_test.go b/acc_test.go index c3a4ae9..d585e4e 100644 --- a/acc_test.go +++ b/acc_test.go @@ -5,6 +5,7 @@ package sdk_test import ( "fmt" + "os" "testing" "time" @@ -12,7 +13,7 @@ import ( ) func TestSmoke(t *testing.T) { - cl, err := sdk.NewClient() + cl, err := sdk.NewClient(sdk.Config{Key: os.Getenv("NEON_API_KEY")}) if err != nil { t.Fatalf("cannot initialise SDK: %v", err) } diff --git a/doc.go b/doc.go index 53d5ed9..c0f35d2 100644 --- a/doc.go +++ b/doc.go @@ -2,7 +2,7 @@ Find more about the service: https://neon.tech/docs/introduction. -Author: dkisler.com +Author: Dmitry Kisler */ diff --git a/error.go b/error.go new file mode 100644 index 0000000..233ca4e --- /dev/null +++ b/error.go @@ -0,0 +1,60 @@ +package sdk + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strconv" +) + +// Error API error. +type Error struct { + HTTPCode int + errorResp +} + +func (e Error) Error() string { + return "[HTTP Code: " + strconv.Itoa(e.HTTPCode) + "][Error Code: " + e.Code + "] " + e.Message +} + +func (e Error) httpResp() *http.Response { + o, _ := json.Marshal(e.errorResp) + return &http.Response{ + Status: e.Code, + StatusCode: e.HTTPCode, + Body: io.NopCloser(bytes.NewReader(o)), + ContentLength: int64(len(o)), + } +} + +type errorResp struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func convertErrorResponse(res *http.Response) error { + var v errorResp + buf, err := io.ReadAll(res.Body) + defer func() { _ = res.Body.Close() }() + if err != nil { + return Error{ + HTTPCode: res.StatusCode, + errorResp: errorResp{ + Message: "cannot read response bytes", + }, + } + } + if err := json.Unmarshal(buf, &v); err != nil { + return Error{ + HTTPCode: res.StatusCode, + errorResp: errorResp{ + Message: err.Error(), + }, + } + } + return Error{ + HTTPCode: res.StatusCode, + errorResp: v, + } +} diff --git a/generator/extractSpecs_internal_test.go b/generator/extractSpecs_internal_test.go deleted file mode 100644 index 46afd65..0000000 --- a/generator/extractSpecs_internal_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package generator - -import ( - "reflect" - "testing" - - "github.com/getkin/kin-openapi/openapi3" -) - -func Test_extractSpecs(t *testing.T) { - t.Parallel() - - parseSpecExample := func(s string) (openAPISpec, error) { - var spec openAPISpec - if err := spec.UnmarshalJSON([]byte(s)); err != nil { - return openAPISpec{}, err - } - return spec, nil - } - - enrichSpec := func(spec openAPISpec) openAPISpec { - spec.Info = &openapi3.Info{ - Title: "foo", - Description: "bar", - } - spec.Servers = openapi3.Servers{ - { - URL: "https://console.neon.tech/api/v2", - }, - } - return spec - } - - t.Run( - "listProjects", func(t *testing.T) { - // GIVEN - spec, err := parseSpecExample( - `{ - "openapi": "3.0.3", - "paths": { - "/projects": { - "get": { - "summary": "Get a list of projects", - "description": "Retrieves a list of projects for the Neon account.\nA project is the top-level object in the Neon object hierarchy.\nFor more information, see [Manage projects](https://neon.tech/docs/manage/projects/).\n", - "tags": [ - "Project" - ], - "operationId": "listProjects", - "parameters": [ - { - "name": "cursor", - "description": "Specify the cursor value from the previous response to get the next batch of projects.", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "description": "Specify a value from 1 to 100 to limit number of projects in the response.", - "in": "query", - "schema": { - "type": "integer", - "minimum": 1, - "default": 10, - "maximum": 100 - } - } - ], - "responses": { - "200": { - "description": "Returned a list of projects for the Neon account", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ProjectsResponse" - }, - { - "$ref": "#/components/schemas/PaginationResponse" - } - ] - }, - "example": { - "projects": [ - { - "id": "shiny-wind-028834", - "platform_id": "aws", - "region_id": "aws-us-east-2", - "name": "shiny-wind-028834", - "provisioner": "k8s-pod", - "pg_version": 15, - "created_at": "2022-11-23T17:42:25Z", - "updated_at": "2022-11-23T17:42:25Z", - "proxy_host": "us-east-2.aws.neon.tech", - "cpu_used_sec": 0, - "branch_logical_size_limit": 0, - "owner_id": "1232111", - "creation_source": "console", - "store_passwords": true, - "branch_logical_size_limit_bytes": 10800, - "active_time": 100 - }, - { - "id": "winter-boat-259881", - "platform_id": "aws", - "region_id": "aws-us-east-2", - "name": "winter-boat-259881", - "provisioner": "k8s-pod", - "pg_version": 15, - "created_at": "2022-11-23T17:52:25Z", - "updated_at": "2022-11-23T17:52:25Z", - "proxy_host": "us-east-2.aws.neon.tech", - "cpu_used_sec": 0, - "branch_logical_size_limit": 0, - "owner_id": "1232111", - "creation_source": "console", - "store_passwords": true, - "branch_logical_size_limit_bytes": 10800, - "active_time": 100 - } - ] - } - } - } - }, - "default": { - "$ref": "#/components/responses/GeneralError" - } - } - } - } - } -} -`, - ) - if err != nil { - t.Fatal(err) - } - spec = enrichSpec(spec) - - want := templateInput{ - ServerURL: "https://console.neon.tech/api/v2", - EndpointsInterfaceMethods: []string{ - `// ListProjects Retrieves a list of projects for the Neon account. -// A project is the top-level object in the Neon object hierarchy. -// For more information, see [Manage projects](https://neon.tech/docs/manage/projects/). -ListProjects(cursor *string, limit *int) (ListProjectsRespObj, error)`, - }, - EndpointsImplementation: nil, - EndpointsImplementationTest: nil, - Types: nil, - EndpointsResponseExample: nil, - } - - // WHEN - got := extractSpecs(spec, []string{"/projects"}) - - // THEN - if !reflect.DeepEqual(got.EndpointsInterfaceMethods, want.EndpointsInterfaceMethods) { - t.Fatalf( - "Faulty EndpointsInterfaceMethods: want: %#v, got: %#v", - want.EndpointsInterfaceMethods, - got.EndpointsInterfaceMethods, - ) - } - }, - ) -} diff --git a/generator/generator.go b/generator/generator.go index 7a9a502..f195adb 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -118,7 +118,6 @@ func extractSpecs(spec openAPISpec, orderedEndpointRoutes []string) templateInpu endpointsStr := make([]string, len(endpoints)) endpointsTestStr := make([]string, len(endpoints)) - interfaceMethodsStr := make([]string, len(endpoints)) models := models{} mockResponses := map[string]map[string]mockResponse{ @@ -449,7 +448,6 @@ func extractSpecs(spec openAPISpec, orderedEndpointRoutes []string) templateInpu for i, s := range endpoints { endpointsStr[i] = s.generateMethodImplementation() - interfaceMethodsStr[i] = s.generateMethodDefinition() endpointsTestStr[i] = s.generateMethodImplementationTest() if _, ok := mockResponses[s.Route]; !ok { @@ -476,7 +474,6 @@ func extractSpecs(spec openAPISpec, orderedEndpointRoutes []string) templateInpu return templateInput{ Info: spec.Info.Description, ServerURL: spec.Servers[0].URL, - EndpointsInterfaceMethods: interfaceMethodsStr, EndpointsImplementation: endpointsStr, EndpointsImplementationTest: endpointsTestStr, EndpointsResponseExample: mockResponses, @@ -522,7 +519,6 @@ type openAPISpec struct { type templateInput struct { Info string ServerURL string - EndpointsInterfaceMethods []string EndpointsImplementation []string EndpointsImplementationTest []string Types []string @@ -563,7 +559,11 @@ func (e endpointImplementation) functionDescription() string { } func (e endpointImplementation) generateMethodImplementation() string { - o := "func (c *client) " + e.generateMethodHeader() + " {\n" + var o string + if e.Description != "" { + o += e.functionDescription() + "\n" + } + o += "func (c *Client) " + e.generateMethodHeader() + " {\n" reqObj := "nil" if e.RequestBodyStruct != nil { @@ -775,7 +775,7 @@ func (e endpointImplementation) generateMethodImplementationTest() string { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } diff --git a/generator/generator_test.go b/generator/generator_test.go index 5fb4af6..1af9c3b 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -73,12 +73,13 @@ func TestRun(t *testing.T) { }, wantErr: false, files: map[string]struct{}{ - "go.mod": {}, - "doc.go": {}, - "sdk.go": {}, - "sdk_test.go": {}, - "mock.go": {}, - "mock_test.go": {}, + "go.mod": {}, + "doc.go": {}, + "sdk.go": {}, + "sdk_test.go": {}, + "error.go": {}, + "mockhttp.go": {}, + "mockhttp_test.go": {}, }, }, } @@ -171,7 +172,8 @@ func Test_endpointImplementation_generateMethodImplementation(t *testing.T) { }, }, }, - want: `func (c *client) ListProjects(cursor *string, limit *int) (ListProjectsResponse, error) { + want: `// ListProjects Retrieves a list of projects for the Neon account +func (c *Client) ListProjects(cursor *string, limit *int) (ListProjectsResponse, error) { var queryElements []string if cursor != nil { queryElements = append(queryElements, "cursor=" + *cursor) @@ -190,15 +192,20 @@ func Test_endpointImplementation_generateMethodImplementation(t *testing.T) { { name: "get project details", fields: fields{ - Name: "GetProject", - Method: "GET", - Route: "/projects/{project_id}", - Description: "Retrieves information about the specified project", + Name: "GetProject", + Method: "GET", + Route: "/projects/{project_id}", + Description: `Retrieves information about the specified project. +foo bar +qux`, RequestBodyStruct: nil, ResponseStruct: &model{name: "ProjectsResponse"}, RequestParametersPath: []field{{"project_id", "string", "", "", true, true, false}}, }, - want: `func (c *client) GetProject(projectID string) (ProjectsResponse, error) { + want: `// GetProject Retrieves information about the specified project. +// foo bar +// qux +func (c *Client) GetProject(projectID string) (ProjectsResponse, error) { var v ProjectsResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID, "GET", nil, &v); err != nil { return ProjectsResponse{}, err @@ -220,7 +227,8 @@ func Test_endpointImplementation_generateMethodImplementation(t *testing.T) { {"branch_id", "string", "", "", true, true, false}, }, }, - want: `func (c *client) ListProjectBranchDatabases(projectID string, branchID string) (DatabasesResponse, error) { + want: `// ListProjectBranchDatabases Retrieves a list of databases for the specified branch +func (c *Client) ListProjectBranchDatabases(projectID string, branchID string) (DatabasesResponse, error) { var v DatabasesResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases", "GET", nil, &v); err != nil { return DatabasesResponse{}, err @@ -239,7 +247,8 @@ func Test_endpointImplementation_generateMethodImplementation(t *testing.T) { ResponseStruct: &model{name: "ApiKeyRevokeResponse"}, RequestParametersPath: []field{{"key_id", "integer", "int64", "", true, true, false}}, }, - want: `func (c *client) RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) { + want: `// RevokeApiKey Revokes the specified API key +func (c *Client) RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) { var v ApiKeyRevokeResponse if err := c.requestHandler(c.baseURL+"/api_keys/"+strconv.FormatInt(keyID, 10), "DELETE", nil, &v); err != nil { return ApiKeyRevokeResponse{}, err @@ -258,7 +267,8 @@ func Test_endpointImplementation_generateMethodImplementation(t *testing.T) { ResponseStruct: &model{name: "CreatedProject"}, RequestParametersPath: nil, }, - want: `func (c *client) CreateProject(cfg *ProjectCreateRequest) (CreatedProject, error) { + want: `// CreateProject Creates a Neon project +func (c *Client) CreateProject(cfg *ProjectCreateRequest) (CreatedProject, error) { var v CreatedProject if err := c.requestHandler(c.baseURL+"/projects", "POST", cfg, &v); err != nil { return CreatedProject{}, err @@ -1415,7 +1425,7 @@ func Test_endpointImplementation_generateMethodImplementationTest(t *testing.T) for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1512,7 +1522,7 @@ func Test_endpointImplementation_generateMethodImplementationTest(t *testing.T) for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } diff --git a/generator/templates/doc.go.templ b/generator/templates/doc.go.templ index 53d5ed9..c0f35d2 100644 --- a/generator/templates/doc.go.templ +++ b/generator/templates/doc.go.templ @@ -2,7 +2,7 @@ Find more about the service: https://neon.tech/docs/introduction. -Author: dkisler.com +Author: Dmitry Kisler */ diff --git a/generator/templates/error.go.templ b/generator/templates/error.go.templ new file mode 100644 index 0000000..233ca4e --- /dev/null +++ b/generator/templates/error.go.templ @@ -0,0 +1,60 @@ +package sdk + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strconv" +) + +// Error API error. +type Error struct { + HTTPCode int + errorResp +} + +func (e Error) Error() string { + return "[HTTP Code: " + strconv.Itoa(e.HTTPCode) + "][Error Code: " + e.Code + "] " + e.Message +} + +func (e Error) httpResp() *http.Response { + o, _ := json.Marshal(e.errorResp) + return &http.Response{ + Status: e.Code, + StatusCode: e.HTTPCode, + Body: io.NopCloser(bytes.NewReader(o)), + ContentLength: int64(len(o)), + } +} + +type errorResp struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func convertErrorResponse(res *http.Response) error { + var v errorResp + buf, err := io.ReadAll(res.Body) + defer func() { _ = res.Body.Close() }() + if err != nil { + return Error{ + HTTPCode: res.StatusCode, + errorResp: errorResp{ + Message: "cannot read response bytes", + }, + } + } + if err := json.Unmarshal(buf, &v); err != nil { + return Error{ + HTTPCode: res.StatusCode, + errorResp: errorResp{ + Message: err.Error(), + }, + } + } + return Error{ + HTTPCode: res.StatusCode, + errorResp: v, + } +} diff --git a/generator/templates/mock.go.templ b/generator/templates/mockhttp.go.templ similarity index 100% rename from generator/templates/mock.go.templ rename to generator/templates/mockhttp.go.templ diff --git a/generator/templates/mock_test.go.templ b/generator/templates/mockhttp_test.go.templ similarity index 100% rename from generator/templates/mock_test.go.templ rename to generator/templates/mockhttp_test.go.templ diff --git a/generator/templates/sdk.go.templ b/generator/templates/sdk.go.templ index 0c30237..3b15550 100644 --- a/generator/templates/sdk.go.templ +++ b/generator/templates/sdk.go.templ @@ -6,87 +6,39 @@ import ( "errors" "io" "net/http" - "os" "reflect" "strconv" "strings" "time" ) -type errorResp struct { - Code string `json:"code"` - Message string `json:"message"` -} - -// Error API error. -type Error struct { - HTTPCode int - errorResp -} - -func (e Error) Error() string { - return "[HTTP Code: " + strconv.Itoa(e.HTTPCode) + "][Error Code: " + e.Code + "] " + e.Message -} - -func (e Error) httpResp() *http.Response { - o, _ := json.Marshal(e.errorResp) - return &http.Response{ - Status: e.Code, - StatusCode: e.HTTPCode, - Body: io.NopCloser(bytes.NewReader(o)), - ContentLength: int64(len(o)), +// NewClient initialised the Client to communicate to the Neon Platform. +func NewClient(cfg Config) (*Client, error) { + if _, ok := (cfg.HTTPClient).(mockHTTPClient); !ok && cfg.Key == "" { + return nil, errors.New( + "authorization key must be provided: https://neon.tech/docs/reference/api-reference/#authentication", + ) } -} -func convertErrorResponse(res *http.Response) error { - var v errorResp - buf, err := io.ReadAll(res.Body) - defer func() { _ = res.Body.Close() }() - if err != nil { - return Error{ - HTTPCode: res.StatusCode, - errorResp: errorResp{ - Message: "cannot read response bytes", - }, - } - } - if err := json.Unmarshal(buf, &v); err != nil { - return Error{ - HTTPCode: res.StatusCode, - errorResp: errorResp{ - Message: err.Error(), - }, - } - } - return Error{ - HTTPCode: res.StatusCode, - errorResp: v, - } -} + c := &Client{ + baseURL: baseURL, + cfg: cfg, + } -// Client defines the Neon SDK client. -type Client interface { {{ range .EndpointsInterfaceMethods }} -{{.}} -{{ end }} -} + if c.cfg.HTTPClient == nil { + c.cfg.HTTPClient = &http.Client{Timeout: defaultTimeout} + } -// HTTPClient client to handle http requests. -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) + return c, nil } -type options struct { - // key API access key. - key string +// Config defines the client's configuration. +type Config struct { + // Key defines the access API key. + Key string - // httpClient Client to communicate with the API over http. - httpClient HTTPClient -} - -type client struct { - options options - - baseURL string + // HTTPClient HTTP client to communicate with the API. + HTTPClient HTTPClient } const ( @@ -94,60 +46,16 @@ const ( defaultTimeout = 2 * time.Minute ) -// NewClient initialised the Client to communicate to the Neon Platform. -func NewClient(optFns ...func(*options)) (Client, error) { - o := options{ - key: "", - httpClient: nil, - } - - for _, fn := range optFns { - fn(&o) - } - - resolveHTTPClient(&o) - if err := resolveApiKey(&o); err != nil { - return nil, err - } - - return &client{ - baseURL: baseURL, - options: o, - }, nil -} - -func resolveApiKey(o *options) error { - if o.key == "" { - o.key = os.Getenv("NEON_API_KEY") - } - - if _, ok := (o.httpClient).(mockHTTPClient); !ok && o.key == "" { - return errors.New( - "authorization key must be provided: https://neon.tech/docs/reference/api-reference/#authentication", - ) - } - - return nil -} - -func resolveHTTPClient(o *options) { - if o.httpClient == nil { - o.httpClient = &http.Client{Timeout: defaultTimeout} - } -} +// Client defines the Neon SDK client. +type Client struct { + cfg Config -// WithHTTPClient sets custom http Client. -func WithHTTPClient(client HTTPClient) func(*options) { - return func(o *options) { - o.httpClient = client - } + baseURL string } -// WithAPIKey sets the Neon API key. -func WithAPIKey(key string) func(*options) { - return func(o *options) { - o.key = key - } +// HTTPClient client to handle http requests. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) } func setHeaders(req *http.Request, token string) { @@ -158,7 +66,7 @@ func setHeaders(req *http.Request, token string) { } } -func (c *client) requestHandler(url string, t string, reqPayload interface{}, responsePayload interface{}) error { +func (c *Client) requestHandler(url string, t string, reqPayload interface{}, responsePayload interface{}) error { var body io.Reader var err error @@ -173,9 +81,9 @@ func (c *client) requestHandler(url string, t string, reqPayload interface{}, re } req, _ := http.NewRequest(t, url, body) - setHeaders(req, c.options.key) + setHeaders(req, c.cfg.Key) - res, err := c.options.httpClient.Do(req) + res, err := c.cfg.HTTPClient.Do(req) if err != nil { return err } diff --git a/generator/templates/sdk_test.go.templ b/generator/templates/sdk_test.go.templ index a11235b..6198984 100644 --- a/generator/templates/sdk_test.go.templ +++ b/generator/templates/sdk_test.go.templ @@ -14,69 +14,67 @@ import ( func TestNewClient(t *testing.T) { type args struct { - optFns []func(*options) + cfg Config } tests := []struct { name string - envVar string args args - want Client + want *Client wantErr bool }{ { - name: "happy path", - envVar: "foo", + name: "happy path, default http client", args: args{ - optFns: nil, + cfg: Config{ + Key: "foo", + }, }, - want: &client{ - options: options{ - key: "foo", - httpClient: &http.Client{Timeout: defaultTimeout}, + want: &Client{ + cfg: Config{ + Key: "foo", + HTTPClient: &http.Client{Timeout: defaultTimeout}, }, baseURL: baseURL, }, wantErr: false, }, { - name: "unhappy path: missing api key", - envVar: "", + name: "unhappy path: missing api key", args: args{ - optFns: nil, + cfg: Config{}, }, want: nil, wantErr: true, }, { - name: "happy path: custom http client", - envVar: "bar", + name: "happy path: custom http client", args: args{ - optFns: []func(*options){ - WithHTTPClient(&http.Client{Timeout: 1 * time.Minute}), + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, }, - want: &client{ - options: options{ - key: "bar", - httpClient: &http.Client{Timeout: 1 * time.Minute}, + want: &Client{ + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, baseURL: baseURL, }, wantErr: false, }, { - name: "happy path: custom http client and key from variadic fn", - envVar: "", + name: "happy path: custom http client and key", args: args{ - optFns: []func(*options){ - WithHTTPClient(&http.Client{Timeout: 1 * time.Minute}), - WithAPIKey("bar"), + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, }, - want: &client{ - options: options{ - key: "bar", - httpClient: &http.Client{Timeout: 1 * time.Minute}, + want: &Client{ + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, baseURL: baseURL, }, @@ -87,9 +85,7 @@ func TestNewClient(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - t.Setenv("NEON_API_KEY", tt.envVar) - - got, err := NewClient(tt.args.optFns...) + got, err := NewClient(tt.args.cfg) if (err != nil) != tt.wantErr { t.Errorf("NewClient() error = %v, wantErr %v", err, tt.wantErr) return @@ -158,7 +154,7 @@ func (m *mockHttp) Do(req *http.Request) (*http.Response, error) { func Test_client_requestHandler(t *testing.T) { type fields struct { - options options + cfg Config baseURL string } type args struct { @@ -181,9 +177,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "happy path: post w payload", fields: fields{ - options: options{ - key: "foo", - httpClient: &mockHttp{ + cfg: Config{ + Key: "foo", + HTTPClient: &mockHttp{ err: Error{HTTPCode: http.StatusOK}, }, }, @@ -206,9 +202,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "happy path: get w/o payload", fields: fields{ - options: options{ - key: "bar", - httpClient: &mockHttp{ + cfg: Config{ + Key: "bar", + HTTPClient: &mockHttp{ err: Error{HTTPCode: http.StatusOK}, respBody: mockPayload{Foo: "resp:"}, }, @@ -231,9 +227,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "unhappy path: get w/o payload", fields: fields{ - options: options{ - key: "bar", - httpClient: &mockHttp{ + cfg: Config{ + Key: "bar", + HTTPClient: &mockHttp{ err: Error{ HTTPCode: http.StatusNotFound, errorResp: errorResp{ @@ -268,8 +264,8 @@ func Test_client_requestHandler(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c := &client{ - options: tt.fields.options, + c := &Client{ + cfg: tt.fields.cfg, baseURL: tt.fields.baseURL, } respPayload = mockPayload{} @@ -280,7 +276,7 @@ func Test_client_requestHandler(t *testing.T) { t.Errorf("requestHandler() error = %v, wantErr %v", err, tt.wantErr) } - if !reflect.DeepEqual(tt.wantRequestHeaders, (tt.fields.options.httpClient).(*mockHttp).reqHeaders) { + if !reflect.DeepEqual(tt.wantRequestHeaders, (tt.fields.cfg.HTTPClient).(*mockHttp).reqHeaders) { t.Errorf("missing expected request headers") } diff --git a/mock.go b/mockhttp.go similarity index 99% rename from mock.go rename to mockhttp.go index 3c7e2df..4647ed4 100644 --- a/mock.go +++ b/mockhttp.go @@ -480,8 +480,8 @@ var endpointResponseExamples = map[string]map[string]mockResponse{ // Mock client return the response as per API spec, except for the errors: 404 and 401 status codes are covered only. // - 401 is returned when the string `invalidApiKey` is used as the API key; // - 404 is returned if either of the following: -// - the string value `notFound` is used as the string argument, e.g. projectID -// - a negative int/float value is used as the int/float argument, e.g. database ID +// - the string value `notFound` is used as the string argument, e.g. projectID +// - a negative int/float value is used as the int/float argument, e.g. database ID func NewMockHTTPClient() HTTPClient { u, _ := url.Parse(baseURL) return mockHTTPClient{ diff --git a/mock_test.go b/mockhttp_test.go similarity index 100% rename from mock_test.go rename to mockhttp_test.go diff --git a/sdk.go b/sdk.go index ae05f96..6336aa7 100644 --- a/sdk.go +++ b/sdk.go @@ -6,359 +6,39 @@ import ( "errors" "io" "net/http" - "os" "reflect" "strconv" "strings" "time" ) -type errorResp struct { - Code string `json:"code"` - Message string `json:"message"` -} - -// Error API error. -type Error struct { - HTTPCode int - errorResp -} - -func (e Error) Error() string { - return "[HTTP Code: " + strconv.Itoa(e.HTTPCode) + "][Error Code: " + e.Code + "] " + e.Message -} - -func (e Error) httpResp() *http.Response { - o, _ := json.Marshal(e.errorResp) - return &http.Response{ - Status: e.Code, - StatusCode: e.HTTPCode, - Body: io.NopCloser(bytes.NewReader(o)), - ContentLength: int64(len(o)), +// NewClient initialised the Client to communicate to the Neon Platform. +func NewClient(cfg Config) (*Client, error) { + if _, ok := (cfg.HTTPClient).(mockHTTPClient); !ok && cfg.Key == "" { + return nil, errors.New( + "authorization key must be provided: https://neon.tech/docs/reference/api-reference/#authentication", + ) } -} -func convertErrorResponse(res *http.Response) error { - var v errorResp - buf, err := io.ReadAll(res.Body) - defer func() { _ = res.Body.Close() }() - if err != nil { - return Error{ - HTTPCode: res.StatusCode, - errorResp: errorResp{ - Message: "cannot read response bytes", - }, - } - } - if err := json.Unmarshal(buf, &v); err != nil { - return Error{ - HTTPCode: res.StatusCode, - errorResp: errorResp{ - Message: err.Error(), - }, - } - } - return Error{ - HTTPCode: res.StatusCode, - errorResp: v, + c := &Client{ + baseURL: baseURL, + cfg: cfg, } -} - -// Client defines the Neon SDK client. -type Client interface { - // ListApiKeys Retrieves the API keys for your Neon account. - // The response does not include API key tokens. A token is only provided when creating an API key. - // API keys can also be managed in the Neon Console. - // For more information, see [Manage API keys](https://neon.tech/docs/manage/api-keys/). - ListApiKeys() ([]ApiKeysListResponseItem, error) - - // CreateApiKey Creates an API key. - // The `key_name` is a user-specified name for the key. - // This method returns an `id` and `key`. The `key` is a randomly generated, 64-bit token required to access the Neon API. - // API keys can also be managed in the Neon Console. - // See [Manage API keys](https://neon.tech/docs/manage/api-keys/). - CreateApiKey(cfg ApiKeyCreateRequest) (ApiKeyCreateResponse, error) - - // RevokeApiKey Revokes the specified API key. - // An API key that is no longer needed can be revoked. - // This action cannot be reversed. - // You can obtain `key_id` values by listing the API keys for your Neon account. - // API keys can also be managed in the Neon Console. - // See [Manage API keys](https://neon.tech/docs/manage/api-keys/). - RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) - - // GetProjectOperation Retrieves details for the specified operation. - // An operation is an action performed on a Neon project resource. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain a `operation_id` by listing operations for the project. - GetProjectOperation(projectID string, operationID string) (OperationResponse, error) - - // ListProjects Retrieves a list of projects for the Neon account. - // A project is the top-level object in the Neon object hierarchy. - // For more information, see [Manage projects](https://neon.tech/docs/manage/projects/). - ListProjects(cursor *string, limit *int) (ListProjectsRespObj, error) - - // CreateProject Creates a Neon project. - // A project is the top-level object in the Neon object hierarchy. - // Plan limits define how many projects you can create. - // Neon's Free plan permits one project per Neon account. - // For more information, see [Manage projects](https://neon.tech/docs/manage/projects/). - // You can specify a region and PostgreSQL version in the request body. - // Neon currently supports PostgreSQL 14 and 15. - // For supported regions and `region_id` values, see [Regions](https://neon.tech/docs/introduction/regions/). - CreateProject(cfg ProjectCreateRequest) (CreatedProject, error) - - // GetProject Retrieves information about the specified project. - // A project is the top-level object in the Neon object hierarchy. - // You can obtain a `project_id` by listing the projects for your Neon account. - GetProject(projectID string) (ProjectResponse, error) - - // UpdateProject Updates the specified project. - // You can obtain a `project_id` by listing the projects for your Neon account. - // Neon permits updating the project name only. - UpdateProject(projectID string, cfg ProjectUpdateRequest) (UpdateProjectRespObj, error) - - // DeleteProject Deletes the specified project. - // You can obtain a `project_id` by listing the projects for your Neon account. - // Deleting a project is a permanent action. - // Deleting a project also deletes endpoints, branches, databases, and users that belong to the project. - DeleteProject(projectID string) (ProjectResponse, error) - - // ListProjectOperations Retrieves a list of operations for the specified Neon project. - // You can obtain a `project_id` by listing the projects for your Neon account. - // The number of operations returned can be large. - // To paginate the response, issue an initial request with a `limit` value. - // Then, add the `cursor` value that was returned in the response to the next request. - ListProjectOperations(projectID string, cursor *string, limit *int) (ListOperations, error) - - // ListProjectBranches Retrieves a list of branches for the specified project. - // You can obtain a `project_id` by listing the projects for your Neon account. - // Each Neon project has a root branch named `main`. - // A `branch_id` value has a `br-` prefix. - // A project may contain child branches that were branched from `main` or from another branch. - // A parent branch is identified by the `parent_id` value, which is the `id` of the parent branch. - // For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). - ListProjectBranches(projectID string) (BranchesResponse, error) - - // CreateProjectBranch Creates a branch in the specified project. - // You can obtain a `project_id` by listing the projects for your Neon account. - // This method does not require a request body, but you can specify one to create an endpoint for the branch or to select a non-default parent branch. - // The default behavior is to create a branch from the project's root branch (`main`) with no endpoint, and the branch name is auto-generated. - // For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). - CreateProjectBranch(projectID string, cfg *BranchCreateRequest) (CreatedBranch, error) - - // GetProjectBranch Retrieves information about the specified branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain a `branch_id` by listing the project's branches. - // A `branch_id` value has a `br-` prefix. - // Each Neon project has a root branch named `main`. - // A project may contain child branches that were branched from `main` or from another branch. - // A parent branch is identified by a `parent_id` value, which is the `id` of the parent branch. - // For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). - GetProjectBranch(projectID string, branchID string) (BranchResponse, error) - - // UpdateProjectBranch Updates the specified branch. Only changing the branch name is supported. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // For more information, see [Manage branches](https://neon.tech/docs/manage/branches/). - UpdateProjectBranch(projectID string, branchID string, cfg BranchUpdateRequest) (BranchOperations, error) - - // DeleteProjectBranch Deletes the specified branch from a project, and places - // all endpoints into an idle state, breaking existing client connections. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain a `branch_id` by listing the project's branches. - // For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). - // When a successful response status is received, the endpoints are still active, - // and the branch is not yet deleted from storage. - // The deletion occurs after all operations finish. - // You cannot delete a branch if it is the only remaining branch in the project. - // A project must have at least one branch. - DeleteProjectBranch(projectID string, branchID string) (BranchOperations, error) - - // SetPrimaryProjectBranch The primary mark is automatically removed from the previous primary branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // For more information, see [Manage branches](https://neon.tech/docs/manage/branches/). - SetPrimaryProjectBranch(projectID string, branchID string) (BranchOperations, error) - - // ListProjectBranchEndpoints Retrieves a list of endpoints for the specified branch. - // Currently, Neon permits only one endpoint per branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - ListProjectBranchEndpoints(projectID string, branchID string) (EndpointsResponse, error) - - // ListProjectBranchDatabases Retrieves a list of databases for the specified branch. - // A branch can have multiple databases. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). - ListProjectBranchDatabases(projectID string, branchID string) (DatabasesResponse, error) - - // CreateProjectBranchDatabase Creates a database in the specified branch. - // A branch can have multiple databases. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). - CreateProjectBranchDatabase(projectID string, branchID string, cfg DatabaseCreateRequest) (DatabaseOperations, error) - - // GetProjectBranchDatabase Retrieves information about the specified database. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` and `database_name` by listing branch's databases. - // For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). - GetProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseResponse, error) - - // UpdateProjectBranchDatabase Updates the specified database in the branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` and `database_name` by listing the branch's databases. - // For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). - UpdateProjectBranchDatabase(projectID string, branchID string, databaseName string, cfg DatabaseUpdateRequest) (DatabaseOperations, error) - - // DeleteProjectBranchDatabase Deletes the specified database from the branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` and `database_name` by listing branch's databases. - // For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). - DeleteProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseOperations, error) - - // ListProjectBranchRoles Retrieves a list of roles from the specified branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - ListProjectBranchRoles(projectID string, branchID string) (RolesResponse, error) - - // CreateProjectBranchRole Creates a role in the specified branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - // Connections established to the active compute endpoint will be dropped. - // If the compute endpoint is idle, the endpoint becomes active for a short period of time and is suspended afterward. - CreateProjectBranchRole(projectID string, branchID string, cfg RoleCreateRequest) (RoleOperations, error) - - // GetProjectBranchRole Retrieves details about the specified role. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // You can obtain the `role_name` by listing the roles for a branch. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - GetProjectBranchRole(projectID string, branchID string, roleName string) (RoleResponse, error) - - // DeleteProjectBranchRole Deletes the specified role from the branch. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // You can obtain the `role_name` by listing the roles for a branch. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - DeleteProjectBranchRole(projectID string, branchID string, roleName string) (RoleOperations, error) - - // GetProjectBranchRolePassword Retrieves password of the specified role if possible. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // You can obtain the `role_name` by listing the roles for a branch. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - GetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RolePasswordResponse, error) - - // ResetProjectBranchRolePassword Resets the password for the specified role. - // Returns a new password and operations. The new password is ready to use when the last operation finishes. - // The old password remains valid until last operation finishes. - // Connections to the compute endpoint are dropped. If idle, - // the compute endpoint becomes active for a short period of time. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain the `branch_id` by listing the project's branches. - // You can obtain the `role_name` by listing the roles for a branch. - // In Neon, the terms "role" and "user" are synonymous. - // For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). - ResetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RoleOperations, error) - - // ListProjectEndpoints Retrieves a list of endpoints for the specified project. - // An endpoint is a Neon compute instance. - // You can obtain a `project_id` by listing the projects for your Neon account. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - ListProjectEndpoints(projectID string) (EndpointsResponse, error) - - // CreateProjectEndpoint Creates an endpoint for the specified branch. - // An endpoint is a Neon compute instance. - // There is a maximum of one read-write endpoint per branch. - // If the specified branch already has a read-write endpoint, the operation fails. - // A branch can have multiple read-only endpoints. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain `branch_id` by listing the project's branches. - // A `branch_id` has a `br-` prefix. - // For supported regions and `region_id` values, see [Regions](https://neon.tech/docs/introduction/regions/). - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - CreateProjectEndpoint(projectID string, cfg EndpointCreateRequest) (EndpointOperations, error) - - // GetProjectEndpoint Retrieves information about the specified endpoint. - // An endpoint is a Neon compute instance. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain an `endpoint_id` by listing your project's endpoints. - // An `endpoint_id` has an `ep-` prefix. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - GetProjectEndpoint(projectID string, endpointID string) (EndpointResponse, error) - - // UpdateProjectEndpoint Updates the specified endpoint. Currently, only changing the associated branch is supported. - // The branch that you specify cannot have an existing endpoint. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain an `endpoint_id` and `branch_id` by listing your project's endpoints. - // An `endpoint_id` has an `ep-` prefix. A `branch_id` has a `br-` prefix. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - // If the returned list of operations is not empty, the endpoint is not ready to use. - // The client must wait for the last operation to finish before using the endpoint. - // If the endpoint was idle before the update, the endpoint becomes active for a short period of time, - // and the control plane suspends it again after the update. - UpdateProjectEndpoint(projectID string, endpointID string, cfg EndpointUpdateRequest) (EndpointOperations, error) - - // DeleteProjectEndpoint Delete the specified endpoint. - // An endpoint is a Neon compute instance. - // Deleting an endpoint drops existing network connections to the endpoint. - // The deletion is completed when last operation in the chain finishes successfully. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain an `endpoint_id` by listing your project's endpoints. - // An `endpoint_id` has an `ep-` prefix. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - DeleteProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) - - // StartProjectEndpoint Starts an endpoint. The endpoint is ready to use - // after the last operation in chain finishes successfully. - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain an `endpoint_id` by listing your project's endpoints. - // An `endpoint_id` has an `ep-` prefix. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - StartProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) - - // SuspendProjectEndpoint Suspend the specified endpoint - // You can obtain a `project_id` by listing the projects for your Neon account. - // You can obtain an `endpoint_id` by listing your project's endpoints. - // An `endpoint_id` has an `ep-` prefix. - // For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). - SuspendProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) - - // ListProjectsConsumption This is a preview API and is subject to changes in the future. - // Retrieves a list project consumption metrics for each project for the current billing period. - ListProjectsConsumption(cursor *string, limit *int) (ListProjectsConsumptionRespObj, error) - - // GetCurrentUserInfo Retrieves information about the current user - GetCurrentUserInfo() (CurrentUserInfoResponse, error) -} - -// HTTPClient client to handle http requests. -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} -type options struct { - // key API access key. - key string + if c.cfg.HTTPClient == nil { + c.cfg.HTTPClient = &http.Client{Timeout: defaultTimeout} + } - // httpClient Client to communicate with the API over http. - httpClient HTTPClient + return c, nil } -type client struct { - options options +// Config defines the client's configuration. +type Config struct { + // Key defines the access API key. + Key string - baseURL string + // HTTPClient HTTP client to communicate with the API. + HTTPClient HTTPClient } const ( @@ -366,60 +46,16 @@ const ( defaultTimeout = 2 * time.Minute ) -// NewClient initialised the Client to communicate to the Neon Platform. -func NewClient(optFns ...func(*options)) (Client, error) { - o := options{ - key: "", - httpClient: nil, - } - - for _, fn := range optFns { - fn(&o) - } - - resolveHTTPClient(&o) - if err := resolveApiKey(&o); err != nil { - return nil, err - } - - return &client{ - baseURL: baseURL, - options: o, - }, nil -} - -func resolveApiKey(o *options) error { - if o.key == "" { - o.key = os.Getenv("NEON_API_KEY") - } - - if _, ok := (o.httpClient).(mockHTTPClient); !ok && o.key == "" { - return errors.New( - "authorization key must be provided: https://neon.tech/docs/reference/api-reference/#authentication", - ) - } - - return nil -} - -func resolveHTTPClient(o *options) { - if o.httpClient == nil { - o.httpClient = &http.Client{Timeout: defaultTimeout} - } -} +// Client defines the Neon SDK client. +type Client struct { + cfg Config -// WithHTTPClient sets custom http Client. -func WithHTTPClient(client HTTPClient) func(*options) { - return func(o *options) { - o.httpClient = client - } + baseURL string } -// WithAPIKey sets the Neon API key. -func WithAPIKey(key string) func(*options) { - return func(o *options) { - o.key = key - } +// HTTPClient client to handle http requests. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) } func setHeaders(req *http.Request, token string) { @@ -430,7 +66,7 @@ func setHeaders(req *http.Request, token string) { } } -func (c *client) requestHandler(url string, t string, reqPayload interface{}, responsePayload interface{}) error { +func (c *Client) requestHandler(url string, t string, reqPayload interface{}, responsePayload interface{}) error { var body io.Reader var err error @@ -445,9 +81,9 @@ func (c *client) requestHandler(url string, t string, reqPayload interface{}, re } req, _ := http.NewRequest(t, url, body) - setHeaders(req, c.options.key) + setHeaders(req, c.cfg.Key) - res, err := c.options.httpClient.Do(req) + res, err := c.cfg.HTTPClient.Do(req) if err != nil { return err } @@ -468,7 +104,11 @@ func (c *client) requestHandler(url string, t string, reqPayload interface{}, re return nil } -func (c *client) ListApiKeys() ([]ApiKeysListResponseItem, error) { +// ListApiKeys Retrieves the API keys for your Neon account. +// The response does not include API key tokens. A token is only provided when creating an API key. +// API keys can also be managed in the Neon Console. +// For more information, see [Manage API keys](https://neon.tech/docs/manage/api-keys/). +func (c *Client) ListApiKeys() ([]ApiKeysListResponseItem, error) { var v []ApiKeysListResponseItem if err := c.requestHandler(c.baseURL+"/api_keys", "GET", nil, &v); err != nil { return nil, err @@ -476,7 +116,12 @@ func (c *client) ListApiKeys() ([]ApiKeysListResponseItem, error) { return v, nil } -func (c *client) CreateApiKey(cfg ApiKeyCreateRequest) (ApiKeyCreateResponse, error) { +// CreateApiKey Creates an API key. +// The `key_name` is a user-specified name for the key. +// This method returns an `id` and `key`. The `key` is a randomly generated, 64-bit token required to access the Neon API. +// API keys can also be managed in the Neon Console. +// See [Manage API keys](https://neon.tech/docs/manage/api-keys/). +func (c *Client) CreateApiKey(cfg ApiKeyCreateRequest) (ApiKeyCreateResponse, error) { var v ApiKeyCreateResponse if err := c.requestHandler(c.baseURL+"/api_keys", "POST", cfg, &v); err != nil { return ApiKeyCreateResponse{}, err @@ -484,7 +129,13 @@ func (c *client) CreateApiKey(cfg ApiKeyCreateRequest) (ApiKeyCreateResponse, er return v, nil } -func (c *client) RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) { +// RevokeApiKey Revokes the specified API key. +// An API key that is no longer needed can be revoked. +// This action cannot be reversed. +// You can obtain `key_id` values by listing the API keys for your Neon account. +// API keys can also be managed in the Neon Console. +// See [Manage API keys](https://neon.tech/docs/manage/api-keys/). +func (c *Client) RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) { var v ApiKeyRevokeResponse if err := c.requestHandler(c.baseURL+"/api_keys/"+strconv.FormatInt(keyID, 10), "DELETE", nil, &v); err != nil { return ApiKeyRevokeResponse{}, err @@ -492,7 +143,11 @@ func (c *client) RevokeApiKey(keyID int64) (ApiKeyRevokeResponse, error) { return v, nil } -func (c *client) GetProjectOperation(projectID string, operationID string) (OperationResponse, error) { +// GetProjectOperation Retrieves details for the specified operation. +// An operation is an action performed on a Neon project resource. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain a `operation_id` by listing operations for the project. +func (c *Client) GetProjectOperation(projectID string, operationID string) (OperationResponse, error) { var v OperationResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/operations/"+operationID, "GET", nil, &v); err != nil { return OperationResponse{}, err @@ -500,7 +155,10 @@ func (c *client) GetProjectOperation(projectID string, operationID string) (Oper return v, nil } -func (c *client) ListProjects(cursor *string, limit *int) (ListProjectsRespObj, error) { +// ListProjects Retrieves a list of projects for the Neon account. +// A project is the top-level object in the Neon object hierarchy. +// For more information, see [Manage projects](https://neon.tech/docs/manage/projects/). +func (c *Client) ListProjects(cursor *string, limit *int) (ListProjectsRespObj, error) { var queryElements []string if cursor != nil { queryElements = append(queryElements, "cursor="+*cursor) @@ -516,7 +174,15 @@ func (c *client) ListProjects(cursor *string, limit *int) (ListProjectsRespObj, return v, nil } -func (c *client) CreateProject(cfg ProjectCreateRequest) (CreatedProject, error) { +// CreateProject Creates a Neon project. +// A project is the top-level object in the Neon object hierarchy. +// Plan limits define how many projects you can create. +// Neon's Free plan permits one project per Neon account. +// For more information, see [Manage projects](https://neon.tech/docs/manage/projects/). +// You can specify a region and PostgreSQL version in the request body. +// Neon currently supports PostgreSQL 14 and 15. +// For supported regions and `region_id` values, see [Regions](https://neon.tech/docs/introduction/regions/). +func (c *Client) CreateProject(cfg ProjectCreateRequest) (CreatedProject, error) { var v CreatedProject if err := c.requestHandler(c.baseURL+"/projects", "POST", cfg, &v); err != nil { return CreatedProject{}, err @@ -524,7 +190,10 @@ func (c *client) CreateProject(cfg ProjectCreateRequest) (CreatedProject, error) return v, nil } -func (c *client) GetProject(projectID string) (ProjectResponse, error) { +// GetProject Retrieves information about the specified project. +// A project is the top-level object in the Neon object hierarchy. +// You can obtain a `project_id` by listing the projects for your Neon account. +func (c *Client) GetProject(projectID string) (ProjectResponse, error) { var v ProjectResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID, "GET", nil, &v); err != nil { return ProjectResponse{}, err @@ -532,7 +201,10 @@ func (c *client) GetProject(projectID string) (ProjectResponse, error) { return v, nil } -func (c *client) UpdateProject(projectID string, cfg ProjectUpdateRequest) (UpdateProjectRespObj, error) { +// UpdateProject Updates the specified project. +// You can obtain a `project_id` by listing the projects for your Neon account. +// Neon permits updating the project name only. +func (c *Client) UpdateProject(projectID string, cfg ProjectUpdateRequest) (UpdateProjectRespObj, error) { var v UpdateProjectRespObj if err := c.requestHandler(c.baseURL+"/projects/"+projectID, "PATCH", cfg, &v); err != nil { return UpdateProjectRespObj{}, err @@ -540,7 +212,11 @@ func (c *client) UpdateProject(projectID string, cfg ProjectUpdateRequest) (Upda return v, nil } -func (c *client) DeleteProject(projectID string) (ProjectResponse, error) { +// DeleteProject Deletes the specified project. +// You can obtain a `project_id` by listing the projects for your Neon account. +// Deleting a project is a permanent action. +// Deleting a project also deletes endpoints, branches, databases, and users that belong to the project. +func (c *Client) DeleteProject(projectID string) (ProjectResponse, error) { var v ProjectResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID, "DELETE", nil, &v); err != nil { return ProjectResponse{}, err @@ -548,7 +224,12 @@ func (c *client) DeleteProject(projectID string) (ProjectResponse, error) { return v, nil } -func (c *client) ListProjectOperations(projectID string, cursor *string, limit *int) (ListOperations, error) { +// ListProjectOperations Retrieves a list of operations for the specified Neon project. +// You can obtain a `project_id` by listing the projects for your Neon account. +// The number of operations returned can be large. +// To paginate the response, issue an initial request with a `limit` value. +// Then, add the `cursor` value that was returned in the response to the next request. +func (c *Client) ListProjectOperations(projectID string, cursor *string, limit *int) (ListOperations, error) { var queryElements []string if cursor != nil { queryElements = append(queryElements, "cursor="+*cursor) @@ -564,7 +245,14 @@ func (c *client) ListProjectOperations(projectID string, cursor *string, limit * return v, nil } -func (c *client) ListProjectBranches(projectID string) (BranchesResponse, error) { +// ListProjectBranches Retrieves a list of branches for the specified project. +// You can obtain a `project_id` by listing the projects for your Neon account. +// Each Neon project has a root branch named `main`. +// A `branch_id` value has a `br-` prefix. +// A project may contain child branches that were branched from `main` or from another branch. +// A parent branch is identified by the `parent_id` value, which is the `id` of the parent branch. +// For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). +func (c *Client) ListProjectBranches(projectID string) (BranchesResponse, error) { var v BranchesResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches", "GET", nil, &v); err != nil { return BranchesResponse{}, err @@ -572,7 +260,12 @@ func (c *client) ListProjectBranches(projectID string) (BranchesResponse, error) return v, nil } -func (c *client) CreateProjectBranch(projectID string, cfg *BranchCreateRequest) (CreatedBranch, error) { +// CreateProjectBranch Creates a branch in the specified project. +// You can obtain a `project_id` by listing the projects for your Neon account. +// This method does not require a request body, but you can specify one to create an endpoint for the branch or to select a non-default parent branch. +// The default behavior is to create a branch from the project's root branch (`main`) with no endpoint, and the branch name is auto-generated. +// For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). +func (c *Client) CreateProjectBranch(projectID string, cfg *BranchCreateRequest) (CreatedBranch, error) { var v CreatedBranch if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches", "POST", cfg, &v); err != nil { return CreatedBranch{}, err @@ -580,7 +273,15 @@ func (c *client) CreateProjectBranch(projectID string, cfg *BranchCreateRequest) return v, nil } -func (c *client) GetProjectBranch(projectID string, branchID string) (BranchResponse, error) { +// GetProjectBranch Retrieves information about the specified branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain a `branch_id` by listing the project's branches. +// A `branch_id` value has a `br-` prefix. +// Each Neon project has a root branch named `main`. +// A project may contain child branches that were branched from `main` or from another branch. +// A parent branch is identified by a `parent_id` value, which is the `id` of the parent branch. +// For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). +func (c *Client) GetProjectBranch(projectID string, branchID string) (BranchResponse, error) { var v BranchResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID, "GET", nil, &v); err != nil { return BranchResponse{}, err @@ -588,7 +289,11 @@ func (c *client) GetProjectBranch(projectID string, branchID string) (BranchResp return v, nil } -func (c *client) UpdateProjectBranch(projectID string, branchID string, cfg BranchUpdateRequest) (BranchOperations, error) { +// UpdateProjectBranch Updates the specified branch. Only changing the branch name is supported. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// For more information, see [Manage branches](https://neon.tech/docs/manage/branches/). +func (c *Client) UpdateProjectBranch(projectID string, branchID string, cfg BranchUpdateRequest) (BranchOperations, error) { var v BranchOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID, "PATCH", cfg, &v); err != nil { return BranchOperations{}, err @@ -596,7 +301,17 @@ func (c *client) UpdateProjectBranch(projectID string, branchID string, cfg Bran return v, nil } -func (c *client) DeleteProjectBranch(projectID string, branchID string) (BranchOperations, error) { +// DeleteProjectBranch Deletes the specified branch from a project, and places +// all endpoints into an idle state, breaking existing client connections. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain a `branch_id` by listing the project's branches. +// For related information, see [Manage branches](https://neon.tech/docs/manage/branches/). +// When a successful response status is received, the endpoints are still active, +// and the branch is not yet deleted from storage. +// The deletion occurs after all operations finish. +// You cannot delete a branch if it is the only remaining branch in the project. +// A project must have at least one branch. +func (c *Client) DeleteProjectBranch(projectID string, branchID string) (BranchOperations, error) { var v BranchOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID, "DELETE", nil, &v); err != nil { return BranchOperations{}, err @@ -604,7 +319,11 @@ func (c *client) DeleteProjectBranch(projectID string, branchID string) (BranchO return v, nil } -func (c *client) SetPrimaryProjectBranch(projectID string, branchID string) (BranchOperations, error) { +// SetPrimaryProjectBranch The primary mark is automatically removed from the previous primary branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// For more information, see [Manage branches](https://neon.tech/docs/manage/branches/). +func (c *Client) SetPrimaryProjectBranch(projectID string, branchID string) (BranchOperations, error) { var v BranchOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/set_as_primary", "POST", nil, &v); err != nil { return BranchOperations{}, err @@ -612,7 +331,11 @@ func (c *client) SetPrimaryProjectBranch(projectID string, branchID string) (Bra return v, nil } -func (c *client) ListProjectBranchEndpoints(projectID string, branchID string) (EndpointsResponse, error) { +// ListProjectBranchEndpoints Retrieves a list of endpoints for the specified branch. +// Currently, Neon permits only one endpoint per branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +func (c *Client) ListProjectBranchEndpoints(projectID string, branchID string) (EndpointsResponse, error) { var v EndpointsResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/endpoints", "GET", nil, &v); err != nil { return EndpointsResponse{}, err @@ -620,7 +343,12 @@ func (c *client) ListProjectBranchEndpoints(projectID string, branchID string) ( return v, nil } -func (c *client) ListProjectBranchDatabases(projectID string, branchID string) (DatabasesResponse, error) { +// ListProjectBranchDatabases Retrieves a list of databases for the specified branch. +// A branch can have multiple databases. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). +func (c *Client) ListProjectBranchDatabases(projectID string, branchID string) (DatabasesResponse, error) { var v DatabasesResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases", "GET", nil, &v); err != nil { return DatabasesResponse{}, err @@ -628,7 +356,12 @@ func (c *client) ListProjectBranchDatabases(projectID string, branchID string) ( return v, nil } -func (c *client) CreateProjectBranchDatabase(projectID string, branchID string, cfg DatabaseCreateRequest) (DatabaseOperations, error) { +// CreateProjectBranchDatabase Creates a database in the specified branch. +// A branch can have multiple databases. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). +func (c *Client) CreateProjectBranchDatabase(projectID string, branchID string, cfg DatabaseCreateRequest) (DatabaseOperations, error) { var v DatabaseOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases", "POST", cfg, &v); err != nil { return DatabaseOperations{}, err @@ -636,7 +369,11 @@ func (c *client) CreateProjectBranchDatabase(projectID string, branchID string, return v, nil } -func (c *client) GetProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseResponse, error) { +// GetProjectBranchDatabase Retrieves information about the specified database. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` and `database_name` by listing branch's databases. +// For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). +func (c *Client) GetProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseResponse, error) { var v DatabaseResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases/"+databaseName, "GET", nil, &v); err != nil { return DatabaseResponse{}, err @@ -644,7 +381,11 @@ func (c *client) GetProjectBranchDatabase(projectID string, branchID string, dat return v, nil } -func (c *client) UpdateProjectBranchDatabase(projectID string, branchID string, databaseName string, cfg DatabaseUpdateRequest) (DatabaseOperations, error) { +// UpdateProjectBranchDatabase Updates the specified database in the branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` and `database_name` by listing the branch's databases. +// For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). +func (c *Client) UpdateProjectBranchDatabase(projectID string, branchID string, databaseName string, cfg DatabaseUpdateRequest) (DatabaseOperations, error) { var v DatabaseOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases/"+databaseName, "PATCH", cfg, &v); err != nil { return DatabaseOperations{}, err @@ -652,7 +393,11 @@ func (c *client) UpdateProjectBranchDatabase(projectID string, branchID string, return v, nil } -func (c *client) DeleteProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseOperations, error) { +// DeleteProjectBranchDatabase Deletes the specified database from the branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` and `database_name` by listing branch's databases. +// For related information, see [Manage databases](https://neon.tech/docs/manage/databases/). +func (c *Client) DeleteProjectBranchDatabase(projectID string, branchID string, databaseName string) (DatabaseOperations, error) { var v DatabaseOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/databases/"+databaseName, "DELETE", nil, &v); err != nil { return DatabaseOperations{}, err @@ -660,7 +405,12 @@ func (c *client) DeleteProjectBranchDatabase(projectID string, branchID string, return v, nil } -func (c *client) ListProjectBranchRoles(projectID string, branchID string) (RolesResponse, error) { +// ListProjectBranchRoles Retrieves a list of roles from the specified branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +func (c *Client) ListProjectBranchRoles(projectID string, branchID string) (RolesResponse, error) { var v RolesResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles", "GET", nil, &v); err != nil { return RolesResponse{}, err @@ -668,7 +418,14 @@ func (c *client) ListProjectBranchRoles(projectID string, branchID string) (Role return v, nil } -func (c *client) CreateProjectBranchRole(projectID string, branchID string, cfg RoleCreateRequest) (RoleOperations, error) { +// CreateProjectBranchRole Creates a role in the specified branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +// Connections established to the active compute endpoint will be dropped. +// If the compute endpoint is idle, the endpoint becomes active for a short period of time and is suspended afterward. +func (c *Client) CreateProjectBranchRole(projectID string, branchID string, cfg RoleCreateRequest) (RoleOperations, error) { var v RoleOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles", "POST", cfg, &v); err != nil { return RoleOperations{}, err @@ -676,7 +433,13 @@ func (c *client) CreateProjectBranchRole(projectID string, branchID string, cfg return v, nil } -func (c *client) GetProjectBranchRole(projectID string, branchID string, roleName string) (RoleResponse, error) { +// GetProjectBranchRole Retrieves details about the specified role. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// You can obtain the `role_name` by listing the roles for a branch. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +func (c *Client) GetProjectBranchRole(projectID string, branchID string, roleName string) (RoleResponse, error) { var v RoleResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles/"+roleName, "GET", nil, &v); err != nil { return RoleResponse{}, err @@ -684,7 +447,13 @@ func (c *client) GetProjectBranchRole(projectID string, branchID string, roleNam return v, nil } -func (c *client) DeleteProjectBranchRole(projectID string, branchID string, roleName string) (RoleOperations, error) { +// DeleteProjectBranchRole Deletes the specified role from the branch. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// You can obtain the `role_name` by listing the roles for a branch. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +func (c *Client) DeleteProjectBranchRole(projectID string, branchID string, roleName string) (RoleOperations, error) { var v RoleOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles/"+roleName, "DELETE", nil, &v); err != nil { return RoleOperations{}, err @@ -692,7 +461,13 @@ func (c *client) DeleteProjectBranchRole(projectID string, branchID string, role return v, nil } -func (c *client) GetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RolePasswordResponse, error) { +// GetProjectBranchRolePassword Retrieves password of the specified role if possible. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// You can obtain the `role_name` by listing the roles for a branch. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +func (c *Client) GetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RolePasswordResponse, error) { var v RolePasswordResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles/"+roleName+"/reveal_password", "GET", nil, &v); err != nil { return RolePasswordResponse{}, err @@ -700,7 +475,17 @@ func (c *client) GetProjectBranchRolePassword(projectID string, branchID string, return v, nil } -func (c *client) ResetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RoleOperations, error) { +// ResetProjectBranchRolePassword Resets the password for the specified role. +// Returns a new password and operations. The new password is ready to use when the last operation finishes. +// The old password remains valid until last operation finishes. +// Connections to the compute endpoint are dropped. If idle, +// the compute endpoint becomes active for a short period of time. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain the `branch_id` by listing the project's branches. +// You can obtain the `role_name` by listing the roles for a branch. +// In Neon, the terms "role" and "user" are synonymous. +// For related information, see [Manage roles](https://neon.tech/docs/manage/roles/). +func (c *Client) ResetProjectBranchRolePassword(projectID string, branchID string, roleName string) (RoleOperations, error) { var v RoleOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/branches/"+branchID+"/roles/"+roleName+"/reset_password", "POST", nil, &v); err != nil { return RoleOperations{}, err @@ -708,7 +493,11 @@ func (c *client) ResetProjectBranchRolePassword(projectID string, branchID strin return v, nil } -func (c *client) ListProjectEndpoints(projectID string) (EndpointsResponse, error) { +// ListProjectEndpoints Retrieves a list of endpoints for the specified project. +// An endpoint is a Neon compute instance. +// You can obtain a `project_id` by listing the projects for your Neon account. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) ListProjectEndpoints(projectID string) (EndpointsResponse, error) { var v EndpointsResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints", "GET", nil, &v); err != nil { return EndpointsResponse{}, err @@ -716,7 +505,17 @@ func (c *client) ListProjectEndpoints(projectID string) (EndpointsResponse, erro return v, nil } -func (c *client) CreateProjectEndpoint(projectID string, cfg EndpointCreateRequest) (EndpointOperations, error) { +// CreateProjectEndpoint Creates an endpoint for the specified branch. +// An endpoint is a Neon compute instance. +// There is a maximum of one read-write endpoint per branch. +// If the specified branch already has a read-write endpoint, the operation fails. +// A branch can have multiple read-only endpoints. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain `branch_id` by listing the project's branches. +// A `branch_id` has a `br-` prefix. +// For supported regions and `region_id` values, see [Regions](https://neon.tech/docs/introduction/regions/). +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) CreateProjectEndpoint(projectID string, cfg EndpointCreateRequest) (EndpointOperations, error) { var v EndpointOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints", "POST", cfg, &v); err != nil { return EndpointOperations{}, err @@ -724,7 +523,13 @@ func (c *client) CreateProjectEndpoint(projectID string, cfg EndpointCreateReque return v, nil } -func (c *client) GetProjectEndpoint(projectID string, endpointID string) (EndpointResponse, error) { +// GetProjectEndpoint Retrieves information about the specified endpoint. +// An endpoint is a Neon compute instance. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain an `endpoint_id` by listing your project's endpoints. +// An `endpoint_id` has an `ep-` prefix. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) GetProjectEndpoint(projectID string, endpointID string) (EndpointResponse, error) { var v EndpointResponse if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints/"+endpointID, "GET", nil, &v); err != nil { return EndpointResponse{}, err @@ -732,7 +537,17 @@ func (c *client) GetProjectEndpoint(projectID string, endpointID string) (Endpoi return v, nil } -func (c *client) UpdateProjectEndpoint(projectID string, endpointID string, cfg EndpointUpdateRequest) (EndpointOperations, error) { +// UpdateProjectEndpoint Updates the specified endpoint. Currently, only changing the associated branch is supported. +// The branch that you specify cannot have an existing endpoint. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain an `endpoint_id` and `branch_id` by listing your project's endpoints. +// An `endpoint_id` has an `ep-` prefix. A `branch_id` has a `br-` prefix. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +// If the returned list of operations is not empty, the endpoint is not ready to use. +// The client must wait for the last operation to finish before using the endpoint. +// If the endpoint was idle before the update, the endpoint becomes active for a short period of time, +// and the control plane suspends it again after the update. +func (c *Client) UpdateProjectEndpoint(projectID string, endpointID string, cfg EndpointUpdateRequest) (EndpointOperations, error) { var v EndpointOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints/"+endpointID, "PATCH", cfg, &v); err != nil { return EndpointOperations{}, err @@ -740,7 +555,15 @@ func (c *client) UpdateProjectEndpoint(projectID string, endpointID string, cfg return v, nil } -func (c *client) DeleteProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { +// DeleteProjectEndpoint Delete the specified endpoint. +// An endpoint is a Neon compute instance. +// Deleting an endpoint drops existing network connections to the endpoint. +// The deletion is completed when last operation in the chain finishes successfully. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain an `endpoint_id` by listing your project's endpoints. +// An `endpoint_id` has an `ep-` prefix. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) DeleteProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { var v EndpointOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints/"+endpointID, "DELETE", nil, &v); err != nil { return EndpointOperations{}, err @@ -748,7 +571,13 @@ func (c *client) DeleteProjectEndpoint(projectID string, endpointID string) (End return v, nil } -func (c *client) StartProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { +// StartProjectEndpoint Starts an endpoint. The endpoint is ready to use +// after the last operation in chain finishes successfully. +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain an `endpoint_id` by listing your project's endpoints. +// An `endpoint_id` has an `ep-` prefix. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) StartProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { var v EndpointOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints/"+endpointID+"/start", "POST", nil, &v); err != nil { return EndpointOperations{}, err @@ -756,7 +585,12 @@ func (c *client) StartProjectEndpoint(projectID string, endpointID string) (Endp return v, nil } -func (c *client) SuspendProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { +// SuspendProjectEndpoint Suspend the specified endpoint +// You can obtain a `project_id` by listing the projects for your Neon account. +// You can obtain an `endpoint_id` by listing your project's endpoints. +// An `endpoint_id` has an `ep-` prefix. +// For more information about endpoints, see [Manage endpoints](https://neon.tech/docs/manage/endpoints/). +func (c *Client) SuspendProjectEndpoint(projectID string, endpointID string) (EndpointOperations, error) { var v EndpointOperations if err := c.requestHandler(c.baseURL+"/projects/"+projectID+"/endpoints/"+endpointID+"/suspend", "POST", nil, &v); err != nil { return EndpointOperations{}, err @@ -764,7 +598,9 @@ func (c *client) SuspendProjectEndpoint(projectID string, endpointID string) (En return v, nil } -func (c *client) ListProjectsConsumption(cursor *string, limit *int) (ListProjectsConsumptionRespObj, error) { +// ListProjectsConsumption This is a preview API and is subject to changes in the future. +// Retrieves a list project consumption metrics for each project for the current billing period. +func (c *Client) ListProjectsConsumption(cursor *string, limit *int) (ListProjectsConsumptionRespObj, error) { var queryElements []string if cursor != nil { queryElements = append(queryElements, "cursor="+*cursor) @@ -780,7 +616,8 @@ func (c *client) ListProjectsConsumption(cursor *string, limit *int) (ListProjec return v, nil } -func (c *client) GetCurrentUserInfo() (CurrentUserInfoResponse, error) { +// GetCurrentUserInfo Retrieves information about the current user +func (c *Client) GetCurrentUserInfo() (CurrentUserInfoResponse, error) { var v CurrentUserInfoResponse if err := c.requestHandler(c.baseURL+"/users/me", "GET", nil, &v); err != nil { return CurrentUserInfoResponse{}, err diff --git a/sdk_test.go b/sdk_test.go index b72ac17..8f4655f 100644 --- a/sdk_test.go +++ b/sdk_test.go @@ -14,69 +14,67 @@ import ( func TestNewClient(t *testing.T) { type args struct { - optFns []func(*options) + cfg Config } tests := []struct { name string - envVar string args args - want Client + want *Client wantErr bool }{ { - name: "happy path", - envVar: "foo", + name: "happy path, default http client", args: args{ - optFns: nil, + cfg: Config{ + Key: "foo", + }, }, - want: &client{ - options: options{ - key: "foo", - httpClient: &http.Client{Timeout: defaultTimeout}, + want: &Client{ + cfg: Config{ + Key: "foo", + HTTPClient: &http.Client{Timeout: defaultTimeout}, }, baseURL: baseURL, }, wantErr: false, }, { - name: "unhappy path: missing api key", - envVar: "", + name: "unhappy path: missing api key", args: args{ - optFns: nil, + cfg: Config{}, }, want: nil, wantErr: true, }, { - name: "happy path: custom http client", - envVar: "bar", + name: "happy path: custom http client", args: args{ - optFns: []func(*options){ - WithHTTPClient(&http.Client{Timeout: 1 * time.Minute}), + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, }, - want: &client{ - options: options{ - key: "bar", - httpClient: &http.Client{Timeout: 1 * time.Minute}, + want: &Client{ + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, baseURL: baseURL, }, wantErr: false, }, { - name: "happy path: custom http client and key from variadic fn", - envVar: "", + name: "happy path: custom http client and key", args: args{ - optFns: []func(*options){ - WithHTTPClient(&http.Client{Timeout: 1 * time.Minute}), - WithAPIKey("bar"), + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, }, - want: &client{ - options: options{ - key: "bar", - httpClient: &http.Client{Timeout: 1 * time.Minute}, + want: &Client{ + cfg: Config{ + Key: "bar", + HTTPClient: &http.Client{Timeout: 1 * time.Minute}, }, baseURL: baseURL, }, @@ -87,9 +85,7 @@ func TestNewClient(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - t.Setenv("NEON_API_KEY", tt.envVar) - - got, err := NewClient(tt.args.optFns...) + got, err := NewClient(tt.args.cfg) if (err != nil) != tt.wantErr { t.Errorf("NewClient() error = %v, wantErr %v", err, tt.wantErr) return @@ -158,7 +154,7 @@ func (m *mockHttp) Do(req *http.Request) (*http.Response, error) { func Test_client_requestHandler(t *testing.T) { type fields struct { - options options + cfg Config baseURL string } type args struct { @@ -181,9 +177,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "happy path: post w payload", fields: fields{ - options: options{ - key: "foo", - httpClient: &mockHttp{ + cfg: Config{ + Key: "foo", + HTTPClient: &mockHttp{ err: Error{HTTPCode: http.StatusOK}, }, }, @@ -206,9 +202,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "happy path: get w/o payload", fields: fields{ - options: options{ - key: "bar", - httpClient: &mockHttp{ + cfg: Config{ + Key: "bar", + HTTPClient: &mockHttp{ err: Error{HTTPCode: http.StatusOK}, respBody: mockPayload{Foo: "resp:"}, }, @@ -231,9 +227,9 @@ func Test_client_requestHandler(t *testing.T) { { name: "unhappy path: get w/o payload", fields: fields{ - options: options{ - key: "bar", - httpClient: &mockHttp{ + cfg: Config{ + Key: "bar", + HTTPClient: &mockHttp{ err: Error{ HTTPCode: http.StatusNotFound, errorResp: errorResp{ @@ -268,8 +264,8 @@ func Test_client_requestHandler(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c := &client{ - options: tt.fields.options, + c := &Client{ + cfg: tt.fields.cfg, baseURL: tt.fields.baseURL, } respPayload = mockPayload{} @@ -280,7 +276,7 @@ func Test_client_requestHandler(t *testing.T) { t.Errorf("requestHandler() error = %v, wantErr %v", err, tt.wantErr) } - if !reflect.DeepEqual(tt.wantRequestHeaders, (tt.fields.options.httpClient).(*mockHttp).reqHeaders) { + if !reflect.DeepEqual(tt.wantRequestHeaders, (tt.fields.cfg.HTTPClient).(*mockHttp).reqHeaders) { t.Errorf("missing expected request headers") } @@ -484,7 +480,7 @@ func Test_client_ListApiKeys(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -541,7 +537,7 @@ func Test_client_CreateApiKey(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -598,7 +594,7 @@ func Test_client_RevokeApiKey(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -658,7 +654,7 @@ func Test_client_GetProjectOperation(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -718,7 +714,7 @@ func Test_client_ListProjects(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -775,7 +771,7 @@ func Test_client_CreateProject(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -832,7 +828,7 @@ func Test_client_GetProject(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -892,7 +888,7 @@ func Test_client_UpdateProject(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -949,7 +945,7 @@ func Test_client_DeleteProject(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1012,7 +1008,7 @@ func Test_client_ListProjectOperations(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1069,7 +1065,7 @@ func Test_client_ListProjectBranches(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1129,7 +1125,7 @@ func Test_client_CreateProjectBranch(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1189,7 +1185,7 @@ func Test_client_GetProjectBranch(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1252,7 +1248,7 @@ func Test_client_UpdateProjectBranch(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1312,7 +1308,7 @@ func Test_client_DeleteProjectBranch(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1372,7 +1368,7 @@ func Test_client_SetPrimaryProjectBranch(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1432,7 +1428,7 @@ func Test_client_ListProjectBranchEndpoints(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1492,7 +1488,7 @@ func Test_client_ListProjectBranchDatabases(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1555,7 +1551,7 @@ func Test_client_CreateProjectBranchDatabase(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1618,7 +1614,7 @@ func Test_client_GetProjectBranchDatabase(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1684,7 +1680,7 @@ func Test_client_UpdateProjectBranchDatabase(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1747,7 +1743,7 @@ func Test_client_DeleteProjectBranchDatabase(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1807,7 +1803,7 @@ func Test_client_ListProjectBranchRoles(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1870,7 +1866,7 @@ func Test_client_CreateProjectBranchRole(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1933,7 +1929,7 @@ func Test_client_GetProjectBranchRole(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -1996,7 +1992,7 @@ func Test_client_DeleteProjectBranchRole(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2059,7 +2055,7 @@ func Test_client_GetProjectBranchRolePassword(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2122,7 +2118,7 @@ func Test_client_ResetProjectBranchRolePassword(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2179,7 +2175,7 @@ func Test_client_ListProjectEndpoints(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2239,7 +2235,7 @@ func Test_client_CreateProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2299,7 +2295,7 @@ func Test_client_GetProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2362,7 +2358,7 @@ func Test_client_UpdateProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2422,7 +2418,7 @@ func Test_client_DeleteProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2482,7 +2478,7 @@ func Test_client_StartProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2542,7 +2538,7 @@ func Test_client_SuspendProjectEndpoint(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2602,7 +2598,7 @@ func Test_client_ListProjectsConsumption(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) } @@ -2649,7 +2645,7 @@ func Test_client_GetCurrentUserInfo(t *testing.T) { for _, tt := range tests { t.Run( tt.name, func(t *testing.T) { - c, err := NewClient(WithAPIKey(tt.apiKey), WithHTTPClient(NewMockHTTPClient())) + c, err := NewClient(Config{tt.apiKey, NewMockHTTPClient()}) if err != nil { panic(err) }