Skip to content

Commit

Permalink
add error handling to REST methods on Client method operations
Browse files Browse the repository at this point in the history
  • Loading branch information
austin-denoble committed May 29, 2024
1 parent c7eda0a commit 75a2115
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 70 deletions.
215 changes: 146 additions & 69 deletions pinecone/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,37 +107,24 @@ func (c *Client) IndexWithAdditionalMetadata(host string, namespace string, addi
return idx, nil
}

func (c *Client) extractAuthHeader() map[string]string {
possibleAuthKeys := []string{
"api-key",
"authorization",
"access_token",
}

for key, value := range c.headers {
for _, checkKey := range possibleAuthKeys {
if strings.ToLower(key) == checkKey {
return map[string]string{key: value}
}
}
}

return nil
}

func (c *Client) ListIndexes(ctx context.Context) ([]*Index, error) {
res, err := c.restClient.ListIndexes(ctx)
if err != nil {
return nil, err
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to list indexes: ")
}

var indexList control.IndexList
err = json.NewDecoder(res.Body).Decode(&indexList)
err = json.Unmarshal(resBodyBytes, &indexList)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -221,16 +208,16 @@ func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest)
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusCreated {
var errResp control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errResp)
if err != nil {
return nil, fmt.Errorf("failed to decode error response: %w", err)
}
return nil, fmt.Errorf("failed to create index: %s", errResp.Error.Message)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to create index: ")
}

return decodeIndex(res.Body)
return decodeIndex(resBodyBytes)
}

type CreateServerlessIndexRequest struct {
Expand Down Expand Up @@ -261,16 +248,16 @@ func (c *Client) CreateServerlessIndex(ctx context.Context, in *CreateServerless
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusCreated {
var errResp control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errResp)
if err != nil {
return nil, fmt.Errorf("failed to decode error response: %w", err)
}
return nil, fmt.Errorf("failed to create index: %s", errResp.Error.Message)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to create index: ")
}

return decodeIndex(res.Body)
return decodeIndex(resBodyBytes)
}

func (c *Client) DescribeIndex(ctx context.Context, idxName string) (*Index, error) {
Expand All @@ -280,16 +267,16 @@ func (c *Client) DescribeIndex(ctx context.Context, idxName string) (*Index, err
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusOK {
var errResp control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errResp)
if err != nil {
return nil, fmt.Errorf("failed to decode error response: %w", err)
}
return nil, fmt.Errorf("failed to describe idx: %s", errResp.Error.Message)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to describe index: ")
}

return decodeIndex(res.Body)
return decodeIndex(resBodyBytes)
}

func (c *Client) DeleteIndex(ctx context.Context, idxName string) error {
Expand All @@ -299,13 +286,13 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error {
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusAccepted {
var errResp control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errResp)
if err != nil {
return fmt.Errorf("failed to decode error response: %w", err)
}
return fmt.Errorf("failed to delete index: %s", errResp.Error.Message)
return handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to delete index: ")
}

return nil
Expand All @@ -318,12 +305,17 @@ func (c *Client) ListCollections(ctx context.Context) ([]*Collection, error) {
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to list collections: ")
}

var collectionsResponse control.CollectionList
if err := json.NewDecoder(res.Body).Decode(&collectionsResponse); err != nil {
if err := json.Unmarshal(resBodyBytes, &collectionsResponse); err != nil {
return nil, err
}

Expand All @@ -342,11 +334,16 @@ func (c *Client) DescribeCollection(ctx context.Context, collectionName string)
}
defer res.Body.Close()

resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to describe collection: ")
}

return decodeCollection(res.Body)
return decodeCollection(resBodyBytes)
}

type CreateCollectionRequest struct {
Expand All @@ -366,17 +363,16 @@ func (c *Client) CreateCollection(ctx context.Context, in *CreateCollectionReque
}
defer res.Body.Close()

if res.StatusCode != http.StatusCreated {
var errorResponse control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errorResponse)
if err != nil {
return nil, err
}
resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

return nil, fmt.Errorf("failed to create collection: %s", errorResponse.Error.Message)
if res.StatusCode != http.StatusCreated {
return nil, handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to create collection: ")
}

return decodeCollection(res.Body)
return decodeCollection(resBodyBytes)
}

