From 34fca3d564640514d0af9561dc807d12d8d25c5f Mon Sep 17 00:00:00 2001 From: saji Date: Sat, 24 Feb 2024 16:48:19 -0600 Subject: [PATCH] wip: work on http api --- http.go | 81 +++++++++++++++++++++--------------------- internal/db/getters.go | 63 ++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 43 deletions(-) diff --git a/http.go b/http.go index bb044d9..cbd80c6 100644 --- a/http.go +++ b/http.go @@ -31,13 +31,6 @@ func TelemRouter(log *slog.Logger, broker *Broker, db *db.TelemDb) http.Handler r.Use(middleware.Logger) // TODO: integrate with slog instead of go default logger. r.Use(middleware.Recoverer) - r.Get("/schema", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - // return the spicy json response. - w.WriteHeader(http.StatusOK) - w.Write([]byte(skylab.SkylabDefinitions)) - }) - // heartbeat request. r.Get("/ping", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pong")) @@ -84,39 +77,7 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router { }) // this is to get a single field - r.Get("/{name:[a-z_]+}/{field:[a-z_]+}", func(w http.ResponseWriter, r *http.Request) { - var err error - - // we need a start and end time. If none is provided, - // we use unix epoch as start, and now + 1 day as end. - start := time.Unix(0, 0) - startString := r.URL.Query().Get("start") - if startString != "" { - start, err = time.Parse(time.RFC3339, startString) - if err != nil { - - } - } - end := time.Now().Add(1 * time.Hour) - endParam := r.URL.Query().Get("start") - if endParam != "" { - end, err = time.Parse(time.RFC3339, endParam) - if err != nil { - } - } - name := chi.URLParam(r, "name") - field := chi.URLParam(r, "field") - // TODO: add limit/pagination ? - - res, err := db.GetValues(r.Context(), name, field, start, end) - if err != nil { - // 500 server error: - fmt.Print(err) - } - b, err := json.Marshal(res) - w.Write(b) - - }) + r.Get("/{name:[a-z_]+}/{field:[a-z_]+}") }) @@ -190,6 +151,46 @@ func apiV1PacketSubscribe(broker *Broker, db *db.TelemDb) http.HandlerFunc { } } +// apiV1GetValues is a function that creates a handler for +// getting the specific value from a packet. +// this is useful for OpenMCT or other viewer APIs +func apiV1GetValues(db *db.TelemDb) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var err error + + // we need a start and end time. If none is provided, + // we use unix epoch as start, and now + 1 day as end. + start := time.Unix(0, 0) + startString := r.URL.Query().Get("start") + if startString != "" { + start, err = time.Parse(time.RFC3339, startString) + if err != nil { + + } + } + end := time.Now().Add(1 * time.Hour) + endParam := r.URL.Query().Get("start") + if endParam != "" { + end, err = time.Parse(time.RFC3339, endParam) + if err != nil { + } + } + name := chi.URLParam(r, "name") + field := chi.URLParam(r, "field") + + // TODO: add limit and pagination + + res, err := db.GetValues(r.Context(), name, field, start, end) + if err != nil { + // 500 server error: + fmt.Print(err) + } + b, err := json.Marshal(res) + w.Write(b) + } + +} + // TODO: rename. record is not a clear name. Runs? drives? segments? func apiV1GetRecords(db *db.TelemDb) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/db/getters.go b/internal/db/getters.go index 203de55..c1925c7 100644 --- a/internal/db/getters.go +++ b/internal/db/getters.go @@ -3,9 +3,66 @@ package db import ( "context" "fmt" + "strings" "time" ) +// Modifier augments SQL strings. +type Modifier interface { + ModifyStatement(string) string +} + + +type LimitOffsetModifier struct { + Limit int + Offset int +} + +// BusEventFilter is a filter for bus events. +type BusEventFilter struct { + Names []string + TimerangeStart time.Time + TimerangeEnd time.Time +} + +func (bef *BusEventFilter) String() string { + var sb []string = make([]string, 0, 2) + if len(bef.Names) > 0 { + names := strings.Join(bef.Names, ",") + sb = append(sb, fmt.Sprintf("name IN (%s)", names)) + } + if !bef.TimerangeStart.IsZero() && !bef.TimerangeEnd.IsZero() { + sb = append(sb, fmt.Sprintf("")) + } + return "" +} + +type BusEventElement interface { + Element() string +} + +type NormalExtract struct { + Key string +} + +type JSONExtract struct { + Key string +} + +type BusEventQuery struct { + Elements []BusEventElement + Filter BusEventFilter + Limits LimitOffsetModifier +} + +func (beq *BusEventQuery) String() string { + // select + return "" +} + + +// now we can optionally add a limit. + // Datum is a single measurement - it is more granular than a packet. // the classic example is bms_measurement.current type Datum struct { @@ -23,10 +80,10 @@ func (tdb *TelemDb) GetValues(ctx context.Context, packetName, field string, sta SqlFrag := ` SELECT ts as timestamp, - json_extract(data, '$.current') as val - FROM bus_events WHERE name IS 'bms_measurement' + json_extract(data, '$.' || ?) as val + FROM bus_events WHERE name IS ? ` - rows, err := tdb.db.QueryxContext(ctx, SqlFrag) + rows, err := tdb.db.QueryxContext(ctx, SqlFrag, field, packetName) if err != nil { return nil, err }