diff --git a/.env.example b/.env.example index 846feee..8a6919b 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ API_KEY="" TEST_POD_INDEX_NAME="" TEST_SERVERLESS_INDEX_NAME="" +ORG_API_KEY="" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6470966..4e27ab0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + - alpha jobs: build: @@ -21,4 +22,5 @@ jobs: env: TEST_POD_INDEX_NAME: ${{ secrets.TEST_POD_INDEX_NAME }} TEST_SERVERLESS_INDEX_NAME: ${{ secrets.TEST_SERVERLESS_INDEX_NAME }} - API_KEY: ${{ secrets.API_KEY }} \ No newline at end of file + API_KEY: ${{ secrets.API_KEY }} + ORG_API_KEY: ${{ secrets.ORG_API_KEY }} \ No newline at end of file diff --git a/apis b/apis index 3a31d37..474c95e 160000 --- a/apis +++ b/apis @@ -1 +1 @@ -Subproject commit 3a31d37fa6d5830b5bf674fe11b411caabc142e7 +Subproject commit 474c95ef30691bae36b7fd867dd935f817d08965 diff --git a/internal/gen/data/vector_service.pb.go b/internal/gen/data/vector_service.pb.go index b6eb6b9..1715f29 100644 --- a/internal/gen/data/vector_service.pb.go +++ b/internal/gen/data/vector_service.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.33.0 // protoc v3.12.4 // source: pinecone/data/v1/vector_service.proto diff --git a/internal/gen/management/management_plane.oas.go b/internal/gen/management/management_plane.oas.go new file mode 100644 index 0000000..9eedd2d --- /dev/null +++ b/internal/gen/management/management_plane.oas.go @@ -0,0 +1,1410 @@ +// Package management provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. +package management + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + ApiKeyAuthScopes = "ApiKeyAuth.Scopes" +) + +// Defines values for ErrorResponseErrorCode. +const ( + ABORTED ErrorResponseErrorCode = "ABORTED" + ALREADYEXISTS ErrorResponseErrorCode = "ALREADY_EXISTS" + BADPARAMS ErrorResponseErrorCode = "BAD_PARAMS" + CANCELLED ErrorResponseErrorCode = "CANCELLED" + DATALOSS ErrorResponseErrorCode = "DATA_LOSS" + DEADLINEEXCEEDED ErrorResponseErrorCode = "DEADLINE_EXCEEDED" + FAILEDPRECONDITION ErrorResponseErrorCode = "FAILED_PRECONDITION" + INTERNAL ErrorResponseErrorCode = "INTERNAL" + INVALIDARGUMENT ErrorResponseErrorCode = "INVALID_ARGUMENT" + NOTFOUND ErrorResponseErrorCode = "NOT_FOUND" + OUTOFRANGE ErrorResponseErrorCode = "OUT_OF_RANGE" + PERMISSIONDENIED ErrorResponseErrorCode = "PERMISSION_DENIED" + QUOTAEXCEEDED ErrorResponseErrorCode = "QUOTA_EXCEEDED" + RESOURCEEXHAUSTED ErrorResponseErrorCode = "RESOURCE_EXHAUSTED" + UNAUTHENTICATED ErrorResponseErrorCode = "UNAUTHENTICATED" + UNAVAILABLE ErrorResponseErrorCode = "UNAVAILABLE" + UNIMPLEMENTED ErrorResponseErrorCode = "UNIMPLEMENTED" + UNKNOWN ErrorResponseErrorCode = "UNKNOWN" +) + +// APIKeyWithSecret defines model for APIKeyWithSecret. +type APIKeyWithSecret struct { + // Id The unique ID of the API key. + Id openapi_types.UUID `json:"id"` + + // Name The name of the API key. + Name string `json:"name"` + + // ProjectId The ID of the project containing the API key. + ProjectId openapi_types.UUID `json:"project_id"` + + // Secret An API key secret for authenticating requests to Pinecone's Data Plane and Control Plane APIs. + Secret string `json:"secret"` +} + +// APIKeyWithoutSecret The details of an API key, without the secret. +type APIKeyWithoutSecret struct { + // Id The unique ID of the API key. + Id openapi_types.UUID `json:"id"` + + // Name The name of the API key. + Name string `json:"name"` + + // ProjectId The ID of the project containing the API key. + ProjectId openapi_types.UUID `json:"project_id"` +} + +// ErrorResponse The response shape used for all error responses +type ErrorResponse struct { + // Error Detailed information about the error that occurred + Error struct { + Code ErrorResponseErrorCode `json:"code"` + Details *map[string]interface{} `json:"details,omitempty"` + Message string `json:"message"` + } `json:"error"` + + // Status The HTTP status code of the error + Status int `json:"status"` +} + +// ErrorResponseErrorCode defines model for ErrorResponse.Error.Code. +type ErrorResponseErrorCode string + +// Project The details of a project. +type Project struct { + // Id The unique ID of the project. + Id openapi_types.UUID `json:"id"` + + // Name The name of the project. + Name string `json:"name"` +} + +// ApiKeyId defines model for api_key_id. +type ApiKeyId = openapi_types.UUID + +// ProjectId defines model for project_id. +type ProjectId = openapi_types.UUID + +// N400BadRequest The response shape used for all error responses +type N400BadRequest = ErrorResponse + +// N401Unauthorized The response shape used for all error responses +type N401Unauthorized = ErrorResponse + +// N403QuotaExceeded The response shape used for all error responses +type N403QuotaExceeded = ErrorResponse + +// N404NotFound The response shape used for all error responses +type N404NotFound = ErrorResponse + +// N4XXUnknownError The response shape used for all error responses +type N4XXUnknownError = ErrorResponse + +// N500InternalServerError The response shape used for all error responses +type N500InternalServerError = ErrorResponse + +// APIKeyCreationRequest defines model for APIKeyCreationRequest. +type APIKeyCreationRequest struct { + // Name The name of the API key. The name must be 3-7 characters long and consist only of alphanumeric characters. + Name string `json:"name"` +} + +// CreateProjectJSONBody defines parameters for CreateProject. +type CreateProjectJSONBody struct { + // Name The name of the project. The name must be 3-250 characters long. + Name string `json:"name"` +} + +// CreateApiKeyJSONBody defines parameters for CreateApiKey. +type CreateApiKeyJSONBody struct { + // Name The name of the API key. The name must be 3-7 characters long and consist only of alphanumeric characters. + Name string `json:"name"` +} + +// CreateProjectJSONRequestBody defines body for CreateProject for application/json ContentType. +type CreateProjectJSONRequestBody CreateProjectJSONBody + +// CreateApiKeyJSONRequestBody defines body for CreateApiKey for application/json ContentType. +type CreateApiKeyJSONRequestBody CreateApiKeyJSONBody + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // DeleteApiKey request + DeleteApiKey(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // FetchApiKey request + FetchApiKey(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListProjects request + ListProjects(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateProjectWithBody request with any body + CreateProjectWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + CreateProject(ctx context.Context, body CreateProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteProject request + DeleteProject(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // FetchProject request + FetchProject(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListApiKeys request + ListApiKeys(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateApiKeyWithBody request with any body + CreateApiKeyWithBody(ctx context.Context, projectId ProjectId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + CreateApiKey(ctx context.Context, projectId ProjectId, body CreateApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) DeleteApiKey(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteApiKeyRequest(c.Server, apiKeyId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) FetchApiKey(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFetchApiKeyRequest(c.Server, apiKeyId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListProjects(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListProjectsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateProjectWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateProjectRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateProject(ctx context.Context, body CreateProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateProjectRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) DeleteProject(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteProjectRequest(c.Server, projectId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) FetchProject(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFetchProjectRequest(c.Server, projectId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListApiKeys(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListApiKeysRequest(c.Server, projectId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateApiKeyWithBody(ctx context.Context, projectId ProjectId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateApiKeyRequestWithBody(c.Server, projectId, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateApiKey(ctx context.Context, projectId ProjectId, body CreateApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateApiKeyRequest(c.Server, projectId, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewDeleteApiKeyRequest generates requests for DeleteApiKey +func NewDeleteApiKeyRequest(server string, apiKeyId ApiKeyId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "api_key_id", runtime.ParamLocationPath, apiKeyId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api-keys/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewFetchApiKeyRequest generates requests for FetchApiKey +func NewFetchApiKeyRequest(server string, apiKeyId ApiKeyId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "api_key_id", runtime.ParamLocationPath, apiKeyId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api-keys/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListProjectsRequest generates requests for ListProjects +func NewListProjectsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateProjectRequest calls the generic CreateProject builder with application/json body +func NewCreateProjectRequest(server string, body CreateProjectJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateProjectRequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateProjectRequestWithBody generates requests for CreateProject with any type of body +func NewCreateProjectRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteProjectRequest generates requests for DeleteProject +func NewDeleteProjectRequest(server string, projectId ProjectId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "project_id", runtime.ParamLocationPath, projectId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewFetchProjectRequest generates requests for FetchProject +func NewFetchProjectRequest(server string, projectId ProjectId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "project_id", runtime.ParamLocationPath, projectId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListApiKeysRequest generates requests for ListApiKeys +func NewListApiKeysRequest(server string, projectId ProjectId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "project_id", runtime.ParamLocationPath, projectId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects/%s/api-keys", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateApiKeyRequest calls the generic CreateApiKey builder with application/json body +func NewCreateApiKeyRequest(server string, projectId ProjectId, body CreateApiKeyJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateApiKeyRequestWithBody(server, projectId, "application/json", bodyReader) +} + +// NewCreateApiKeyRequestWithBody generates requests for CreateApiKey with any type of body +func NewCreateApiKeyRequestWithBody(server string, projectId ProjectId, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "project_id", runtime.ParamLocationPath, projectId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/projects/%s/api-keys", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // DeleteApiKeyWithResponse request + DeleteApiKeyWithResponse(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*DeleteApiKeyResponse, error) + + // FetchApiKeyWithResponse request + FetchApiKeyWithResponse(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*FetchApiKeyResponse, error) + + // ListProjectsWithResponse request + ListProjectsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListProjectsResponse, error) + + // CreateProjectWithBodyWithResponse request with any body + CreateProjectWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProjectResponse, error) + + CreateProjectWithResponse(ctx context.Context, body CreateProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProjectResponse, error) + + // DeleteProjectWithResponse request + DeleteProjectWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*DeleteProjectResponse, error) + + // FetchProjectWithResponse request + FetchProjectWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*FetchProjectResponse, error) + + // ListApiKeysWithResponse request + ListApiKeysWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*ListApiKeysResponse, error) + + // CreateApiKeyWithBodyWithResponse request with any body + CreateApiKeyWithBodyWithResponse(ctx context.Context, projectId ProjectId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateApiKeyResponse, error) + + CreateApiKeyWithResponse(ctx context.Context, projectId ProjectId, body CreateApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateApiKeyResponse, error) +} + +type DeleteApiKeyResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401Unauthorized + JSON404 *N404NotFound + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r DeleteApiKeyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteApiKeyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type FetchApiKeyResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *APIKeyWithoutSecret + JSON401 *N401Unauthorized + JSON404 *N404NotFound + JSON4XX *N4XXUnknownError + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r FetchApiKeyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FetchApiKeyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ListProjectsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Data *[]Project `json:"data,omitempty"` + } + JSON401 *N401Unauthorized + JSON4XX *N4XXUnknownError + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r ListProjectsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListProjectsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CreateProjectResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *Project + JSON400 *N400BadRequest + JSON401 *N401Unauthorized + JSON403 *N403QuotaExceeded + JSON4XX *N4XXUnknownError +} + +// Status returns HTTPResponse.Status +func (r CreateProjectResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateProjectResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type DeleteProjectResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401Unauthorized + JSON404 *N404NotFound + JSON4XX *N4XXUnknownError + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r DeleteProjectResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteProjectResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type FetchProjectResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Project + JSON401 *N401Unauthorized + JSON404 *N404NotFound + JSON4XX *N4XXUnknownError + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r FetchProjectResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FetchProjectResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ListApiKeysResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Data *[]APIKeyWithoutSecret `json:"data,omitempty"` + } + JSON401 *N401Unauthorized + JSON4XX *N4XXUnknownError + JSON500 *N500InternalServerError +} + +// Status returns HTTPResponse.Status +func (r ListApiKeysResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListApiKeysResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CreateApiKeyResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *APIKeyWithSecret + JSON400 *N400BadRequest + JSON401 *N401Unauthorized + JSON403 *N403QuotaExceeded +} + +// Status returns HTTPResponse.Status +func (r CreateApiKeyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateApiKeyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// DeleteApiKeyWithResponse request returning *DeleteApiKeyResponse +func (c *ClientWithResponses) DeleteApiKeyWithResponse(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*DeleteApiKeyResponse, error) { + rsp, err := c.DeleteApiKey(ctx, apiKeyId, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteApiKeyResponse(rsp) +} + +// FetchApiKeyWithResponse request returning *FetchApiKeyResponse +func (c *ClientWithResponses) FetchApiKeyWithResponse(ctx context.Context, apiKeyId ApiKeyId, reqEditors ...RequestEditorFn) (*FetchApiKeyResponse, error) { + rsp, err := c.FetchApiKey(ctx, apiKeyId, reqEditors...) + if err != nil { + return nil, err + } + return ParseFetchApiKeyResponse(rsp) +} + +// ListProjectsWithResponse request returning *ListProjectsResponse +func (c *ClientWithResponses) ListProjectsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListProjectsResponse, error) { + rsp, err := c.ListProjects(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseListProjectsResponse(rsp) +} + +// CreateProjectWithBodyWithResponse request with arbitrary body returning *CreateProjectResponse +func (c *ClientWithResponses) CreateProjectWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProjectResponse, error) { + rsp, err := c.CreateProjectWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateProjectResponse(rsp) +} + +func (c *ClientWithResponses) CreateProjectWithResponse(ctx context.Context, body CreateProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProjectResponse, error) { + rsp, err := c.CreateProject(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateProjectResponse(rsp) +} + +// DeleteProjectWithResponse request returning *DeleteProjectResponse +func (c *ClientWithResponses) DeleteProjectWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*DeleteProjectResponse, error) { + rsp, err := c.DeleteProject(ctx, projectId, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteProjectResponse(rsp) +} + +// FetchProjectWithResponse request returning *FetchProjectResponse +func (c *ClientWithResponses) FetchProjectWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*FetchProjectResponse, error) { + rsp, err := c.FetchProject(ctx, projectId, reqEditors...) + if err != nil { + return nil, err + } + return ParseFetchProjectResponse(rsp) +} + +// ListApiKeysWithResponse request returning *ListApiKeysResponse +func (c *ClientWithResponses) ListApiKeysWithResponse(ctx context.Context, projectId ProjectId, reqEditors ...RequestEditorFn) (*ListApiKeysResponse, error) { + rsp, err := c.ListApiKeys(ctx, projectId, reqEditors...) + if err != nil { + return nil, err + } + return ParseListApiKeysResponse(rsp) +} + +// CreateApiKeyWithBodyWithResponse request with arbitrary body returning *CreateApiKeyResponse +func (c *ClientWithResponses) CreateApiKeyWithBodyWithResponse(ctx context.Context, projectId ProjectId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateApiKeyResponse, error) { + rsp, err := c.CreateApiKeyWithBody(ctx, projectId, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateApiKeyResponse(rsp) +} + +func (c *ClientWithResponses) CreateApiKeyWithResponse(ctx context.Context, projectId ProjectId, body CreateApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateApiKeyResponse, error) { + rsp, err := c.CreateApiKey(ctx, projectId, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateApiKeyResponse(rsp) +} + +// ParseDeleteApiKeyResponse parses an HTTP response from a DeleteApiKeyWithResponse call +func ParseDeleteApiKeyResponse(rsp *http.Response) (*DeleteApiKeyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteApiKeyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseFetchApiKeyResponse parses an HTTP response from a FetchApiKeyWithResponse call +func ParseFetchApiKeyResponse(rsp *http.Response) (*FetchApiKeyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FetchApiKeyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest APIKeyWithoutSecret + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListProjectsResponse parses an HTTP response from a ListProjectsWithResponse call +func ParseListProjectsResponse(rsp *http.Response) (*ListProjectsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListProjectsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Data *[]Project `json:"data,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseCreateProjectResponse parses an HTTP response from a CreateProjectWithResponse call +func ParseCreateProjectResponse(rsp *http.Response) (*CreateProjectResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateProjectResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest Project + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403QuotaExceeded + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + } + + return response, nil +} + +// ParseDeleteProjectResponse parses an HTTP response from a DeleteProjectWithResponse call +func ParseDeleteProjectResponse(rsp *http.Response) (*DeleteProjectResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteProjectResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseFetchProjectResponse parses an HTTP response from a FetchProjectWithResponse call +func ParseFetchProjectResponse(rsp *http.Response) (*FetchProjectResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FetchProjectResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Project + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListApiKeysResponse parses an HTTP response from a ListApiKeysWithResponse call +func ParseListApiKeysResponse(rsp *http.Response) (*ListApiKeysResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListApiKeysResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Data *[]APIKeyWithoutSecret `json:"data,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode/100 == 4: + var dest N4XXUnknownError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON4XX = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseCreateApiKeyResponse parses an HTTP response from a CreateApiKeyWithResponse call +func ParseCreateApiKeyResponse(rsp *http.Response) (*CreateApiKeyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateApiKeyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest APIKeyWithSecret + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403QuotaExceeded + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + } + + return response, nil +} diff --git a/justfile b/justfile index 2196223..13ff0be 100644 --- a/justfile +++ b/justfile @@ -12,6 +12,7 @@ bootstrap: gen: protoc --experimental_allow_proto3_optional --proto_path=apis/proto --go_opt=module="github.com/pinecone-io/go-pinecone" --go-grpc_opt=module="github.com/pinecone-io/go-pinecone" --go_out=. --go-grpc_out=. apis/proto/pinecone/data/v1/vector_service.proto oapi-codegen --package=control --generate types,client apis/openapi/control/v1/control_v1.yaml > internal/gen/control/control_plane.oas.go + oapi-codegen --package=management --generate types,client apis/openapi/management/v1/management_v1alpha.yaml > internal/gen/management/management_plane.oas.go docs: @echo "Serving docs at http://localhost:6060/pkg/github.com/pinecone-io/go-pinecone/pinecone/" @godoc -http=:6060 >/dev/null diff --git a/pinecone/management_client.go b/pinecone/management_client.go new file mode 100644 index 0000000..0caa88f --- /dev/null +++ b/pinecone/management_client.go @@ -0,0 +1,500 @@ +package pinecone + +import ( + "context" + "fmt" + "github.com/deepmap/oapi-codegen/v2/pkg/securityprovider" + "github.com/google/uuid" + "github.com/pinecone-io/go-pinecone/internal/gen/management" + "github.com/pinecone-io/go-pinecone/internal/provider" + "github.com/pinecone-io/go-pinecone/internal/useragent" + "net/http" +) + +// ManagementClient provides a high-level interface for interacting with the +// Pinecone management plane API. It encapsulates the necessary authentication, +// request creation, and response handling for the API's operations. +// +// The ManagementClient is designed to simplify the management of projects and +// their API keys by abstracting away the direct handling of HTTP requests and +// responses. It leverages a generated low-level client (`restClient`) for +// communication with the API, ensuring that requests are properly authenticated +// and formatted according to the API's specification. +// +// Fields: +// - apiKey: The API key used for authenticating requests to the management API. +// This key should have the necessary organization-level permissions for the operations +// you intend to perform. +// - restClient: An instance of the generated low-level client that actually performs +// HTTP requests to the management API. This field is internal and managed +// by the ManagementClient. +// - sourceTag: An optional string used to help Pinecone attribute API activity to our partners. +// +// To use ManagementClient, first instantiate it using the NewManagementClient function, +// providing it with the necessary configuration. Once instantiated, you can call its +// methods to perform actions such as listing, creating, getting, and deleting projects +// and project API keys. +// +// Example: +// +// clientParams := NewManagementClientParams{ +// ApiKey: "your_api_key_here", +// SourceTag: "your_source_identifier", // optional +// } +// managementClient, err := NewManagementClient(clientParams) +// if err != nil { +// log.Fatalf("Failed to create management client: %v", err) +// } +// // Now you can use managementClient to interact with the API +// +// Note that ManagementClient methods are designed to be safe for concurrent use by multiple +// goroutines, assuming that its configuration (e.g., the API key) is not modified after +// initialization. + +type ManagementClient struct { + apiKey string + restClient *management.ClientWithResponses + sourceTag string +} + +// NewManagementClientParams holds the parameters for creating a new ManagementClient. +type NewManagementClientParams struct { + ApiKey string + ApiEndpoint string // optional + SourceTag string // optional +} + +// NewManagementClient creates and initializes a new instance of ManagementClient. +// This method sets up the management plane client with the necessary configuration for +// authentication and communication with the management API. +// +// The method requires an input parameter of type NewManagementClientParams, which includes: +// - ApiKey: The API key used for authenticating requests to the management API. +// This key should have the necessary organization-level permissions for the operations +// you intend to perform. +// - ApiEndpoint: An optional string denoting an alternate endpoint to https://api.pinecone.io/management/v1alpha. +// Mainly used in testing. +// - SourceTag: An optional string used to help Pinecone attribute API activity to our partners. +// For more info, see https://docs.pinecone.io/integrations/build-integration/attribute-api-activity +// +// Returns a pointer to an initialized ManagementClient instance on success. In case of +// failure, it returns nil and an error describing the issue encountered. Possible errors +// include issues with setting up the API key provider or problems initializing the +// underlying REST client. +// +// Example: +// +// clientParams := NewManagementClientParams{ +// ApiKey: "your_api_key_here", +// } +// managementClient, err := NewManagementClient(clientParams) +// if err != nil { +// log.Fatalf("Failed to create management client: %v", err) +// } +// // Use managementClient to interact with the management API +// +// It is important to handle the error returned by this method to ensure that the +// management client has been created successfully before attempting to make API calls. +func NewManagementClient(in NewManagementClientParams) (*ManagementClient, error) { + apiKeyProvider, err := securityprovider.NewSecurityProviderApiKey("header", "Api-Key", in.ApiKey) + if err != nil { + return nil, err + } + + userAgentProvider := provider.NewHeaderProvider("User-Agent", useragent.BuildUserAgent(in.SourceTag)) + + apiEndpoint := in.ApiEndpoint + if apiEndpoint == "" { + apiEndpoint = "https://api.pinecone.io/management/v1alpha" + } + client, err := management.NewClientWithResponses(apiEndpoint, + management.WithRequestEditorFn(apiKeyProvider.Intercept), + management.WithRequestEditorFn(userAgentProvider.Intercept), + ) + if err != nil { + return nil, err + } + + c := ManagementClient{apiKey: in.ApiKey, restClient: client, sourceTag: in.SourceTag} + return &c, nil +} + +// ListProjects retrieves all projects from the management API. It makes a call to the +// management plane's ListProjects endpoint and returns a slice of Project pointers. +// +// The method handles various HTTP response codes from the API, including: +// - 401 Unauthorized: Returned if the API key is invalid or missing. +// - 500 Internal Server Error: Indicates a server-side error. It might be temporary. +// - 4XX: Covers other client-side errors not explicitly handled by other conditions. +// +// Context (ctx) is used to control the request's lifetime. It allows for the request +// to be canceled or to timeout according to the context's deadline. +// +// Returns a slice of pointers to Project structs populated with the project data +// on success. In case of failure, it returns an error describing the issue encountered. +// It's important to check the returned error to understand the outcome of the request. +// +// Example: +// +// projects, err := managementClient.ListProjects(ctx) +// if err != nil { +// log.Fatalf("Failed to list projects: %v", err) +// } +// for _, project := range projects { +// fmt.Printf("Project ID: %s, Name: %s\n", project.Id, project.Name) +// } +func (c *ManagementClient) ListProjects(ctx context.Context) ([]*Project, error) { + resp, err := c.restClient.ListProjectsWithResponse(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list projects: %w", err) + } + + switch resp.StatusCode() { + case http.StatusOK: + if resp.JSON200 != nil && resp.JSON200.Data != nil { + projectList := make([]*Project, len(*resp.JSON200.Data)) + for i, p := range *resp.JSON200.Data { + project := Project{ + Id: p.Id, + Name: p.Name, + } + projectList[i] = &project + } + return projectList, nil + } + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusInternalServerError: + return nil, fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } + + return nil, fmt.Errorf("unexpected response format or empty data") +} + +// FetchProject retrieves a project by its ID from the management API. It makes a call to the +// management plane's FetchProject endpoint and returns the project details. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime. +// - projectId: A string representing the unique identifier of the project to retrieve. +// +// Returns the project details on success or an error if the operation fails. Possible errors +// include unauthorized access, project not found, internal server errors, or other HTTP client +// errors. +// +// Example: +// +// project, err := managementClient.FetchProject(ctx, "your_project_id_here") +// if err != nil { +// log.Fatalf("Failed to fetch project: %v", err) +// } +// fmt.Printf("Project ID: %s, Name: %s\n", project.Id, project.Name) +func (c *ManagementClient) FetchProject(ctx context.Context, projectId uuid.UUID) (*Project, error) { + resp, err := c.restClient.FetchProjectWithResponse(ctx, projectId) + if err != nil { + return nil, fmt.Errorf("failed to fetch project: %w", err) + } + + switch resp.StatusCode() { + case http.StatusOK: + if resp.JSON200 != nil { + return &Project{ + Id: resp.JSON200.Id, + Name: resp.JSON200.Name, + }, nil + } + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusNotFound: + return nil, fmt.Errorf("project not found: %v", resp.JSON404) + case http.StatusInternalServerError: + return nil, fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } + + return nil, fmt.Errorf("unexpected response format or empty data") +} + +// CreateProject creates a new project in the management API. It sends a request to the +// management plane's CreateProject endpoint with the project details. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime. +// - projectName: A string representing the name of the project to create. +// +// Returns the created project's details on success or an error if the creation fails. +// Possible errors include unauthorized access, validation errors, internal server errors, +// or other HTTP client errors. +// +// Example: +// +// project, err := managementClient.CreateProject(ctx, "New Project Name") +// if err != nil { +// log.Fatalf("Failed to create project: %v", err) +// } +// fmt.Printf("Created Project ID: %s, Name: %s\n", project.Id, project.Name) +func (c *ManagementClient) CreateProject(ctx context.Context, projectName string) (*Project, error) { + body := management.CreateProjectJSONRequestBody{ + Name: projectName, + } + + resp, err := c.restClient.CreateProjectWithResponse(ctx, body) + if err != nil { + return nil, fmt.Errorf("failed to create project: %w", err) + } + + switch resp.StatusCode() { + case http.StatusCreated: + if resp.JSON201 != nil { + return &Project{ + Id: resp.JSON201.Id, + Name: resp.JSON201.Name, + }, nil + } + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusBadRequest: + return nil, fmt.Errorf("bad request: %v", resp.JSON400) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } + + return nil, fmt.Errorf("unexpected response format or empty data") +} + +// DeleteProject deletes a project by its ID from the management API. It makes a call to the +// management plane's DeleteProject endpoint. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime. +// - projectId: A string representing the unique identifier of the project to delete. +// +// Returns an error if the deletion fails. Possible errors include unauthorized access, +// project not found, internal server errors, or other HTTP client errors. +// +// Example: +// +// err := managementClient.DeleteProject(ctx, "your_project_id_here") +// if err != nil { +// log.Fatalf("Failed to delete project: %v", err) +// } +func (c *ManagementClient) DeleteProject(ctx context.Context, projectId uuid.UUID) error { + resp, err := c.restClient.DeleteProjectWithResponse(ctx, projectId) + if err != nil { + return fmt.Errorf("failed to delete project: %w", err) + } + + switch resp.StatusCode() { + case http.StatusOK, http.StatusAccepted, http.StatusNoContent: + return nil // Success case + case http.StatusUnauthorized: + return fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusNotFound: + return fmt.Errorf("project not found: %v", resp.JSON404) + case http.StatusInternalServerError: + return fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } +} + +// ListApiKeys retrieves all API keys associated with a specific project from the management API. +// It sends a request to the management plane's ListApiKeys endpoint and returns a slice of APIKeyWithoutSecret pointers. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime. +// - projectId: A UUID representing the unique identifier of the project whose API keys are to be listed. +// +// Returns a slice of pointers to APIKeyWithoutSecret structs populated with the API key data +// on success. In case of failure, it returns an error describing the issue encountered. This could be due +// to unauthorized access, project not found, internal server errors, or other HTTP client errors. +// +// Example: +// +// apiKeys, err := managementClient.ListApiKeys(ctx, projectId) +// if err != nil { +// log.Fatalf("Failed to list API keys: %v", err) +// } +// for _, apiKey := range apiKeys { +// fmt.Printf("API Key ID: %s, Name: %s\n", apiKey.Id, apiKey.Name) +// } +func (c *ManagementClient) ListApiKeys(ctx context.Context, projectId uuid.UUID) ([]*APIKeyWithoutSecret, error) { + resp, err := c.restClient.ListApiKeysWithResponse(ctx, projectId) + if err != nil { + return nil, fmt.Errorf("failed to list API keys: %w", err) + } + + // Handle various HTTP response codes and errors + if resp.JSON200 != nil { + apiKeys := make([]*APIKeyWithoutSecret, len(*resp.JSON200.Data)) + for i, key := range *resp.JSON200.Data { + apiKeys[i] = &APIKeyWithoutSecret{ + Id: key.Id, + Name: key.Name, + ProjectId: key.ProjectId, + } + } + return apiKeys, nil + } + + // Detailed error handling based on status code + switch resp.StatusCode() { + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusInternalServerError: + return nil, fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } +} + +// FetchApiKey retrieves the details of a specific API key by its ID from the management API. +// It sends a request to the management plane's FetchApiKey endpoint and returns the API key details, +// excluding its secret for security reasons. This function is designed to provide information about +// an API key, such as its name and associated project, without compromising sensitive information. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime, allowing for request cancellation and timeouts. +// - apiKeyId: The UUID representing the unique identifier of the API key to retrieve. +// +// Returns a pointer to an APIKeyWithoutSecret struct populated with the API key's details on success, +// or an error if the operation fails. Possible errors include unauthorized access if the API key used for the +// request doesn't have sufficient permissions, the API key not being found, internal server errors, +// or other HTTP client errors. +// +// Example usage: +// +// apiKeyDetails, err := managementClient.FetchApiKey(ctx, apiKeyId) +// if err != nil { +// log.Fatalf("Failed to fetch API key details: %v", err) +// } +// fmt.Printf("API Key ID: %s, Name: %s\n", apiKeyDetails.Id, apiKeyDetails.Name) +func (c *ManagementClient) FetchApiKey(ctx context.Context, apiKeyId uuid.UUID) (*APIKeyWithoutSecret, error) { + resp, err := c.restClient.FetchApiKeyWithResponse(ctx, apiKeyId) + if err != nil { + return nil, fmt.Errorf("failed to fetch API key: %w", err) + } + + switch resp.StatusCode() { + case http.StatusOK: + if resp.JSON200 != nil { + return &APIKeyWithoutSecret{ + Id: resp.JSON200.Id, + Name: resp.JSON200.Name, + ProjectId: resp.JSON200.ProjectId, + }, nil + } + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusNotFound: + return nil, fmt.Errorf("API key not found: %v", resp.JSON404) + case http.StatusInternalServerError: + return nil, fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } + + return nil, fmt.Errorf("unexpected response format or empty data") +} + +// CreateApiKey creates a new API key for a given project in the management API. +// It sends a request to the management plane's CreateApiKey endpoint with the necessary +// details and returns the newly created API key's information, including the secret. +// +// This function is critical for enabling secure access to the management and data plane APIs, +// allowing for the creation of scoped access keys associated with specific projects. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime, enabling request cancellation and timeouts. +// - projectId: The UUID of the project within which the new API key will be created. +// - apiKeyName: A string representing the desired name for the new API key. This name helps identify +// the API key within the project scope and should be unique. +// +// Returns a pointer to an APIKeyWithSecret struct populated with the details of the newly created API key +// on success, or an error if the operation fails. Possible errors include unauthorized access if the provided +// API key does not have sufficient permissions, validation errors for incorrect input values, +// internal server errors, or other HTTP client errors. +// +// Example usage: +// +// newApiKey, err := managementClient.CreateApiKey(ctx, projectId, "NewAPIKeyName") +// if err != nil { +// log.Fatalf("Failed to create API key: %v", err) +// } +// fmt.Printf("Created API Key ID: %s, Name: %s, Project ID: %s, Secret: %s\n", +// newApiKey.Id, newApiKey.Name, newApiKey.ProjectId, newApiKey.Secret) +func (c *ManagementClient) CreateApiKey(ctx context.Context, projectId uuid.UUID, apiKeyName string) (*APIKeyWithSecret, error) { + body := management.CreateApiKeyJSONRequestBody{ + Name: apiKeyName, + } + + resp, err := c.restClient.CreateApiKeyWithResponse(ctx, projectId, body) + if err != nil { + return nil, fmt.Errorf("failed to create API key: %w", err) + } + + switch resp.StatusCode() { + case http.StatusCreated: + if resp.JSON201 != nil { + return &APIKeyWithSecret{ + Id: resp.JSON201.Id, + Name: resp.JSON201.Name, + ProjectId: projectId, + Secret: resp.JSON201.Secret, + }, nil + } + case http.StatusUnauthorized: + return nil, fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusBadRequest: + return nil, fmt.Errorf("bad request: %v", resp.JSON400) + default: + return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } + + return nil, fmt.Errorf("unexpected response format or empty data") +} + +// DeleteApiKey deletes an API key by its ID from the management API. This method sends a request +// to the management plane's DeleteApiKey endpoint to permanently remove the specified API key, +// revoking any access it provided. +// +// This operation is essential for managing the lifecycle and security of API keys by allowing +// for the removal of keys that are obsolete or should no longer have access to the API. +// +// Parameters: +// - ctx: A context.Context to control the request's lifetime, enabling features like request cancellation +// and timeouts to handle slow or unresponsive network conditions. +// - apiKeyId: The UUID representing the unique identifier of the API key to be deleted. +// +// Returns an error if the deletion operation fails, providing insight into the failure. Possible +// errors include unauthorized access if the caller lacks sufficient permissions, the specified API key +// not being found, internal server errors, or other HTTP client errors. +// +// Example usage: +// +// err := managementClient.DeleteApiKey(ctx, apiKeyId) +// if err != nil { +// log.Fatalf("Failed to delete API key: %v", err) +// } +func (c *ManagementClient) DeleteApiKey(ctx context.Context, apiKeyId uuid.UUID) error { + resp, err := c.restClient.DeleteApiKeyWithResponse(ctx, apiKeyId) + if err != nil { + return fmt.Errorf("failed to delete API key: %w", err) + } + + switch resp.StatusCode() { + case http.StatusOK, http.StatusAccepted, http.StatusNoContent: + return nil + case http.StatusUnauthorized: + return fmt.Errorf("unauthorized: %v", resp.JSON401) + case http.StatusNotFound: + return fmt.Errorf("API key not found: %v", resp.JSON404) + case http.StatusInternalServerError: + return fmt.Errorf("internal server error: %v", resp.JSON500) + default: + return fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode(), string(resp.Body)) + } +} diff --git a/pinecone/management_client_test.go b/pinecone/management_client_test.go new file mode 100644 index 0000000..942092c --- /dev/null +++ b/pinecone/management_client_test.go @@ -0,0 +1,199 @@ +package pinecone + +import ( + "context" + "fmt" + "github.com/stretchr/testify/suite" + "math/rand" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +type ManagementClientTests struct { + suite.Suite + client ManagementClient + clientSourceTag ManagementClient + project Project + apiKey APIKeyWithoutSecret + sourceTag string +} + +func TestManagementClient(t *testing.T) { + suite.Run(t, new(ManagementClientTests)) +} + +func (ts *ManagementClientTests) SetupSuite() { + apiKey := os.Getenv("ORG_API_KEY") + require.NotEmpty(ts.T(), apiKey, "ORG_API_KEY env variable not set") + + client, err := NewManagementClient(NewManagementClientParams{ApiKey: apiKey}) + if err != nil { + ts.FailNow(err.Error()) + } + ts.client = *client + + ts.sourceTag = "test_source_tag" + clientSourceTag, err := NewManagementClient(NewManagementClientParams{ApiKey: apiKey, SourceTag: ts.sourceTag}) + if err != nil { + ts.FailNow(err.Error()) + } + ts.clientSourceTag = *clientSourceTag + + projects, err := ts.client.ListProjects(context.Background()) + require.NoError(ts.T(), err, "Failed to list projects for test setup") + require.Greater(ts.T(), len(projects), 0, "Projects list should not be empty") + ts.project = *projects[0] + + apiKeys, err := ts.client.ListApiKeys(context.Background(), ts.project.Id) + require.NoError(ts.T(), err, "Failed to list API keys for test setup") + require.NotNil(ts.T(), apiKeys, "API keys in test setup should not be nil") + require.Greater(ts.T(), len(apiKeys), 0, "API key list in test setup should not be empty") + ts.apiKey = *apiKeys[0] +} + +func (ts *ManagementClientTests) TestNewClientParamsSet() { + apiKey := "test-api-key" + client, err := NewManagementClient(NewManagementClientParams{ApiKey: apiKey}) + if err != nil { + ts.FailNow(err.Error()) + } + if client.apiKey != apiKey { + ts.FailNow(fmt.Sprintf("Expected client to have apiKey '%s', but got '%s'", apiKey, client.apiKey)) + } + if client.sourceTag != "" { + ts.FailNow(fmt.Sprintf("Expected client to have empty sourceTag, but got '%s'", client.sourceTag)) + } +} + +func (ts *ManagementClientTests) TestNewClientParamsSetSourceTag() { + apiKey := "test-api-key" + sourceTag := "test-source-tag" + client, err := NewManagementClient(NewManagementClientParams{ApiKey: apiKey, SourceTag: sourceTag}) + if err != nil { + ts.FailNow(err.Error()) + } + if client.apiKey != apiKey { + ts.FailNow(fmt.Sprintf("Expected client to have apiKey '%s', but got '%s'", apiKey, client.apiKey)) + } + if client.sourceTag != sourceTag { + ts.FailNow(fmt.Sprintf("Expected client to have sourceTag '%s', but got '%s'", sourceTag, client.sourceTag)) + } +} + +func (ts *ManagementClientTests) TestListProjects() { + projects, err := ts.client.ListProjects(context.Background()) + require.NoError(ts.T(), err, "Failed to list projects") + require.Greater(ts.T(), len(projects), 0, "Projects list should not be empty") +} + +func (ts *ManagementClientTests) TestListProjectsSourceTag() { + projects, err := ts.clientSourceTag.ListProjects(context.Background()) + require.NoError(ts.T(), err, "Failed to list projects") + require.Greater(ts.T(), len(projects), 0, "Projects list should not be empty") +} + +func (ts *ManagementClientTests) TestFetchProject() { + testProjectID := ts.project.Id + + project, err := ts.client.FetchProject(context.Background(), testProjectID) + require.NoError(ts.T(), err, "Failed to fetch project") + + require.NotNil(ts.T(), project, "Fetched project should not be nil") + require.Equal(ts.T(), testProjectID, project.Id, "Fetched project ID should match the requested ID") + require.Equal(ts.T(), ts.project.Name, project.Name, "Fetched project name should match the expected name") +} + +func (ts *ManagementClientTests) TestCreateProject() { + projectName := "TestProject_" + fmt.Sprint(time.Now().UnixNano()) + + createdProject, err := ts.client.CreateProject(context.Background(), projectName) + defer func() { + if createdProject != nil { + delErr := ts.client.DeleteProject(context.Background(), createdProject.Id) + require.NoError(ts.T(), delErr, "Failed to clean up project") + } + }() + + require.NoError(ts.T(), err, "Failed to create project") + require.NotNil(ts.T(), createdProject, "Created project should not be nil") + require.Equal(ts.T(), projectName, createdProject.Name, "Created project name should match") +} + +func (ts *ManagementClientTests) TestDeleteProject() { + // Create a project to delete + projectName := "TestProjectForDeletion_" + fmt.Sprint(time.Now().UnixNano()) + projectToDelete, err := ts.client.CreateProject(context.Background(), projectName) + require.NoError(ts.T(), err, "Failed to create project for deletion") + require.NotNil(ts.T(), projectToDelete, "Project for deletion should not be nil") + + // Attempt to delete the project + err = ts.client.DeleteProject(context.Background(), projectToDelete.Id) + require.NoError(ts.T(), err, "Failed to delete project") + + // Verify deletion by attempting to fetch the deleted project + _, fetchErr := ts.client.FetchProject(context.Background(), projectToDelete.Id) + require.Error(ts.T(), fetchErr, "Expected an error when fetching a deleted project") +} + +func (ts *ManagementClientTests) TestListApiKeys() { + apiKeys, err := ts.client.ListApiKeys(context.Background(), ts.project.Id) + require.NoError(ts.T(), err, "Failed to list API keys") + require.NotNil(ts.T(), apiKeys, "API keys should not be nil") + require.Greater(ts.T(), len(apiKeys), 0, "API key list should not be empty") +} + +func (ts *ManagementClientTests) TestFetchApiKey() { + apiKeyDetails, err := ts.client.FetchApiKey(context.Background(), ts.apiKey.Id) + require.NoError(ts.T(), err, "Failed to fetch API key details") + require.NotNil(ts.T(), apiKeyDetails, "API key details should not be nil") + require.Equal(ts.T(), apiKeyDetails.Id, ts.apiKey.Id, "API key ID should match") + require.Equal(ts.T(), apiKeyDetails.Name, ts.apiKey.Name, "API key Name should match") + require.Equal(ts.T(), apiKeyDetails.ProjectId, ts.apiKey.ProjectId, "API key's Project ID should match") +} + +func (ts *ManagementClientTests) TestFetchApiKeySourceTag() { + apiKeyDetails, err := ts.clientSourceTag.FetchApiKey(context.Background(), ts.apiKey.Id) + require.NoError(ts.T(), err, "Failed to fetch API key details") + require.NotNil(ts.T(), apiKeyDetails, "API key details should not be nil") + require.Equal(ts.T(), apiKeyDetails.Id, ts.apiKey.Id, "API key ID should match") + require.Equal(ts.T(), apiKeyDetails.Name, ts.apiKey.Name, "API key Name should match") + require.Equal(ts.T(), apiKeyDetails.ProjectId, ts.apiKey.ProjectId, "API key's Project ID should match") +} + +func (ts *ManagementClientTests) TestCreateApiKey() { + apiKeyName := generateRandomString(6) // current limitation of Alpha-release + newApiKey, err := ts.client.CreateApiKey(context.Background(), ts.project.Id, apiKeyName) + defer func() { + if newApiKey != nil { + delErr := ts.client.DeleteApiKey(context.Background(), newApiKey.Id) + require.NoError(ts.T(), delErr, "Failed to clean up api key") + } + }() + + require.NoError(ts.T(), err, "Failed to create API key") + require.NotNil(ts.T(), newApiKey, "Newly created API key should not be nil") + require.Equal(ts.T(), apiKeyName, newApiKey.Name, "API key name should match") + require.NotEmpty(ts.T(), newApiKey.Secret, "Newly created API key should have a secret") +} + +func (ts *ManagementClientTests) TestDeleteApiKey() { + // Create an API key to delete + apiKeyName := generateRandomString(6) // current limitation of Alpha-release + apiKey, _ := ts.client.CreateApiKey(context.Background(), ts.project.Id, apiKeyName) + err := ts.client.DeleteApiKey(context.Background(), apiKey.Id) + require.NoError(ts.T(), err, "Failed to delete API key") +} + +func generateRandomString(length int) string { + const charset = "abcdefghijklmnopqrstuvwxyz" + var seededRand = rand.New(rand.NewSource(time.Now().UnixNano())) + + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} diff --git a/pinecone/models.go b/pinecone/models.go index 4d00443..14ccde3 100644 --- a/pinecone/models.go +++ b/pinecone/models.go @@ -1,6 +1,9 @@ package pinecone -import "google.golang.org/protobuf/types/known/structpb" +import ( + "github.com/google/uuid" + "google.golang.org/protobuf/types/known/structpb" +) type IndexMetric string @@ -113,3 +116,43 @@ type Usage struct { type Filter = structpb.Struct type Metadata = structpb.Struct + +type Project struct { + Id uuid.UUID + Name string +} + +// APIKeyWithoutSecret represents an API key without exposing the secret part. +// It includes the key's ID, name, and the ID of the project it belongs to. +type APIKeyWithoutSecret struct { + // Id is the unique identifier of the API key. + Id uuid.UUID + + // Name is the name given to the API key, useful for identification purposes. + Name string + + // ProjectId is the ID of the project this API key is associated with. + ProjectId uuid.UUID +} + +// APIKeyWithSecret represents an API key along with its secret. This struct is used +// when creating a new API key, where the secret is returned as part of the creation +// process. The secret is sensitive information and should be handled securely, as it +// provides authenticated access to the API. +type APIKeyWithSecret struct { + // Id is the unique identifier of the API key, represented as a UUID. + Id uuid.UUID + + // Name is the name given to the API key, useful for identification and organization + // purposes within a project. + Name string + + // ProjectId is the UUID of the project to which this API key is associated. It helps + // in scoping the API key to specific projects, enhancing security and manageability. + ProjectId uuid.UUID + + // Secret is the sensitive part of the API key, used for authentication in API requests. + // The secret is only exposed to the user upon creation of the API key and should be + // stored securely by the client. + Secret string +}