tempestas

A REST API for processing sensor.community data
git clone https://git.bracken.jp/tempestas.git
Log | Files | Refs | README | LICENSE

commit 0d6c3e32ae774ba0a91ae1a76df13f0b280f03a7
parent 643d13b91062e6bbd86bf9b93adffdfb47350b1b
Author: Chris Bracken <chris@bracken.jp>
Date:   Thu, 11 Nov 2021 16:45:41 -0800

Add JSON API to query readings

Adds an HTTP GET endpoint at /sensor/reading/ to query readings from
storage.

Diffstat:
Mhttp/http.go | 18++++++++++++++++++
Mstorage/storage.go | 39++++++++++++++++++++++++++++++++++-----
2 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/http/http.go b/http/http.go @@ -25,6 +25,7 @@ func CreateServer(ds storage.DataStore) *HttpServer { func (s HttpServer) ListenAndServe(addr string) error { mux := goji.NewMux() mux.HandleFunc(pat.Post("/sensor/airrohr/"), s.PostAirrohr) + mux.HandleFunc(pat.Get("/sensor/reading/"), s.QueryReadings) return http.ListenAndServe(addr, mux) } @@ -52,6 +53,23 @@ func (s HttpServer) PostAirrohr(w http.ResponseWriter, r *http.Request) { errorResponse(w, "Success", http.StatusOK) } +func (s HttpServer) QueryReadings(w http.ResponseWriter, r *http.Request) { + start := time.Now().Add(time.Hour * -24) + end := time.Now() + // TODO: extract start/end from r.URL.Query() with a default if not present. + readings, err := s.ds.QueryReadings(start, end) + if err != nil { + fmt.Println("Error: failed to query sensor data: " + err.Error()) + errorResponse(w, "Bad Request. "+err.Error(), http.StatusBadRequest) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + jsonResp, _ := json.Marshal(readings) + w.Write(jsonResp) +} + func errorResponse(w http.ResponseWriter, message string, httpStatusCode int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(httpStatusCode) diff --git a/storage/storage.go b/storage/storage.go @@ -11,19 +11,22 @@ const ( INSERT_READING = "INSERT INTO " + "sensor_data(sensor_id, sw_version, reading_time, reading_type, reading_value) " + "VALUES($1, $2, $3, $4, $5)" + QUERY_READINGS = "SELECT sensor_id, sw_version, reading_time, reading_type, reading_value " + + "FROM sensor_data WHERE reading_time >= $1 AND reading_time < $2" ) type Reading struct { - SensorId string - SoftwareVersion string - Time time.Time - Type string - Value float64 + SensorId string `json:"sensor_id"` + SoftwareVersion string `json:"sw_version"` + Time time.Time `json:"time",string` + Type string `json:"type"` + Value float64 `json:"value"` } type DataStore interface { Close() StoreReading(r *Reading) error + QueryReadings(start time.Time, end time.Time) ([]Reading, error) } type PostgresDataStore struct { @@ -59,6 +62,27 @@ func (s *PostgresDataStore) StoreReading(r *Reading) error { return err } +func (s *PostgresDataStore) QueryReadings(start time.Time, end time.Time) ([]Reading, error) { + rows, err := s.db.Query(QUERY_READINGS, start, end) + if err != nil { + return nil, err + } + defer rows.Close() + + var readings []Reading + for rows.Next() { + var r Reading + if err := rows.Scan(&r.SensorId, &r.SoftwareVersion, &r.Time, &r.Type, &r.Value); err != nil { + return readings, err + } + readings = append(readings, r) + } + if err = rows.Err(); err != nil { + return readings, err + } + return readings, nil +} + type NullDataStore struct { } @@ -72,3 +96,8 @@ func (s *NullDataStore) Close() { func (s *NullDataStore) StoreReading(r *Reading) error { return nil } + +func (s *NullDataStore) QueryReadings(start time.Time, end time.Time) ([]Reading, error) { + var readings []Reading + return readings, nil +}