C
last analyzed

Complexity

Conditions 9

Size

Total Lines 33
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
eloc 24
nop 2
dl 0
loc 33
ccs 23
cts 23
cp 1
crap 9
rs 6.6666
c 0
b 0
f 0
1
package negotiator
2
3
import (
4
	"context"
5
	"math"
6
	"net/http"
7
	"regexp"
8
	"strconv"
9
	"strings"
10
11
	"github.com/getkin/kin-openapi/openapi3"
12
	"github.com/muonsoft/openapi-mock/pkg/logcontext"
13
	"github.com/pkg/errors"
14
)
15
16
type StatusCodeNegotiator interface {
17
	NegotiateStatusCode(request *http.Request, responses openapi3.Responses) (key string, code int, err error)
18
}
19
20
func NewStatusCodeNegotiator() StatusCodeNegotiator {
21 1
	return &statusCodeNegotiator{
22
		rangeDefinitionPattern: regexp.MustCompile("^[1-5]xx$"),
23
	}
24
}
25
26
type statusCodeNegotiator struct {
27
	rangeDefinitionPattern *regexp.Regexp
28
}
29
30
func (negotiator *statusCodeNegotiator) NegotiateStatusCode(request *http.Request, responses openapi3.Responses) (key string, code int, err error) {
31 1
	minSuccessCode := math.MaxInt32
32 1
	minSuccessCodeKey := ""
33 1
	hasSuccessCode := false
34 1
	minErrorCode := math.MaxInt32
35 1
	minErrorCodeKey := ""
36 1
	hasErrorCode := false
37
38 1
	for key := range responses {
39 1
		code, err := negotiator.parseStatusCode(request.Context(), key)
40 1
		if err != nil {
41 1
			continue
42
		}
43
44 1
		if code >= 200 && code < 300 && code < minSuccessCode {
45 1
			hasSuccessCode = true
46 1
			minSuccessCode = code
47 1
			minSuccessCodeKey = key
48 1
		} else if code < minErrorCode {
49 1
			hasErrorCode = true
50 1
			minErrorCode = code
51 1
			minErrorCodeKey = key
52
		}
53
	}
54
55 1
	if hasSuccessCode {
56 1
		return minSuccessCodeKey, minSuccessCode, nil
57
	}
58 1
	if hasErrorCode {
59 1
		return minErrorCodeKey, minErrorCode, nil
60
	}
61
62 1
	return "", http.StatusInternalServerError, errors.Wrap(ErrNoMatchingResponse, "[statusCodeNegotiator] failed to negotiate response")
63
}
64
65
func (negotiator *statusCodeNegotiator) parseStatusCode(ctx context.Context, key string) (int, error) {
66 1
	var code int
67 1
	var err error
68
69 1
	key = strings.ToLower(key)
70
71 1
	switch {
72
	case key == "default":
73 1
		code = http.StatusInternalServerError
74
	case negotiator.rangeDefinitionPattern.MatchString(key):
75 1
		code, _ = strconv.Atoi(string(key[0]))
76 1
		code *= 100
77
	default:
78 1
		code, err = strconv.Atoi(key)
79 1
		if err != nil {
80 1
			logger := logcontext.LoggerFromContext(ctx)
81 1
			logger.Warnf(
82
				"[statusCodeNegotiator] response with key '%s' is ignored: "+
83
					"key must be a valid status code integer or equal to 'default', "+
84
					"'1xx', '2xx', '3xx', '4xx' or '5xx'",
85
				key,
86
			)
87
		}
88
	}
89
90 1
	return code, err
91
}
92