func (c *Client) DeleteCollection(ctx context.Context, collectionName string) error {
Expand All @@ -386,14 +382,31 @@ func (c *Client) DeleteCollection(ctx context.Context, collectionName string) er
}
defer res.Body.Close()

// Check for successful response, consider successful HTTP codes like 200 or 204 as successful deletion
resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}

if res.StatusCode != http.StatusAccepted {
var errResp control.ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errResp)
if err != nil {
return fmt.Errorf("failed to decode error response: %w", err)
return handleErrorResponseBody(resBodyBytes, res.StatusCode, "failed to delete collection: ")
}

return nil
}

func (c *Client) extractAuthHeader() map[string]string {
possibleAuthKeys := []string{
"api-key",
"authorization",
"access_token",
}

for key, value := range c.headers {
for _, checkKey := range possibleAuthKeys {
if strings.ToLower(key) == checkKey {
return map[string]string{key: value}
}
}
return fmt.Errorf("failed to delete collection '%s': %s", collectionName, errResp.Error.Message)
}

return nil
Expand Down Expand Up @@ -438,9 +451,9 @@ func toIndex(idx *control.IndexModel) *Index {
}
}

func decodeIndex(resBody io.ReadCloser) (*Index, error) {
func decodeIndex(resBodyBytes []byte) (*Index, error) {
var idx control.IndexModel
err := json.NewDecoder(resBody).Decode(&idx)
err := json.Unmarshal(resBodyBytes, &idx)
if err != nil {
return nil, fmt.Errorf("failed to decode idx response: %w", err)
}
Expand All @@ -463,16 +476,75 @@ func toCollection(cm *control.CollectionModel) *Collection {
}
}

func decodeCollection(resBody io.ReadCloser) (*Collection, error) {
func decodeCollection(resBodyBytes []byte) (*Collection, error) {
var collectionModel control.CollectionModel
err := json.NewDecoder(resBody).Decode(&collectionModel)
err := json.Unmarshal(resBodyBytes, &collectionModel)
if err != nil {
return nil, fmt.Errorf("failed to decode collection response: %w", err)
}

return toCollection(&collectionModel), nil
}

func decodeErrorResponse(resBodyBytes []byte) (*control.ErrorResponse, error) {
var errorResponse control.ErrorResponse
err := json.Unmarshal(resBodyBytes, &errorResponse)
if err != nil {
return nil, fmt.Errorf("failed to decode error response: %w", err)
}

if errorResponse.Status == 0 {
return nil, fmt.Errorf("unable to parse ErrorResponse: %v", string(resBodyBytes))
}

return &errorResponse, nil
}

type errorResponseMap struct {
StatusCode int `json:"status_code"`
Body string `json:"body,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
Message string `json:"message,omitempty"`
Details string `json:"details,omitempty"`
}

func handleErrorResponseBody(resBodyBytes []byte, statusCode int, errMsgPrefix string) error {
var errMap errorResponseMap

errMap.StatusCode = statusCode

// try and decode ErrorResponse
if json.Valid(resBodyBytes) {
errorResponse, err := decodeErrorResponse(resBodyBytes)
if err == nil {
errMap.Message = errorResponse.Error.Message
errMap.ErrorCode = string(errorResponse.Error.Code)

if errorResponse.Error.Details != nil {
errMap.Details = fmt.Sprintf("%+v", errorResponse.Error.Details)
}
}
}

errMap.Body = string(resBodyBytes)

if errMap.Message != "" {
errMap.Message = errMsgPrefix + errMap.Message
}

return formatError(errMap)
}

func formatError(errMap errorResponseMap) error {
jsonString, err := json.Marshal(errMap)
if err != nil {
return err
}
baseError := fmt.Errorf(string(jsonString))

return &PineconeError{Code: errMap.StatusCode, Msg: baseError}
}

func buildClientBaseOptions(in NewClientBaseParams) []control.ClientOption {
clientOptions := []control.ClientOption{}

Expand Down Expand Up @@ -546,3 +618,8 @@ func minOne(x int32) int32 {
}
return x
}

func PrettifyStruct(obj interface{}) string {
bytes, _ := json.MarshalIndent(obj, "", " ")
return string(bytes)
}
Loading

0 comments on commit 75a2115

Please sign in to comment.