package config import ( "encoding/json" "fmt" "io" "net/http" "strings" ) // Parse builds a service from a json reader and checks for most format errors. func Parse(r io.Reader) (*Service, error) { receiver := &Service{} err := json.NewDecoder(r).Decode(receiver) if err != nil { return nil, err } err = receiver.checkAndFormat("/") if err != nil { return nil, err } return receiver, nil } // Method returns the actual method from the http method. func (svc *Service) Method(httpMethod string) *Method { httpMethod = strings.ToUpper(httpMethod) switch httpMethod { case http.MethodGet: return svc.GET case http.MethodPost: return svc.POST case http.MethodPut: return svc.PUT case http.MethodDelete: return svc.DELETE } return nil } // Browse the service childtree and returns the farthest matching child. The `path` is a formatted URL split by '/' func (svc *Service) Browse(path []string) (*Service, int) { currentService := svc var depth int // for each URI depth for depth = 0; depth < len(path); depth++ { currentPath := path[depth] child, exists := currentService.Children[currentPath] if !exists { break } currentService = child } return currentService, depth } // checkAndFormat checks for errors and missing fields and sets default values for optional fields. func (svc *Service) checkAndFormat(servicePath string) error { // 1. check and format every method for _, httpMethod := range availableHTTPMethods { methodDef := svc.Method(httpMethod) if methodDef == nil { continue } err := methodDef.checkAndFormat(servicePath, httpMethod) if err != nil { return err } } // 1. stop if no child */ if svc.Children == nil || len(svc.Children) < 1 { return nil } // 2. for each controller */ for childService, ctl := range svc.Children { // 3. invalid name */ if strings.ContainsAny(childService, "/-") { return fmt.Errorf("Controller '%s' must not contain any slash '/' nor '-' symbols", childService) } // 4. check recursively */ err := ctl.checkAndFormat(childService) if err != nil { return err } } return nil }