// SetSort parses and validate a sort parameter and set it as lookup's Sort func (l *Lookup) SetSort(sort string, validator schema.Validator) error { sorts := []string{} for _, f := range strings.Split(sort, ",") { f = strings.Trim(f, " ") if f == "" { return errors.New("empty soft field") } // If the field start with - (to indicate descended sort), shift it before // validator lookup i := 0 if f[0] == '-' { i = 1 } // Make sure the field exists field := validator.GetField(f[i:]) if field == nil { return fmt.Errorf("invalid sort field: %s", f[i:]) } if !field.Sortable { return fmt.Errorf("field is not sortable: %s", f[i:]) } sorts = append(sorts, f) } l.sort = sorts return nil }
// checkReferences checks that fields with the Reference validator reference an existing object func (r *requestHandler) checkReferences(payload map[string]interface{}, s schema.Validator) *Error { for name, value := range payload { field := s.GetField(name) if field == nil { continue } // Check reference if validator is of type Reference if field.Validator != nil { if ref, ok := field.Validator.(*schema.Reference); ok { resource := r.h.getResource(ref.Path) if resource == nil { return &Error{500, fmt.Sprintf("Invalid resource reference for field `%s': %s", name, ref.Path), nil} } lookup := NewLookup() lookup.Fields["id"] = value list, _ := resource.handler.Find(lookup, 1, 1) if len(list.Items) == 0 { return &Error{404, fmt.Sprintf("Resource reference not found for field `%s'", name), nil} } } } // Check sub-schema if any if field.Schema != nil && value != nil { if subPayload, ok := value.(map[string]interface{}); ok { if err := r.checkReferences(subPayload, field.Schema); err != nil { return err } } } } return nil }
func applySelector(s []Field, v schema.Validator, p map[string]interface{}, resolver ReferenceResolver) (map[string]interface{}, error) { res := map[string]interface{}{} for _, f := range s { if val, found := p[f.Name]; found { name := f.Name // Handle aliasing if f.Alias != "" { name = f.Alias } // Handle selector params if len(f.Params) > 0 { def := v.GetField(f.Name) if def == nil || def.Params == nil { return nil, fmt.Errorf("%s: params not allowed", f.Name) } var err error val, err = def.Params.Handler(val, f.Params) if err != nil { return nil, fmt.Errorf("%s: %s", f.Name, err.Error()) } } // Handle sub field selection (if field has a value) if len(f.Fields) > 0 && val != nil { def := v.GetField(f.Name) if def != nil && def.Schema != nil { subval, ok := val.(map[string]interface{}) if !ok { return nil, fmt.Errorf("%s: invalid value: not a dict", f.Name) } var err error res[name], err = applySelector(f.Fields, def.Schema, subval, resolver) if err != nil { return nil, fmt.Errorf("%s.%s", f.Name, err.Error()) } } else if ref, ok := def.Validator.(*schema.Reference); ok { // Sub-field on a reference (sub-request) subres, subval, err := resolver(ref.Path, val) if err != nil { return nil, fmt.Errorf("%s: error fetching sub-field: %s", f.Name, err.Error()) } res[name], err = applySelector(f.Fields, subres.validator, subval, resolver) } else { return nil, fmt.Errorf("%s: field as no children", f.Name) } } else { res[name] = val } } } return res, nil }
func validateSelector(s []Field, v schema.Validator) error { for _, f := range s { def := v.GetField(f.Name) if def == nil { return fmt.Errorf("%s: unknown field", f.Name) } if len(f.Fields) > 0 { if def.Schema != nil { // Sub-field on a dict (sub-schema) if err := validateSelector(f.Fields, def.Schema); err != nil { return fmt.Errorf("%s.%s", f.Name, err.Error()) } } else if _, ok := def.Validator.(*schema.Reference); ok { // Sub-field on a reference (sub-request) } else { return fmt.Errorf("%s: field as no children", f.Name) } } // TODO: support connections if len(f.Params) > 0 { if def.Params == nil { return fmt.Errorf("%s: params not allowed", f.Name) } for param, value := range f.Params { val, found := def.Params.Validators[param] if !found { return fmt.Errorf("%s: unsupported param name: %s", f.Name, param) } value, err := val.Validate(value) if err != nil { return fmt.Errorf("%s: invalid param `%s' value: %s", f.Name, param, err.Error()) } f.Params[param] = value } } } return nil }
// checkReferences ensures that fields with the Reference validator reference an existing object func checkReferences(ctx context.Context, payload map[string]interface{}, s schema.Validator) *Error { for name, value := range payload { field := s.GetField(name) if field == nil { continue } // Check reference if validator is of type Reference if field.Validator != nil { if ref, ok := field.Validator.(*schema.Reference); ok { router, ok := IndexFromContext(ctx) if !ok { return &Error{500, "Router not available in context", nil} } rsrc, found := router.GetResource(ref.Path, nil) if !found { return &Error{500, fmt.Sprintf("Invalid resource reference for field `%s': %s", name, ref.Path), nil} } _, err := rsrc.Get(ctx, value) if err == resource.ErrNotFound { return &Error{404, fmt.Sprintf("Resource reference not found for field `%s'", name), nil} } else if err != nil { return &Error{500, fmt.Sprintf("Error fetching resource reference for field `%s': %v", name, err), nil} } } } // Check sub-schema if any if field.Schema != nil && value != nil { if subPayload, ok := value.(map[string]interface{}); ok { if err := checkReferences(ctx, subPayload, field.Schema); err != nil { return err } } } } return nil }
// checkReferences checks that fields with the Reference validator reference an existing object func (r *request) checkReferences(ctx context.Context, payload map[string]interface{}, s schema.Validator) *Error { for name, value := range payload { field := s.GetField(name) if field == nil { continue } // Check reference if validator is of type Reference if field.Validator != nil { if ref, ok := field.Validator.(*schema.Reference); ok { router, ok := IndexFromContext(ctx) if !ok { return &Error{500, "Router not available in context", nil} } rsrc, _, found := router.GetResource(ref.Path) if !found { return &Error{500, fmt.Sprintf("Invalid resource reference for field `%s': %s", name, ref.Path), nil} } l := resource.NewLookup() l.AddQuery(schema.Query{schema.Equal{Field: "id", Value: value}}) list, _ := rsrc.Find(ctx, l, 1, 1) if len(list.Items) == 0 { return &Error{404, fmt.Sprintf("Resource reference not found for field `%s'", name), nil} } } } // Check sub-schema if any if field.Schema != nil && value != nil { if subPayload, ok := value.(map[string]interface{}); ok { if err := r.checkReferences(ctx, subPayload, field.Schema); err != nil { return err } } } } return nil }
// validateFilter recursively validates the format of a filter query func validateFilter(f map[string]interface{}, validator schema.Validator, parentKey string) error { for key, exp := range f { switch key { case "$ne", "$gt", "$gte", "$lt", "$lte": op := key if parentKey == "" { return fmt.Errorf("%s can't be at first level", op) } if _, ok := isNumber(exp); !ok { return fmt.Errorf("%s: value for %s must be a number", parentKey, op) } if field := validator.GetField(parentKey); field != nil { if field.Validator != nil { switch field.Validator.(type) { case schema.Integer, schema.Float: // Ok default: return fmt.Errorf("%s: cannot apply %s operation on a non numerical field", parentKey, op) } } } case "$in", "$nin": op := key if parentKey == "" { return fmt.Errorf("%s can't be at first level", op) } if _, ok := exp.(map[string]interface{}); ok { return fmt.Errorf("%s: value for %s can't be a dict", parentKey, op) } case "$or": var subFilters []interface{} var ok bool if subFilters, ok = exp.([]interface{}); !ok { return fmt.Errorf("%s: value for $or must be an array of dicts", parentKey) } if len(subFilters) < 2 { return fmt.Errorf("%s: $or must contain at least to elements", parentKey) } for _, subFilter := range subFilters { if sf, ok := subFilter.(map[string]interface{}); !ok { return fmt.Errorf("%s: value for $or must be an array of dicts", parentKey) } else if err := validateFilter(sf, validator, ""); err != nil { return err } } default: // Exact match if subFilter, ok := exp.(map[string]interface{}); ok { if parentKey != "" { return fmt.Errorf("%s: invalid expression", parentKey) } if err := validateFilter(subFilter, validator, key); err != nil { return err } } else { if field := validator.GetField(key); field != nil { if field.Validator != nil { if _, err := field.Validator.Validate(exp); err != nil { return fmt.Errorf("invalid filter expression for field `%s': %s", key, err) } } } else { return fmt.Errorf("unknown filter field: %s", key) } } } } return nil }