129 lines
4.1 KiB
Go
129 lines
4.1 KiB
Go
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
|
|
}
|