diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c68b316..11875de 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -12,6 +12,8 @@ import ( ) func TestLegalServiceName(t *testing.T) { + t.Parallel() + tests := []struct { Raw string Error error @@ -51,7 +53,7 @@ func TestLegalServiceName(t *testing.T) { }, { `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}" } ]`, - nil, + ErrUndefinedBraceCapture, }, { `[ { "method": "GET", "info": "a", "path": "/invalid/s{braces}/abc" } ]`, @@ -63,7 +65,7 @@ func TestLegalServiceName(t *testing.T) { }, { `[ { "method": "GET", "info": "a", "path": "/invalid/{braces}/abc" } ]`, - nil, + ErrUndefinedBraceCapture, }, { `[ { "method": "GET", "info": "a", "path": "/invalid/{b{races}s/abc" } ]`, @@ -99,6 +101,7 @@ func TestLegalServiceName(t *testing.T) { } } func TestAvailableMethods(t *testing.T) { + t.Parallel() tests := []struct { Raw string ValidMethod bool @@ -146,6 +149,7 @@ func TestAvailableMethods(t *testing.T) { } } func TestParseEmpty(t *testing.T) { + t.Parallel() reader := strings.NewReader(`[]`) _, err := Parse(reader) if err != nil { @@ -167,6 +171,7 @@ func TestParseJsonError(t *testing.T) { } func TestParseMissingMethodDescription(t *testing.T) { + t.Parallel() tests := []struct { Raw string ValidDescription bool @@ -217,6 +222,7 @@ func TestParseMissingMethodDescription(t *testing.T) { } func TestParamEmptyRenameNoRename(t *testing.T) { + t.Parallel() reader := strings.NewReader(`[ { "method": "GET", @@ -247,6 +253,7 @@ func TestParamEmptyRenameNoRename(t *testing.T) { } func TestOptionalParam(t *testing.T) { + t.Parallel() reader := strings.NewReader(`[ { "method": "GET", @@ -288,6 +295,7 @@ func TestOptionalParam(t *testing.T) { } func TestParseParameters(t *testing.T) { + t.Parallel() tests := []struct { Raw string Error error @@ -473,6 +481,57 @@ func TestParseParameters(t *testing.T) { ]`, nil, }, + + { // URI parameter + `[ + { + "method": "GET", + "path": "/{uri}", + "info": "info", + "in": { + "{uri}": { "info": "valid", "type": "any", "name": "freename" } + } + } + ]`, + nil, + }, + { // URI parameter cannot be optional + `[ + { + "method": "GET", + "path": "/{uri}", + "info": "info", + "in": { + "{uri}": { "info": "valid", "type": "?any", "name": "freename" } + } + } + ]`, + ErrIllegalOptionalURIParam, + }, + { // URI parameter not specified + `[ + { + "method": "GET", + "path": "/", + "info": "info", + "in": { + "{uri}": { "info": "valid", "type": "?any", "name": "freename" } + } + } + ]`, + ErrUnspecifiedBraceCapture, + }, + { // URI parameter not defined + `[ + { + "method": "GET", + "path": "/{uri}", + "info": "info", + "in": { } + } + ]`, + ErrUndefinedBraceCapture, + }, } for i, test := range tests { @@ -501,6 +560,7 @@ func TestParseParameters(t *testing.T) { } func TestMatchSimple(t *testing.T) { + t.Parallel() tests := []struct { Config string URL string diff --git a/internal/config/errors.go b/internal/config/errors.go index 7a5b9cc..994ddc1 100644 --- a/internal/config/errors.go +++ b/internal/config/errors.go @@ -29,9 +29,15 @@ const ErrInvalidPatternBraceCapture = Error("invalid uri capturing braces") // ErrUnspecifiedBraceCapture - a parameter brace capture is not specified in the pattern const ErrUnspecifiedBraceCapture = Error("capturing brace missing in the path") +// ErrUndefinedBraceCapture - a parameter brace capture in the pattern is not defined in parameters +const ErrUndefinedBraceCapture = Error("capturing brace missing input definition") + // ErrMissingDescription - a service is missing its description const ErrMissingDescription = Error("missing description") +// ErrIllegalOptionalURIParam - an URI parameter cannot be optional +const ErrIllegalOptionalURIParam = Error("URI parameter cannot be optional") + // ErrMissingParamDesc - a parameter is missing its description const ErrMissingParamDesc = Error("missing parameter description") diff --git a/internal/config/server.go b/internal/config/server.go index b197ee0..6347fdb 100644 --- a/internal/config/server.go +++ b/internal/config/server.go @@ -40,6 +40,17 @@ func Parse(r io.Reader, dtypes ...datatype.T) (*Server, error) { return server, nil } +// Find a service matching an incoming HTTP request +func (server Server) Find(r *http.Request) *Service { + for _, service := range server.Services { + if matches := service.Match(r); matches { + return service + } + } + + return nil +} + // collide returns if there is collision between services func (server *Server) collide() error { length := len(server.Services) @@ -120,17 +131,6 @@ func (server *Server) collide() error { return nil } -// Find a service matching an incoming HTTP request -func (server Server) Find(r *http.Request) *Service { - for _, service := range server.Services { - if matches := service.Match(r); matches { - return service - } - } - - return nil -} - // checkAndFormat checks for errors and missing fields and sets default values for optional fields. func (server Server) checkAndFormat() error { for _, service := range server.Services { @@ -159,6 +159,13 @@ func (server Server) checkAndFormat() error { return fmt.Errorf("%s '%s' [in]: %w", service.Method, service.Pattern, err) } + // fail if a brace capture remains undefined + for _, capture := range service.Captures { + if capture.Ref == nil { + return fmt.Errorf("%s '%s' [in]: %s: %w", service.Method, service.Pattern, capture.Name, ErrUndefinedBraceCapture) + } + } + } return nil } diff --git a/internal/config/service.go b/internal/config/service.go index 90d6a1f..abe5e06 100644 --- a/internal/config/service.go +++ b/internal/config/service.go @@ -100,7 +100,8 @@ func (svc *Service) checkAndFormatInput(types []datatype.T) error { return fmt.Errorf("%s: %w", paramName, ErrIllegalParamName) } - // fail if brace does not exists in pattern + // fail if brace capture does not exists in pattern + iscapture := false if matches := braceRegex.FindAllStringSubmatch(paramName, -1); len(matches) > 0 && len(matches[0]) > 1 { braceName := matches[0][1] @@ -115,6 +116,7 @@ func (svc *Service) checkAndFormatInput(types []datatype.T) error { if !found { return fmt.Errorf("%s: %w", paramName, ErrUnspecifiedBraceCapture) } + iscapture = true } // use param name if no rename @@ -127,6 +129,11 @@ func (svc *Service) checkAndFormatInput(types []datatype.T) error { return fmt.Errorf("%s: %w", paramName, err) } + // capture parameter cannot be optional + if iscapture && param.Optional { + return fmt.Errorf("%s: %w", paramName, ErrIllegalOptionalURIParam) + } + if !param.assignDataType(types) { return fmt.Errorf("%s: %w", paramName, ErrUnknownDataType) }