File: error.go

package info (click to toggle)
golang-github-meilisearch-meilisearch-go 0.33.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 956 kB
  • sloc: makefile: 9
file content (190 lines) | stat: -rw-r--r-- 6,706 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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.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")
	ErrMeilisearchNotAvailable       = errors.New("meilisearch service is not available")
)