refactor: export config errors

This commit is contained in:
Adrien Marquès 2021-06-21 21:50:57 +02:00
parent 19bcc2e8dc
commit 178d9a8eee
Signed by: xdrm-brackets
GPG Key ID: D75243CA236D825E
5 changed files with 104 additions and 102 deletions

View File

@ -21,12 +21,12 @@ type Server struct {
func (srv *Server) Parse(r io.Reader) error { func (srv *Server) Parse(r io.Reader) error {
err := json.NewDecoder(r).Decode(&srv.Services) err := json.NewDecoder(r).Decode(&srv.Services)
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", errRead, err) return fmt.Errorf("%s: %w", ErrRead, err)
} }
err = srv.validate() err = srv.validate()
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", errFormat, err) return fmt.Errorf("%s: %w", ErrFormat, err)
} }
return nil return nil
} }
@ -41,7 +41,7 @@ func (server Server) validate(datatypes ...validator.Type) error {
} }
if err := server.collide(); err != nil { if err := server.collide(); err != nil {
return fmt.Errorf("%s: %w", errFormat, err) return fmt.Errorf("%s: %w", ErrFormat, err)
} }
return nil return nil
} }
@ -105,14 +105,14 @@ func checkURICollision(uriA, uriB []string, inputA, inputB map[string]*Parameter
// both captures -> as we cannot check, consider a collision // both captures -> as we cannot check, consider a collision
if aIsCapture && bIsCapture { if aIsCapture && bIsCapture {
errors = append(errors, fmt.Errorf("%w (path %s and %s)", errPatternCollision, aPart, bPart)) errors = append(errors, fmt.Errorf("%w (path %s and %s)", ErrPatternCollision, aPart, bPart))
continue continue
} }
// no capture -> check strict equality // no capture -> check strict equality
if !aIsCapture && !bIsCapture { if !aIsCapture && !bIsCapture {
if aPart == bPart { if aPart == bPart {
errors = append(errors, fmt.Errorf("%w (same path '%s')", errPatternCollision, aPart)) errors = append(errors, fmt.Errorf("%w (same path '%s')", ErrPatternCollision, aPart))
continue continue
} }
} }
@ -123,13 +123,13 @@ func checkURICollision(uriA, uriB []string, inputA, inputB map[string]*Parameter
// fail if no type or no validator // fail if no type or no validator
if !exists || input.Validator == nil { if !exists || input.Validator == nil {
errors = append(errors, fmt.Errorf("%w (invalid type for %s)", errPatternCollision, aPart)) errors = append(errors, fmt.Errorf("%w (invalid type for %s)", ErrPatternCollision, aPart))
continue continue
} }
// fail if not valid // fail if not valid
if _, valid := input.Validator(bPart); valid { if _, valid := input.Validator(bPart); valid {
errors = append(errors, fmt.Errorf("%w (%s captures '%s')", errPatternCollision, aPart, bPart)) errors = append(errors, fmt.Errorf("%w (%s captures '%s')", ErrPatternCollision, aPart, bPart))
continue continue
} }
@ -139,13 +139,13 @@ func checkURICollision(uriA, uriB []string, inputA, inputB map[string]*Parameter
// fail if no type or no validator // fail if no type or no validator
if !exists || input.Validator == nil { if !exists || input.Validator == nil {
errors = append(errors, fmt.Errorf("%w (invalid type for %s)", errPatternCollision, bPart)) errors = append(errors, fmt.Errorf("%w (invalid type for %s)", ErrPatternCollision, bPart))
continue continue
} }
// fail if not valid // fail if not valid
if _, valid := input.Validator(aPart); valid { if _, valid := input.Validator(aPart); valid {
errors = append(errors, fmt.Errorf("%w (%s captures '%s')", errPatternCollision, bPart, aPart)) errors = append(errors, fmt.Errorf("%w (%s captures '%s')", ErrPatternCollision, bPart, aPart))
continue continue
} }
} }

View File

@ -21,15 +21,15 @@ func TestLegalServiceName(t *testing.T) {
// empty // empty
{ {
`[ { "method": "GET", "info": "a", "path": "" } ]`, `[ { "method": "GET", "info": "a", "path": "" } ]`,
errInvalidPattern, ErrInvalidPattern,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "no-starting-slash" } ]`, `[ { "method": "GET", "info": "a", "path": "no-starting-slash" } ]`,
errInvalidPattern, ErrInvalidPattern,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "ending-slash/" } ]`, `[ { "method": "GET", "info": "a", "path": "ending-slash/" } ]`,
errInvalidPattern, ErrInvalidPattern,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/" } ]`, `[ { "method": "GET", "info": "a", "path": "/" } ]`,
@ -45,35 +45,35 @@ func TestLegalServiceName(t *testing.T) {
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/s{braces}" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/s{braces}" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{braces}a" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}a" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{braces}" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}" } ]`,
errUndefinedBraceCapture, ErrUndefinedBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/s{braces}/abc" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/s{braces}/abc" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{braces}s/abc" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}s/abc" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{braces}/abc" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}/abc" } ]`,
errUndefinedBraceCapture, ErrUndefinedBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{b{races}s/abc" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{b{races}s/abc" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
{ {
`[ { "method": "GET", "info": "a", "path": "/invalid/{braces}/}abc" } ]`, `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}/}abc" } ]`,
errInvalidPatternBraceCapture, ErrInvalidPatternBraceCapture,
}, },
} }
@ -143,8 +143,8 @@ func TestAvailableMethods(t *testing.T) {
t.FailNow() t.FailNow()
} }
if !test.ValidMethod && !errors.Is(err, errUnknownMethod) { if !test.ValidMethod && !errors.Is(err, ErrUnknownMethod) {
t.Errorf("expected error <%s> got <%s>", errUnknownMethod, err) t.Errorf("expected error <%s> got <%s>", ErrUnknownMethod, err)
t.FailNow() t.FailNow()
} }
}) })
@ -184,7 +184,7 @@ func TestParseMissingMethodDescription(t *testing.T) {
`[ { "method": "GET", "path": "/" }]`, `[ { "method": "GET", "path": "/" }]`,
false, false,
}, },
{ // missing description { // missing descriptiontype
`[ { "method": "GET", "path": "/subservice" }]`, `[ { "method": "GET", "path": "/subservice" }]`,
false, false,
}, },
@ -217,8 +217,8 @@ func TestParseMissingMethodDescription(t *testing.T) {
t.FailNow() t.FailNow()
} }
if !test.ValidDescription && !errors.Is(err, errMissingDescription) { if !test.ValidDescription && !errors.Is(err, ErrMissingDescription) {
t.Errorf("expected error <%s> got <%s>", errMissingDescription, err) t.Errorf("expected error <%s> got <%s>", ErrMissingDescription, err)
t.FailNow() t.FailNow()
} }
}) })
@ -321,7 +321,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamDesc, ErrMissingParamDesc,
}, },
{ // invalid param name suffix { // invalid param name suffix
`[ `[
@ -334,7 +334,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamDesc, ErrMissingParamDesc,
}, },
{ // missing param description { // missing param description
@ -348,7 +348,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamDesc, ErrMissingParamDesc,
}, },
{ // empty param description { // empty param description
`[ `[
@ -361,7 +361,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamDesc, ErrMissingParamDesc,
}, },
{ // missing param type { // missing param type
@ -375,7 +375,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamType, ErrMissingParamType,
}, },
{ // empty param type { // empty param type
`[ `[
@ -388,7 +388,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMissingParamType, ErrMissingParamType,
}, },
{ // invalid type (optional mark only) { // invalid type (optional mark only)
`[ `[
@ -402,7 +402,7 @@ func TestParseParameters(t *testing.T) {
} }
]`, ]`,
errMissingParamType, ErrMissingParamType,
}, },
{ // valid description + valid type { // valid description + valid type
`[ `[
@ -444,7 +444,7 @@ func TestParseParameters(t *testing.T) {
} }
]`, ]`,
// 2 possible errors as map order is not deterministic // 2 possible errors as map order is not deterministic
errParamNameConflict, ErrParamNameConflict,
}, },
{ // rename conflict with name { // rename conflict with name
`[ `[
@ -459,7 +459,7 @@ func TestParseParameters(t *testing.T) {
} }
]`, ]`,
// 2 possible errors as map order is not deterministic // 2 possible errors as map order is not deterministic
errParamNameConflict, ErrParamNameConflict,
}, },
{ // rename conflict with rename { // rename conflict with rename
`[ `[
@ -474,7 +474,7 @@ func TestParseParameters(t *testing.T) {
} }
]`, ]`,
// 2 possible errors as map order is not deterministic // 2 possible errors as map order is not deterministic
errParamNameConflict, ErrParamNameConflict,
}, },
{ // both renamed with no conflict { // both renamed with no conflict
@ -503,7 +503,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMandatoryRename, ErrMandatoryRename,
}, },
{ {
`[ `[
@ -516,7 +516,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errMandatoryRename, ErrMandatoryRename,
}, },
{ {
`[ `[
@ -556,7 +556,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errIllegalOptionalURIParam, ErrIllegalOptionalURIParam,
}, },
{ // URI parameter not specified { // URI parameter not specified
`[ `[
@ -569,7 +569,7 @@ func TestParseParameters(t *testing.T) {
} }
} }
]`, ]`,
errUnspecifiedBraceCapture, ErrUnspecifiedBraceCapture,
}, },
{ // URI parameter not defined { // URI parameter not defined
`[ `[
@ -580,7 +580,7 @@ func TestParseParameters(t *testing.T) {
"in": { } "in": { }
} }
]`, ]`,
errUndefinedBraceCapture, ErrUndefinedBraceCapture,
}, },
} }
@ -637,7 +637,7 @@ func TestServiceCollision(t *testing.T) {
"info": "info", "in": {} "info": "info", "in": {}
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -672,7 +672,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -698,7 +698,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -711,7 +711,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -750,7 +750,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -789,7 +789,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[
@ -804,7 +804,7 @@ func TestServiceCollision(t *testing.T) {
} }
} }
]`, ]`,
errPatternCollision, ErrPatternCollision,
}, },
{ {
`[ `[

View File

@ -1,59 +1,61 @@
package config package config
// cerr allows you to create constant "const" error with type boxing. // Err allows you to create constant "const" error with type boxing.
type cerr string type Err string
func (err cerr) Error() string { func (err Err) Error() string {
return string(err) return string(err)
} }
// errRead - read error const (
const errRead = cerr("cannot read config") // ErrRead - read error
ErrRead = Err("cannot read config")
// errUnknownMethod - unknown http method // ErrUnknownMethod - unknown http method
const errUnknownMethod = cerr("unknown HTTP method") ErrUnknownMethod = Err("unknown HTTP method")
// errFormat - invalid format // ErrFormat - invalid format
const errFormat = cerr("invalid config format") ErrFormat = Err("invalid config format")
// errPatternCollision - collision between 2 services' patterns // ErrPatternCollision - collision between 2 services' patterns
const errPatternCollision = cerr("pattern collision") ErrPatternCollision = Err("pattern collision")
// errInvalidPattern - malformed service pattern // ErrInvalidPattern - malformed service pattern
const errInvalidPattern = cerr("malformed service path: must begin with a '/' and not end with") ErrInvalidPattern = Err("malformed service path: must begin with a '/' and not end with")
// errInvalidPatternBraceCapture - invalid brace capture // ErrInvalidPatternBraceCapture - invalid brace capture
const errInvalidPatternBraceCapture = cerr("invalid uri parameter") ErrInvalidPatternBraceCapture = Err("invalid uri parameter")
// errUnspecifiedBraceCapture - missing path brace capture // ErrUnspecifiedBraceCapture - missing path brace capture
const errUnspecifiedBraceCapture = cerr("missing uri parameter") ErrUnspecifiedBraceCapture = Err("missing uri parameter")
// errUndefinedBraceCapture - missing capturing brace definition // ErrUndefinedBraceCapture - missing capturing brace definition
const errUndefinedBraceCapture = cerr("missing uri parameter definition") ErrUndefinedBraceCapture = Err("missing uri parameter definition")
// errMandatoryRename - capture/query parameters must be renamed // ErrMandatoryRename - capture/query parameters must be renamed
const errMandatoryRename = cerr("uri and query parameters must be renamed") ErrMandatoryRename = Err("uri and query parameters must be renamed")
// errMissingDescription - a service is missing its description // ErrMissingDescription - a service is missing its description
const errMissingDescription = cerr("missing description") ErrMissingDescription = Err("missing description")
// errIllegalOptionalURIParam - uri parameter cannot optional // ErrIllegalOptionalURIParam - uri parameter cannot optional
const errIllegalOptionalURIParam = cerr("uri parameter cannot be optional") ErrIllegalOptionalURIParam = Err("uri parameter cannot be optional")
// errOptionalOption - cannot have optional output // ErrOptionalOption - cannot have optional output
const errOptionalOption = cerr("output cannot be optional") ErrOptionalOption = Err("output cannot be optional")
// errMissingParamDesc - missing parameter description // ErrMissingParamDesc - missing parameter description
const errMissingParamDesc = cerr("missing parameter description") ErrMissingParamDesc = Err("missing parameter description")
// errUnknownDataType - unknown parameter datatype // ErrUnknownParamType - unknown parameter type
const errUnknownDataType = cerr("unknown parameter datatype") ErrUnknownParamType = Err("unknown parameter datatype")
// errIllegalParamName - illegal parameter name // ErrIllegalParamName - illegal parameter name
const errIllegalParamName = cerr("illegal parameter name") ErrIllegalParamName = Err("illegal parameter name")
// errMissingParamType - missing parameter type // ErrMissingParamType - missing parameter type
const errMissingParamType = cerr("missing parameter type") ErrMissingParamType = Err("missing parameter type")
// errParamNameConflict - name/rename conflict // ErrParamNameConflict - name/rename conflict
const errParamNameConflict = cerr("parameter name conflict") ErrParamNameConflict = Err("parameter name conflict")
)

View File

@ -20,11 +20,11 @@ type Parameter struct {
func (param *Parameter) validate(datatypes ...validator.Type) error { func (param *Parameter) validate(datatypes ...validator.Type) error {
if len(param.Description) < 1 { if len(param.Description) < 1 {
return errMissingParamDesc return ErrMissingParamDesc
} }
if len(param.Type) < 1 || param.Type == "?" { if len(param.Type) < 1 || param.Type == "?" {
return errMissingParamType return ErrMissingParamType
} }
// optional type // optional type
@ -42,7 +42,7 @@ func (param *Parameter) validate(datatypes ...validator.Type) error {
} }
} }
if param.Validator == nil { if param.Validator == nil {
return errUnknownDataType return ErrUnknownParamType
} }
return nil return nil
} }

