// execute retrieves the records matching a *query, which the user can read, and marshals them to attr maps. // // Errors: // - db.Query error // - access.CanRead error // - iter.Close error func execute(db data.DB, u *models.User, q *query) ([]map[string]interface{}, error) { rs := make([]map[string]interface{}, 0) iter, err := db.Query(q.Kind).Limit(q.Limit).Batch(q.Batch).Skip(q.Skip).Select(q.Select).Order(q.Order...).Execute() if err != nil { return nil, err } m := models.ModelFor(q.Kind) for iter.Next(m) { ok, err := access.CanRead(db, u, m) if err != nil { return nil, err } if !ok { continue } temp := make(map[string]interface{}) transfer.TransferAttrs(m, &temp) rs = append(rs, temp) m = models.ModelFor(q.Kind) } if err := iter.Close(); err != nil { return nil, err } return rs, nil }
// RecordQueryPOST implements gaia's response to a POST request to the '/record/query/' endpoint. // // Assumptions: The user has been authenticated. // // Proceedings: // // Success: // * StatusOK // // Error: // * InternalServerError: parsing url params, // * BadRequest: no kind parameter, unrecognized kind func RecordQueryPOST(ctx context.Context, w http.ResponseWriter, r *http.Request, logger services.Logger, db data.DB) { l := logger.WithPrefix("RecordQueryPOST: ") // Parse the form if err := r.ParseForm(); err != nil { l.Printf("error parsing form: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // Retrieve the kind parameter k := r.FormValue(kindParam) if k == "" { l.Printf("no kind parameter") http.Error(w, fmt.Sprintf("You must specify a %q parameter", kindParam), http.StatusBadRequest) return } kind := data.Kind(k) // Verify the kind is recognized if !models.Kinds[kind] { l.Printf("unrecognized kind %q", kind) http.Error(w, fmt.Sprintf("The kind %q is not recognized", kind), http.StatusBadRequest) return } // Retrieve the limit, batch and skip parameters lim := r.FormValue(limitParam) bat := r.FormValue(batchParam) ski := r.FormValue(skipParam) // Set up the variables to apply to the query var limit, batch, skip int if lim != "" { limit, _ = strconv.Atoi(lim) } else if bat != "" { batch, _ = strconv.Atoi(bat) } else if ski != "" { skip, _ = strconv.Atoi(ski) } // Read the selection attrs from the body var requestBody []byte var err error defer r.Body.Close() if requestBody, err = ioutil.ReadAll(r.Body); err != nil { l.Printf("error while reading request body: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // These are the selectors, unmarshal the request body into them attrs := make(data.AttrMap) // only unmarshall if there is any request body if len(requestBody) > 0 { if err = json.Unmarshal(requestBody, &attrs); err != nil { l.Printf("info: request body:\n%s", string(requestBody)) l.Printf("error: while unmarshalling request body, %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } } // Retrieve the user we are acting on behalf u, ok := user.FromContext(ctx) if !ok { l.Print("failed to retrieve user from context") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // Load our actual query var iter data.Iterator if iter, err = db.Query(kind).Select(attrs).Limit(limit).Batch(batch).Skip(skip).Order(r.Form["order"]...).Execute(); err != nil { l.Printf("db.Query(%q).Select(%v).Limit(%d).Batch(%d).Skip(%d) error: %s", kind, attrs, limit, batch, skip, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } fmt.Fprint(w, "[") first := true // Iterator through the results and write the response m := models.ModelFor(kind) for iter.Next(m) { if !first { fmt.Fprint(w, ",") } first = false if ok, err := access.CanRead(db, u, m); err != nil { // We've hit an error and need to bail l.Printf("access.CanRead error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } else if ok { bytes, err := json.Marshal(m) if err != nil { l.Printf("error marshalling JSON: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Write(bytes) } } if err := iter.Close(); err != nil { l.Printf("error closing query, %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } fmt.Fprint(w, "]") }