feat: expose result subscription HTTP and WebSocket APIs
This commit is contained in:
128
internal/transport/httpserver/events.go
Normal file
128
internal/transport/httpserver/events.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.yuntuo.hk/k2safe/orbit/internal/fault"
|
||||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||||
"git.yuntuo.hk/k2safe/orbit/platform/auth"
|
||||
"git.yuntuo.hk/k2safe/orbit/platform/events"
|
||||
platform "git.yuntuo.hk/k2safe/orbit/platform/model"
|
||||
)
|
||||
|
||||
func RegisterEventSubscriptions(server *http.Server, s *events.Service) {
|
||||
mux := server.Handler.(*http.ServeMux)
|
||||
mux.HandleFunc("POST /v1/subscriptions", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
if err := noEventQuery(r); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var in events.CreateInput
|
||||
if err := decodeEventBody(r, &in); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
key, err := idempotencyKey(r)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Create(r.Context(), t, key, in)
|
||||
return out, createdStatus(out.Deduplicated), err
|
||||
}))
|
||||
mux.HandleFunc("GET /v1/subscriptions", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
queue, in, err := eventQuery(r, true)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.List(r.Context(), t, queue, in)
|
||||
return out, 200, err
|
||||
}))
|
||||
mux.HandleFunc("GET /v1/subscriptions/{id}", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
if err := noEventQuery(r); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Get(r.Context(), t, core.ID(r.PathValue("id")))
|
||||
return out, 200, err
|
||||
}))
|
||||
mux.HandleFunc("POST /v1/subscriptions/{command}", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
id, command, ok := strings.Cut(r.PathValue("command"), ":")
|
||||
if !ok || command != "configure" {
|
||||
return nil, 0, fault.NotFound
|
||||
}
|
||||
if err := noEventQuery(r); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var in events.ControlInput
|
||||
if err := decodeEventBody(r, &in); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
key, err := idempotencyKey(r)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Control(r.Context(), t, core.ID(id), key, in)
|
||||
return out, 200, err
|
||||
}))
|
||||
mux.HandleFunc("GET /v1/subscriptions/{id}/deliveries", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
_, in, err := eventQuery(r, false)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Deliveries(r.Context(), t, core.ID(r.PathValue("id")), in)
|
||||
return out, 200, err
|
||||
}))
|
||||
mux.HandleFunc("GET /v1/subscriptions/{id}/deliveries/{delivery}", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
if err := noEventQuery(r); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Detail(r.Context(), t, core.ID(r.PathValue("id")), core.ID(r.PathValue("delivery")))
|
||||
return out, 200, err
|
||||
}))
|
||||
mux.HandleFunc("POST /v1/subscriptions/{id}/deliveries/{command}", wrap(func(r *http.Request, t auth.Token) (any, int, error) {
|
||||
delivery, command, ok := strings.Cut(r.PathValue("command"), ":")
|
||||
if !ok || command != "retry" {
|
||||
return nil, 0, fault.NotFound
|
||||
}
|
||||
if err := noEventQuery(r); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var in events.RetryInput
|
||||
if err := decodeEventBody(r, &in); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
key, err := idempotencyKey(r)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out, err := s.Retry(r.Context(), t, core.ID(r.PathValue("id")), core.ID(delivery), key, in)
|
||||
return out, 200, err
|
||||
}))
|
||||
}
|
||||
func noEventQuery(r *http.Request) error {
|
||||
if r.URL.RawQuery != "" || r.URL.ForceQuery {
|
||||
return fault.Invalid("query parameters are not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func eventQuery(r *http.Request, subscriptions bool) (core.ID, events.PageInput, error) {
|
||||
var in events.PageInput
|
||||
q, err := url.ParseQuery(r.URL.RawQuery)
|
||||
if err != nil {
|
||||
return "", in, fault.Invalid("invalid query encoding")
|
||||
}
|
||||
for k, v := range q {
|
||||
if len(v) != 1 || (k != "limit" && k != "cursor" && !(subscriptions && k == "queue_id") && !(!subscriptions && k == "state")) {
|
||||
return "", in, fault.Invalid("invalid subscription query")
|
||||
}
|
||||
}
|
||||
if q.Has("limit") {
|
||||
in.Limit, err = strconv.Atoi(q.Get("limit"))
|
||||
if err != nil || in.Limit < 1 || strconv.Itoa(in.Limit) != q.Get("limit") {
|
||||
return "", in, fault.Invalid("invalid limit")
|
||||
}
|
||||
}
|
||||
in.Cursor = q.Get("cursor")
|
||||
in.State = platform.DeliveryState(q.Get("state"))
|
||||
return core.ID(q.Get("queue_id")), in, nil
|
||||
}
|
||||
Reference in New Issue
Block a user