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 }