189 lines
6.5 KiB
Go
189 lines
6.5 KiB
Go
package meilisearch
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ErrCode are all possible errors found during requests
|
|
type ErrCode int
|
|
|
|
const (
|
|
// ErrCodeUnknown default error code, undefined
|
|
ErrCodeUnknown ErrCode = 0
|
|
// ErrCodeMarshalRequest impossible to serialize request body
|
|
ErrCodeMarshalRequest ErrCode = iota + 1
|
|
// ErrCodeResponseUnmarshalBody impossible deserialize the response body
|
|
ErrCodeResponseUnmarshalBody
|
|
// MeilisearchApiError send by the meilisearch api
|
|
MeilisearchApiError
|
|
// MeilisearchApiErrorWithoutMessage MeilisearchApiError send by the meilisearch api
|
|
MeilisearchApiErrorWithoutMessage
|
|
// MeilisearchTimeoutError
|
|
MeilisearchTimeoutError
|
|
// MeilisearchCommunicationError impossible execute a request
|
|
MeilisearchCommunicationError
|
|
// MeilisearchMaxRetriesExceeded used max retries and exceeded
|
|
MeilisearchMaxRetriesExceeded
|
|
)
|
|
|
|
const (
|
|
rawStringCtx = `(path "${method} ${endpoint}" with method "${function}")`
|
|
rawStringMarshalRequest = `unable to marshal body from request: '${request}'`
|
|
rawStringResponseUnmarshalBody = `unable to unmarshal body from response: '${response}' status code: ${statusCode}`
|
|
rawStringMeilisearchApiError = `unaccepted status code found: ${statusCode} expected: ${statusCodeExpected}, MeilisearchApiError Message: ${message}, Code: ${code}, Type: ${type}, Link: ${link}`
|
|
rawStringMeilisearchApiErrorWithoutMessage = `unaccepted status code found: ${statusCode} expected: ${statusCodeExpected}, MeilisearchApiError Message: ${message}`
|
|
rawStringMeilisearchTimeoutError = `MeilisearchTimeoutError`
|
|
rawStringMeilisearchCommunicationError = `MeilisearchCommunicationError unable to execute request`
|
|
rawStringMeilisearchMaxRetriesExceeded = "failed to request and max retries exceeded"
|
|
)
|
|
|
|
func (e ErrCode) rawMessage() string {
|
|
switch e {
|
|
case ErrCodeMarshalRequest:
|
|
return rawStringMarshalRequest + " " + rawStringCtx
|
|
case ErrCodeResponseUnmarshalBody:
|
|
return rawStringResponseUnmarshalBody + " " + rawStringCtx
|
|
case MeilisearchApiError:
|
|
return rawStringMeilisearchApiError + " " + rawStringCtx
|
|
case MeilisearchApiErrorWithoutMessage:
|
|
return rawStringMeilisearchApiErrorWithoutMessage + " " + rawStringCtx
|
|
case MeilisearchTimeoutError:
|
|
return rawStringMeilisearchTimeoutError + " " + rawStringCtx
|
|
case MeilisearchCommunicationError:
|
|
return rawStringMeilisearchCommunicationError + " " + rawStringCtx
|
|
case MeilisearchMaxRetriesExceeded:
|
|
return rawStringMeilisearchMaxRetriesExceeded + " " + rawStringCtx
|
|
default:
|
|
return rawStringCtx
|
|
}
|
|
}
|
|
|
|
type meilisearchApiError struct {
|
|
Message string `json:"message"`
|
|
Code string `json:"code"`
|
|
Type string `json:"type"`
|
|
Link string `json:"link"`
|
|
}
|
|
|
|
// Error is the internal error structure that all exposed method use.
|
|
// So ALL errors returned by this library can be cast to this struct (as a pointer)
|
|
type Error struct {
|
|
// Endpoint is the path of the request (host is not in)
|
|
Endpoint string
|
|
|
|
// Method is the HTTP verb of the request
|
|
Method string
|
|
|
|
// Function name used
|
|
Function string
|
|
|
|
// RequestToString is the raw request into string ('empty request' if not present)
|
|
RequestToString string
|
|
|
|
// RequestToString is the raw request into string ('empty response' if not present)
|
|
ResponseToString string
|
|
|
|
// Error info from meilisearch api
|
|
// Message is the raw request into string ('empty meilisearch message' if not present)
|
|
MeilisearchApiError meilisearchApiError
|
|
|
|
// StatusCode of the request
|
|
StatusCode int
|
|
|
|
// StatusCode expected by the endpoint to be considered as a success
|
|
StatusCodeExpected []int
|
|
|
|
rawMessage string
|
|
|
|
// OriginError is the origin error that produce the current Error. It can be nil in case of a bad status code.
|
|
OriginError error
|
|
|
|
// ErrCode is the internal error code that represent the different step when executing a request that can produce
|
|
// an error.
|
|
ErrCode ErrCode
|
|
|
|
encoder
|
|
}
|
|
|
|
// Error return a well human formatted message.
|
|
func (e *Error) Error() string {
|
|
message := namedSprintf(e.rawMessage, map[string]interface{}{
|
|
"endpoint": e.Endpoint,
|
|
"method": e.Method,
|
|
"function": e.Function,
|
|
"request": e.RequestToString,
|
|
"response": e.ResponseToString,
|
|
"statusCodeExpected": e.StatusCodeExpected,
|
|
"statusCode": e.StatusCode,
|
|
"message": e.MeilisearchApiError.Message,
|
|
"code": e.MeilisearchApiError.Code,
|
|
"type": e.MeilisearchApiError.Type,
|
|
"link": e.MeilisearchApiError.Link,
|
|
})
|
|
if e.OriginError != nil {
|
|
return fmt.Sprintf("%s: %s", message, e.OriginError.Error())
|
|
}
|
|
|
|
return message
|
|
}
|
|
|
|
// WithErrCode add an error code to an error
|
|
func (e *Error) WithErrCode(err ErrCode, errs ...error) *Error {
|
|
if errs != nil {
|
|
e.OriginError = errs[0]
|
|
}
|
|
|
|
e.rawMessage = err.rawMessage()
|
|
e.ErrCode = err
|
|
return e
|
|
}
|
|
|
|
// ErrorBody add a body to an error
|
|
func (e *Error) ErrorBody(body []byte) {
|
|
msg := meilisearchApiError{}
|
|
|
|
if e.encoder != nil {
|
|
err := e.encoder.Decode(body, &msg)
|
|
if err == nil {
|
|
e.MeilisearchApiError.Message = msg.Message
|
|
e.MeilisearchApiError.Code = msg.Code
|
|
e.MeilisearchApiError.Type = msg.Type
|
|
e.MeilisearchApiError.Link = msg.Link
|
|
}
|
|
return
|
|
}
|
|
|
|
e.ResponseToString = string(body)
|
|
err := json.Unmarshal(body, &msg)
|
|
if err == nil {
|
|
e.MeilisearchApiError.Message = msg.Message
|
|
e.MeilisearchApiError.Code = msg.Code
|
|
e.MeilisearchApiError.Type = msg.Type
|
|
e.MeilisearchApiError.Link = msg.Link
|
|
}
|
|
}
|
|
|
|
// VersionErrorHintMessage a hint to the error message if it may come from a version incompatibility with meilisearch
|
|
func VersionErrorHintMessage(err error, req *internalRequest) error {
|
|
return fmt.Errorf("%w. Hint: It might not be working because you're not up to date with the "+
|
|
"Meilisearch version that %s call requires", err, req.functionName)
|
|
}
|
|
|
|
func namedSprintf(format string, params map[string]interface{}) string {
|
|
for key, val := range params {
|
|
format = strings.ReplaceAll(format, "${"+key+"}", fmt.Sprintf("%v", val))
|
|
}
|
|
return format
|
|
}
|
|
|
|
// General errors
|
|
var (
|
|
ErrInvalidRequestMethod = errors.New("request body is not expected for GET and HEAD requests")
|
|
ErrRequestBodyWithoutContentType = errors.New("request body without Content-Type is not allowed")
|
|
ErrNoSearchRequest = errors.New("no search request provided")
|
|
ErrNoFacetSearchRequest = errors.New("no search facet request provided")
|
|
ErrConnectingFailed = errors.New("meilisearch is not connected")
|
|
)
|