View File

@ -113,7 +113,7 @@ func (svc *Service) validate(datatypes ...validator.Type) error {
// check description // check description
if len(strings.Trim(svc.Description, " \t\r\n")) < 1 { if len(strings.Trim(svc.Description, " \t\r\n")) < 1 {
return fmt.Errorf("field 'description': %w", errMissingDescription) return fmt.Errorf("field 'description': %w", ErrMissingDescription)
} }
// check input parameters // check input parameters
@ -125,7 +125,7 @@ func (svc *Service) validate(datatypes ...validator.Type) error {
// fail if a brace capture remains undefined // fail if a brace capture remains undefined
for _, capture := range svc.Captures { for _, capture := range svc.Captures {
if capture.Ref == nil { if capture.Ref == nil {
return fmt.Errorf("field 'in': %s: %w", capture.Name, errUndefinedBraceCapture) return fmt.Errorf("field 'in': %s: %w", capture.Name, ErrUndefinedBraceCapture)
} }
} }
@ -144,7 +144,7 @@ func (svc *Service) isMethodAvailable() error {
return nil return nil
} }
} }
return errUnknownMethod return ErrUnknownMethod
} }
func (svc *Service) isPatternValid() error { func (svc *Service) isPatternValid() error {
@ -152,13 +152,13 @@ func (svc *Service) isPatternValid() error {
// empty pattern // empty pattern
if length < 1 { if length < 1 {
return errInvalidPattern return ErrInvalidPattern
} }
if length > 1 { if length > 1 {
// pattern not starting with '/' or ending with '/' // pattern not starting with '/' or ending with '/'
if svc.Pattern[0] != '/' || svc.Pattern[length-1] == '/' { if svc.Pattern[0] != '/' || svc.Pattern[length-1] == '/' {
return errInvalidPattern return ErrInvalidPattern
} }
} }
@ -166,7 +166,7 @@ func (svc *Service) isPatternValid() error {
parts := SplitURL(svc.Pattern) parts := SplitURL(svc.Pattern)
for i, part := range parts { for i, part := range parts {
if len(part) < 1 { if len(part) < 1 {
return errInvalidPattern return ErrInvalidPattern
} }
// if brace capture // if brace capture
@ -187,7 +187,7 @@ func (svc *Service) isPatternValid() error {
// fail on invalid format // fail on invalid format
if strings.ContainsAny(part, "{}") { if strings.ContainsAny(part, "{}") {
return errInvalidPatternBraceCapture return ErrInvalidPatternBraceCapture
} }
} }
@ -206,7 +206,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
// for each parameter // for each parameter
for paramName, param := range svc.Input { for paramName, param := range svc.Input {
if len(paramName) < 1 { if len(paramName) < 1 {
return fmt.Errorf("%s: %w", paramName, errIllegalParamName) return fmt.Errorf("%s: %w", paramName, ErrIllegalParamName)
} }
// fail if brace capture does not exists in pattern // fail if brace capture does not exists in pattern
@ -223,7 +223,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
} }
} }
if !found { if !found {
return fmt.Errorf("%s: %w", paramName, errUnspecifiedBraceCapture) return fmt.Errorf("%s: %w", paramName, ErrUnspecifiedBraceCapture)
} }
iscapture = true iscapture = true
@ -246,7 +246,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
// fail if capture or query without rename // fail if capture or query without rename
if len(param.Rename) < 1 && (iscapture || isquery) { if len(param.Rename) < 1 && (iscapture || isquery) {
return fmt.Errorf("%s: %w", paramName, errMandatoryRename) return fmt.Errorf("%s: %w", paramName, ErrMandatoryRename)
} }
// use param name if no rename // use param name if no rename
@ -261,7 +261,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
// capture parameter cannot be optional // capture parameter cannot be optional
if iscapture && param.Optional { if iscapture && param.Optional {
return fmt.Errorf("%s: %w", paramName, errIllegalOptionalURIParam) return fmt.Errorf("%s: %w", paramName, ErrIllegalOptionalURIParam)
} }
// fail on name/rename conflict // fail on name/rename conflict
@ -275,7 +275,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
// 3.2.2. Not-renamed field matches a renamed field // 3.2.2. Not-renamed field matches a renamed field
// 3.2.3. Renamed field matches name // 3.2.3. Renamed field matches name
if param.Rename == param2.Rename || paramName == param2.Rename || paramName2 == param.Rename { if param.Rename == param2.Rename || paramName == param2.Rename || paramName2 == param.Rename {
return fmt.Errorf("%s: %w", paramName, errParamNameConflict) return fmt.Errorf("%s: %w", paramName, ErrParamNameConflict)
} }
} }
@ -296,7 +296,7 @@ func (svc *Service) validateOutput(types []validator.Type) error {
// for each parameter // for each parameter
for paramName, param := range svc.Output { for paramName, param := range svc.Output {
if len(paramName) < 1 { if len(paramName) < 1 {
return fmt.Errorf("%s: %w", paramName, errIllegalParamName) return fmt.Errorf("%s: %w", paramName, ErrIllegalParamName)
} }
// use param name if no rename // use param name if no rename
@ -310,7 +310,7 @@ func (svc *Service) validateOutput(types []validator.Type) error {
} }
if param.Optional { if param.Optional {
return fmt.Errorf("%s: %w", paramName, errOptionalOption) return fmt.Errorf("%s: %w", paramName, ErrOptionalOption)
} }
// fail on name/rename conflict // fail on name/rename conflict
@ -324,7 +324,7 @@ func (svc *Service) validateOutput(types []validator.Type) error {
// 3.2.2. Not-renamed field matches a renamed field // 3.2.2. Not-renamed field matches a renamed field
// 3.2.3. Renamed field matches name // 3.2.3. Renamed field matches name
if param.Rename == param2.Rename || paramName == param2.Rename || paramName2 == param.Rename { if param.Rename == param2.Rename || paramName == param2.Rename || paramName2 == param.Rename {
return fmt.Errorf("%s: %w", paramName, errParamNameConflict) return fmt.Errorf("%s: %w", paramName, ErrParamNameConflict)
} }
} }