func TestRecordsQueryTemplate(t *testing.T) { db := mem.NewDB() creds := make([]map[string]interface{}, 5) for i := 0; i < 5; i++ { _, c, err := user.Create(db, "username", "password") if err != nil { t.Fatal("user.Create error: %s", err) } m := make(map[string]interface{}) transfer.TransferAttrs(c, &m) creds[i] = m } s := &records.QueryData{ Model: models.Metis[models.CredentialKind], Records: creds, } var b bytes.Buffer if err := records.QueryTemplate.Execute(&b, s); err != nil { t.Fatalf("template.Execute error: %s", err) } o := b.String() t.Logf("TemplateOutput:\n%s", o) if got, want := strings.Contains(o, "username"), true; got != want { t.Errorf("strings.Contains(\"username\"): got %t, want %t", got, want) } if got, want := strings.Contains(o, "password"), true; got != want { t.Errorf("strings.Contains(\"password\"): got %t, want %t", got, want) } }
// 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 }
func (db *DB) Changes() *chan *data.Change { ch := make(chan *data.Change) go func() { // setup Params wsURL := db.recordChangesURL(url.Values{ "public": []string{db.Username}, "private": []string{db.Password}, }) ws, err := websocket.Dial(wsURL, "", db.URL) if err != nil { log.Print("FAILED TO CONNECT TO GAIA") close(ch) return } defer ws.Close() var change transfer.ChangeTransport for { if err := websocket.JSON.Receive(ws, &change); err != nil { if err == io.EOF { close(ch) return } close(ch) return } m := models.ModelFor(change.RecordKind) transfer.TransferAttrs(change.Record, m) ch <- data.NewChange(change.ChangeKind, m) } }() return &ch }
func (db *DB) query(q *query) (data.Iterator, error) { url := db.recordQueryURL(url.Values{ "kind": []string{q.kind.String()}, "skip": []string{fmt.Sprintf("%d", q.skip)}, "limit": []string{fmt.Sprintf("%d", q.limit)}, "batch": []string{fmt.Sprintf("%d", q.batch)}, "order": q.order, }) resp, err := db.postJSON(url, q.attrs) if err != nil { return nil, err } switch resp.StatusCode { case http.StatusBadRequest: log.Print("gaia db bad request") fallthrough case http.StatusInternalServerError: return nil, data.ErrNoConnection case http.StatusUnauthorized: return nil, data.ErrAccessDenial case http.StatusOK: defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var resultsUntyped []*data.AttrMap if err := json.Unmarshal(body, &resultsUntyped); err != nil { return nil, err } var results []data.Record for _, attrs := range resultsUntyped { r := models.ModelFor(q.kind) if err := transfer.TransferAttrs(attrs, r); err != nil { return nil, err } results = append(results, r) } out := make(chan data.Record) // cache results in another goroutine go func() { // read them off for _, r := range results { out <- r } close(out) }() return mem.Iter(out), nil default: log.Printf("Unexpected status code: %d", resp.StatusCode) return nil, data.ErrNoConnection } return nil, nil }