func WebSensorsAgent(ctx context.Context, db data.DB, u *models.User) { // Get the db's changes, then filter by updates, then // filter by whether this user can read the record changes := data.Filter(data.FilterKind(db.Changes(), models.EventKind), func(c *data.Change) bool { ok, _ := access.CanRead(db, u, c.Record) return ok }) Run: for { select { case c, ok := <-*changes: if !ok { break Run } switch c.Record.(*models.Event).Name { case WEB_SENSOR_LOCATION: webSensorLocation(db, u, c.Record.(*models.Event).Data) } case <-ctx.Done(): break Run } } }
// 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 taskDropGoal(db data.DB, u *models.User, eventData map[string]interface{}) { g, err := tag.ForName(db, u, tag.Goal) if err != nil { log.Printf("agents.taskDropGoal Error: %s", err) return } id, err := db.ParseID(eventData["task_id"].(string)) if err != nil { log.Printf("agents.taskDropGoal Error: %s", err) return } t, err := models.FindTask(db, id) if err != nil { log.Printf("agents.taskMakeGoal Error: %s", err) return } t.ExcludeTag(g) if err := db.Save(t); err != nil { log.Printf("agents.taskMakeGoal Error: %s", err) return } }
func TaskAgent(ctx context.Context, db data.DB, u *models.User) { changes := data.Filter(data.FilterKind(db.Changes(), models.EventKind), func(c *data.Change) bool { ok, _ := access.CanRead(db, u, c.Record) return ok }) Run: for { select { case c, ok := <-*changes: if !ok { break Run } switch c.Record.(*models.Event).Name { case TaskMakeGoal: taskMakeGoal(db, u, c.Record.(*models.Event).Data) case TaskDropGoal: taskDropGoal(db, u, c.Record.(*models.Event).Data) } case <-ctx.Done(): break Run } } }
func createFixture(ui cli.Ui, ownerID string, db data.DB) (fixture *models.Fixture, err error) { ui.Output("Creating a fixture") fixture = models.NewFixture() fixture.SetID(db.NewID()) fixture.OwnerId = ownerID fixture.CreatedAt = time.Now() if fixture.Name, err = stringInput(ui, "Name of the fixture:"); err != nil { return } if fixture.Label, err = boolInput(ui, "Is this a label?"); err != nil { return } if !fixture.Label { if fixture.StartTime, err = timeInput(ui, "Start time of fixture?"); err != nil { return } if fixture.EndTime, err = timeInput(ui, "End time of fixture?"); err != nil { return } } fixture.UpdatedAt = time.Now() err = db.Save(fixture) return }
func newTestUserX(t *testing.T, db data.DB) *models.User { u := new(models.User) u.SetID(db.NewID()) if err := db.Save(u); err != nil { t.Fatalf("Error newTestUserX: %s", err) } return u }
func newTestCalendar(t *testing.T, db olddata.DB, u *oldmodels.User) *oldmodels.Calendar { c := oldmodels.NewCalendar() c.SetID(db.NewID()) c.SetOwner(u) c.CreatedAt = time.Now() c.UpdatedAt = c.CreatedAt return c }
func newTestUser(t *testing.T, db data.DB) *oldmodels.User { u := oldmodels.NewUser() u.SetID(db.NewID()) u.CreatedAt = time.Now() u.UpdatedAt = time.Now() if err := db.Save(u); err != nil { t.Fatalf("Error newTestUser: %s", err) } return u }
func newTestTag(t *testing.T, db data.DB, u *models.User) *models.Tag { tg := models.NewTag() tg.SetID(db.NewID()) tg.SetOwner(u) if err := db.Save(tg); err != nil { t.Fatal(err) } return tg }
func newTestTask(t *testing.T, db data.DB, u *models.User) *models.Task { tsk := new(models.Task) tsk.SetID(db.NewID()) tsk.CreatedAt = models.TimestampFrom(time.Now()) tsk.OwnerId = u.ID().String() tsk.UpdatedAt = models.TimestampFrom(time.Now()) if err := db.Save(tsk); err != nil { t.Fatalf("Error newTestTask: %s", err) } return tsk }
func newTestNote(t *testing.T, db data.DB, u *models.User) *models.Note { note := models.NewNote() note.SetID(db.NewID()) note.CreatedAt = time.Now() note.OwnerId = u.ID().String() note.UpdatedAt = time.Now() if err := db.Save(note); err != nil { t.Fatal("newTestNote Error: %s", err) } return note }
func newTestPerson(t *testing.T, db data.DB, u *models.User) *models.Person { person := models.NewPerson() person.SetID(db.NewID()) person.CreatedAt = time.Now() person.OwnerId = u.ID().String() person.UpdatedAt = time.Now() if err := db.Save(person); err != nil { t.Fatalf("newTestPerson Error: %s", err) } return person }
func newCalendar(db data.DB, userID string) (*models.Calendar, error) { cal := models.NewCalendar() cal.SetID(db.NewID()) cal.CreatedAt = time.Now() cal.Name = "Main" cal.OwnerId = userID cal.UpdatedAt = time.Now() return cal, db.Save(cal) }
func locationUpdate(db data.DB, u *models.User, e *models.Event) { loc, _ := e.Location(db) p, err := user.Profile(db, u) if err == data.ErrNotFound { p = models.NewProfile() p.CreatedAt = time.Now() p.UpdatedAt = p.CreatedAt p.SetID(db.NewID()) p.SetOwner(u) } p.SetLocation(loc) db.Save(p) }
func LocationAgent(ctx context.Context, db data.DB, u *models.User) { locTag, err := tag.ForName(db, u, tag.Location) if err != nil { log.Fatal(err) } updTag, err := tag.ForName(db, u, tag.Update) if err != nil { log.Fatal(err) } // Get the db's changes, then filter by updates, then // filter by whether this user can read the record changes := data.Filter(data.FilterKind(db.Changes(), models.EventKind), func(c *data.Change) bool { ok, _ := access.CanRead(db, u, c.Record) if !ok { return false } return event.ContainsTags(c.Record.(*models.Event), locTag, updTag) }) Run: for { select { case c, ok := <-*changes: if !ok { break Run } locationUpdate(db, u, c.Record.(*models.Event)) case <-ctx.Done(): break Run } } }
// EventPOST implements gaia's response to a POST request to the '/event/' endpoint. // // Assumptions: The user has been authenticated. // // Proceedings: Parses the url parameters. func EventPOST(ctx context.Context, w http.ResponseWriter, r *http.Request, db data.DB, logger services.Logger) { l := logger.WithPrefix("EventPOST: ") // 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 tags parameter tagNames, ok := r.Form[tagsParam] if !ok { l.Print("no tags param") tagNames = []string{} } // if any tag names have commas, split those tagNames = flatten(mapSplit(tagNames, ",")) // Retrieve our user 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 } e := new(models.Event) tags := make([]*models.Tag, len(tagNames)) for i, n := range tagNames { t, err := tag.ForName(db, u, tag.Name(n)) if err != nil { l.Printf("tag.ForName(%q) error: %s", n, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } tags[i] = t } defer r.Body.Close() if requestBody, err := ioutil.ReadAll(r.Body); err != nil { l.Printf("ioutil.ReadAll(r.Body) error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } else if err := json.Unmarshal(requestBody, e); 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 } if e.CreatedAt.IsZero() { e.CreatedAt = time.Now() } e.UpdatedAt = time.Now() e.SetOwner(u) for _, t := range tags { e.IncludeTag(t) } if allowed, err := access.CanCreate(db, u, e); err != nil { l.Printf("access.CanCreate(db, u, e) error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } else if !allowed { l.Print("access.CanCreate(db, u, e) rejected authorization") http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } if err := db.Save(u); err != nil { l.Printf("error saving record: %s", err) switch err { case data.ErrAccessDenial: http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) // These are all equally distressing case data.ErrNotFound: // TODO shouldn't a not found not be fing impossible for a Save? fallthrough case data.ErrNoConnection: fallthrough case data.ErrInvalidID: default: http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } return } // Now we shall write our response b, err := json.MarshalIndent(e, "", " ") if err != nil { l.Printf("json.MarshalIndent(m, \"\", \" \") error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) w.Header().Set("Content-Type", "application/json") w.Write(b) }
func RecordChangesGET(ctx context.Context, ws *websocket.Conn, db data.DB, logger services.Logger) { l := logger.WithPrefix("RecordChangesGet: ") u, ok := user.FromContext(ctx) if !ok { l.Print("failed to retrieve user from context") return } // Get the db's changes, then filter by updates, then // filter by whether this user can read the record changes := data.Filter(db.Changes(), func(c *data.Change) bool { ok, err := access.CanRead(db, u, c.Record) if err != nil { l.Printf("error checking access control: %s", err) } return ok }) var kind data.Kind if kindParam := ws.Request().Form.Get(kindParam); kindParam != "" { kind = data.Kind(kindParam) if _, ok := models.Kinds[kind]; !ok { l.Printf("unrecognized kind: %q", kind) if err := websocket.Message.Send(ws, fmt.Sprintf("The kind %q is not recognized", kind)); err != nil { if err != io.EOF { l.Printf("error sending on websocket: %s", err) } } return } // If a kind was specified, filter by it changes = data.FilterKind(changes, kind) } for { select { case change, ok := <-*changes: if !ok { l.Printf("change channel was closed") return } l.Printf("recieved change: %+v", change) changeTransport := transfer.Change(change) if err := websocket.JSON.Send(ws, changeTransport); err != nil { if err != io.EOF { l.Printf("error sending to socket: %s", err) } return } case <-time.After(5 * time.Second): l.Printf("no change in 5 seconds, but still listening") case <-ctx.Done(): l.Printf("context cancelled") // context was cancelled return } } }
// 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, "]") }
func MobileLocationPOST(ctx context.Context, w http.ResponseWriter, r *http.Request, l services.Logger, db data.DB) { // Parse the form value if err := r.ParseForm(); err != nil { l.Printf("MobileLocationPOST Error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // Altitude alt := r.FormValue(altitudeParam) if alt == "" { http.Error(w, "You must specify an altitude", http.StatusBadRequest) return } altitude, err := strconv.ParseFloat(alt, 64) if err != nil { http.Error(w, "Parsing altitude", http.StatusBadRequest) return } // Latitude lat := r.FormValue(latitudeParam) if lat == "" { http.Error(w, "You must specify a latitude", http.StatusBadRequest) return } latitude, err := strconv.ParseFloat(lat, 64) if err != nil { http.Error(w, "Parsing latitude", http.StatusBadRequest) return } // Longitude lon := r.FormValue(longitudeParam) if lon == "" { http.Error(w, "You must specify an longitude", http.StatusBadRequest) return } longitude, err := strconv.ParseFloat(lon, 64) if err != nil { http.Error(w, "Parsing longitude", http.StatusBadRequest) return } // Retrieve the user this request was authenticated as u, ok := user.FromContext(ctx) if !ok { // This is certainly an issue, and should _never_ happen l.Print("MobileLocationPOST Error: failed to retrieve user from context") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // Create location loc := location.NewCoords(altitude, latitude, longitude) loc.SetID(db.NewID()) loc.SetOwner(u) now := loc.CreatedAt e := models.NewEvent() e.CreatedAt = now e.SetID(db.NewID()) e.SetOwner(u) e.Name = "Location Update" e.SetLocation(loc) e.Time = now e.UpdatedAt = now locationTag, err1 := tag.ForName(db, u, tag.Location) updateTag, err2 := tag.ForName(db, u, tag.Update) mobileTag, err3 := tag.ForName(db, u, tag.Mobile) if err1 != nil || err2 != nil || err3 != nil { l.Printf("MobileLocationPOST Error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } e.IncludeTag(locationTag) e.IncludeTag(updateTag) e.IncludeTag(mobileTag) if err = db.Save(loc); err != nil { l.Printf("MobileLocationPOST Error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if err = db.Save(e); err != nil { l.Printf("MobileLocationPOST Error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } bytes, err := json.MarshalIndent(e, "", " ") if err != nil { l.Printf("MobileLocationPOST Error: while marshalling json %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) w.Header().Set("Content-Type", "application/json") w.Write(bytes) }