func (res *Resource) ApiFind(query *db.Query, r kit.Request) kit.Response { // If query is empty, query for all records. if query == nil { query = res.Q() } apiFindHook, ok := res.hooks.(ApiFindHook) if ok { return apiFindHook.ApiFind(res, query, r) } if alterQuery, ok := res.hooks.(ApiAlterQueryHook); ok { alterQuery.ApiAlterQuery(res, query, r) } result, err := res.Query(query) if err != nil { return kit.NewErrorResponse(err) } user := r.GetUser() if allowFind, ok := res.hooks.(AllowFindHook); ok { finalItems := make([]kit.Model, 0) for _, item := range result { if allowFind.AllowFind(res, item, user) { finalItems = append(finalItems, item) } } result = finalItems } response := &kit.AppResponse{ Data: result, } // If a limit was set, count the total number of results // and set count parameter in metadata. limit := query.GetLimit() if limit > 0 { query.Limit(0).Offset(0) count, err := res.backend.Count(query) if err != nil { return &kit.AppResponse{ Error: apperror.Wrap(err, "count_error", ""), } } response.SetMeta(map[string]interface{}{ "count": count, "total_pages": math.Ceil(float64(count) / float64(limit)), }) } if hook, ok := res.hooks.(ApiAfterFindHook); ok { if err := hook.ApiAfterFind(res, result, r, response); err != nil { return kit.NewErrorResponse(err) } } return response }
func Find(res kit.Resource, request kit.Request) (kit.Response, apperror.Error) { collection := res.Collection() info := res.Backend().ModelInfo(collection) var query *db.Query jsonQuery := request.GetContext().String("query") if jsonQuery != "" { var rawQuery map[string]interface{} if err := json.Unmarshal([]byte(jsonQuery), &rawQuery); err != nil { return nil, apperror.Wrap(err, "invalid_query_json") } rawQuery["collection"] = collection // A custom query was supplied. // Try to parse the query. var err apperror.Error query, err = db.ParseQuery(res.Backend(), rawQuery) if err != nil { return nil, apperror.Wrap(err, "invalid_query", "", false) } } if query == nil { query = res.Backend().Q(collection) } // Check paging parameters. var limit, offset int context := request.GetContext() if context.Has("limit") { val, err := context.Int("limit") if err != nil { return nil, &apperror.Err{ Public: true, Code: "non_numeric_limit_parameter", Message: "The get query contains a non-numeric ?limit", } } limit = val } if context.Has("offset") { val, err := context.Int("offset") if err != nil { return nil, &apperror.Err{ Public: true, Code: "non_numeric_offset_parameter", Message: "The get query contains a non-numeric ?offset", } } offset = val } var page, perPage int if context.Has("page") { val, err := context.Int("page") if err != nil { return nil, &apperror.Err{ Public: true, Code: "non_numeric_page_parameter", Message: "The get query contains a non-numeric ?page", } } page = val } if context.Has("per_page") { val, err := context.Int("per_page") if err != nil { return nil, &apperror.Err{ Public: true, Code: "non_numeric_per_page_parameter", Message: "The get query contains a non-numeric ?per_page", } } perPage = val } if perPage > 0 { limit = perPage } if page > 1 { offset = (page - 1) * limit } if limit > 0 { query.Limit(int(limit)).Offset(int(offset)) } // Add joins. if context.Has("joins") { parts := strings.Split(context.String("joins"), ",") for _, name := range parts { relation := info.FindRelation(name) if relation == nil { return nil, &apperror.Err{ Code: "invalid_join", Message: fmt.Sprintf("Tried to join a NON-existant relationship %v", name), Public: true, } } query.Join(relation.Name()) } } // Add filters. if context.Has("filters") { parts := strings.Split(context.String("filters"), ",") for _, filter := range parts { filterParts := strings.Split(filter, ":") if len(filterParts) != 2 { return nil, &apperror.Err{ Public: true, Code: "invalid_filter", Message: fmt.Sprintf("Invalid filter: %v", filter), } } fieldName := filterParts[0] // COnvert id query to pk field. if fieldName == "id" { fieldName = info.PkAttribute().BackendName() } var typ reflect.Type if attr := info.FindAttribute(fieldName); attr != nil { fieldName = attr.BackendName() typ = attr.Type() } else if rel := info.FindRelation(fieldName); rel != nil { if rel.RelationType() == db.RELATION_TYPE_HAS_ONE { fieldName = rel.LocalField() typ = info.Attribute(rel.LocalField()).Type() } else { return nil, &apperror.Err{ Public: true, Code: "cant_filter_on_relation", Message: fmt.Sprintf("Tried to filter on relationship field %v (only possible for has-one relations)", fieldName), } } } else { return nil, &apperror.Err{ Public: true, Code: "filter_for_inexistant_field", Message: fmt.Sprintf("Tried to filter with inexistant field %v", fieldName), } } converted, err := reflector.R(filterParts[1]).ConvertTo(typ) if err != nil { return nil, &apperror.Err{ Public: true, Code: "inconvertible_filter_value", Message: fmt.Sprintf("Coult not convert filter value %v for field %v (should be %v)", filterParts[1], fieldName, typ), } } query.Filter(fieldName, converted) } } return res.ApiFind(query, request), nil }