-
Notifications
You must be signed in to change notification settings - Fork 0
/
errb2c.go
94 lines (73 loc) · 2.49 KB
/
errb2c.go
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
package errhandling
import (
"context"
"errors"
"fmt"
"strings"
"github.com/datatrails/go-datatrails-common/logger"
)
const (
// grpcErrPrefix is the prefix grpc gives to errors whose format it doesn't recognise
grpcErrPrefix = "rpc error: code = Unknown desc = "
// b2cAPIVersion is the api version of b2c that we are using
b2cAPIVersion = "1.0.0"
b2cFmtString = "apiVersion: %s status: %d userMessage: %s"
)
type ErrB2C struct {
// APIVersion is the API version of b2c that we are using
APIVersion string `json:"version"`
// Status, http status code. The REST API should return an HTTP 4xx error message
Status int `json:"status"`
// UserMessage is the message to the user
UserMessage string `json:"userMessage"`
}
// NewErrB2c creates a B2C error given a http status and a user message.
func NewErrB2c(status int, format string, a ...any) *ErrB2C {
return &ErrB2C{
APIVersion: b2cAPIVersion,
Status: status,
UserMessage: fmt.Sprintf(format, a...),
}
}
// WithErrorString converts a B2C error string into
//
// a b2c error.
func (e *ErrB2C) WithErrorString(log Logger, errString string) error {
// sscan is a reverse sprintf (however we cannot have spaces in %s strings)
if _, scanErr := fmt.Sscanf(errString, b2cFmtString,
&e.APIVersion, &e.Status, &e.UserMessage); scanErr != nil {
log.Infof("scan error: %v", scanErr)
return scanErr
}
// scanF ends on spaces, so ensure we have a correct user message
e.UserMessage = strings.Split(errString, "userMessage: ")[1]
return nil
}
// GetErrB2c attempts to get a ErrB2c from an error
func GetErrB2c(ctx context.Context, err error) (*ErrB2C, error) {
log := logger.Sugar.FromContext(ctx)
defer log.Close()
errB2C := &ErrB2C{}
// already a b2c error.
ok := errors.As(err, &errB2C)
if ok {
return errB2C, nil
}
// chance it is a status.Error (can't access status.Error as its part of an internal package)
if !strings.HasPrefix(err.Error(), grpcErrPrefix) {
log.Infof("error is not b2c error: %v", err)
return nil, err
}
// can't use errors.Unwrap because status.Error doesn't implement Unwrap method
b2cErrString := strings.TrimPrefix(err.Error(), grpcErrPrefix)
if convErr := errB2C.WithErrorString(log, b2cErrString); convErr != nil {
log.Infof("unable to add error string: %v", convErr)
return nil, convErr
}
// gots the b2c error all good
return errB2C, nil
}
// Error implements the err interface
func (e *ErrB2C) Error() string {
return fmt.Sprintf(b2cFmtString, e.APIVersion, e.Status, e.UserMessage)
}