feat: expose result subscription HTTP and WebSocket APIs

This commit is contained in:
2026-09-17 19:57:55 +08:00
parent 1c3300cc5b
commit 8485a662b2
38 changed files with 3462 additions and 26 deletions

View File

@@ -0,0 +1,88 @@
package events
import (
"context"
"time"
"git.yuntuo.hk/k2safe/orbit/internal/fault"
core "git.yuntuo.hk/k2safe/orbit/model"
"git.yuntuo.hk/k2safe/orbit/platform/auth"
platform "git.yuntuo.hk/k2safe/orbit/platform/model"
)
type PageInput struct {
Limit int
Cursor string
State platform.DeliveryState
}
type SubscriptionPage struct {
Items []Subscription `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
}
type DeliveryPage struct {
Items []Delivery `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
}
type DeliveryDetail struct {
Delivery Delivery `json:"delivery"`
Event Event `json:"event"`
}
// ConsumerBinding 由认证关系派生,不接受客户端指定应用、队列或凭据归属。
type ConsumerBinding struct {
ApplicationID core.ID
SubscriptionID core.ID
QueueID core.ID
Enabled bool
ServerTime time.Time
}
func normalizePage(in PageInput) (PageInput, error) {
if in.Limit == 0 {
in.Limit = 20
}
if in.Limit < 1 || in.Limit > 100 || len(in.Cursor) > 2048 {
return in, fault.Invalid("invalid event page")
}
switch in.State {
case "", platform.DeliveryPending, platform.DeliveryLeased, platform.DeliveryAcked, platform.DeliveryExpired:
default:
return in, fault.Invalid("invalid delivery state")
}
return in, nil
}
func (s *Service) List(ctx context.Context, t auth.Token, queue core.ID, in PageInput) (SubscriptionPage, error) {
in, err := normalizePage(in)
if err != nil {
return SubscriptionPage{}, err
}
if queue.Validate() != nil || in.State != "" {
return SubscriptionPage{}, fault.Invalid("invalid subscription query")
}
return s.store.ListSubscriptions(ctx, t, queue, in)
}
func (s *Service) Deliveries(ctx context.Context, t auth.Token, id core.ID, in PageInput) (DeliveryPage, error) {
in, err := normalizePage(in)
if err != nil {
return DeliveryPage{}, err
}
if id.Validate() != nil {
return DeliveryPage{}, fault.Invalid("invalid subscription_id")
}
return s.store.ListDeliveries(ctx, t, id, in)
}
func (s *Service) Detail(ctx context.Context, t auth.Token, sub, id core.ID) (DeliveryDetail, error) {
if sub.Validate() != nil || id.Validate() != nil {
return DeliveryDetail{}, fault.Invalid("invalid subscription_id or delivery_id")
}
return s.store.GetDeliveryDetail(ctx, t, sub, id)
}
func (s *Service) Authorize(ctx context.Context, t auth.Token) error {
return s.store.AuthorizeEvents(ctx, t)
}
func (s *Service) Bind(ctx context.Context, t auth.Token, id core.ID) (ConsumerBinding, error) {
if id.Validate() != nil {
return ConsumerBinding{}, fault.Invalid("invalid subscription_id")
}
return s.store.BindConsumer(ctx, t, id)
}

View File

@@ -99,6 +99,7 @@ type ControlInput struct {
}
type Delivery struct {
CreatedAt time.Time `json:"created_at,omitzero"`
ID core.ID `json:"id"`
SubscriptionID core.ID `json:"subscription_id"`
EventID core.ID `json:"event_id"`
@@ -152,6 +153,11 @@ type RetryResult struct {
}
type Store interface {
AuthorizeEvents(context.Context, auth.Token) error
BindConsumer(context.Context, auth.Token, core.ID) (ConsumerBinding, error)
ListSubscriptions(context.Context, auth.Token, core.ID, PageInput) (SubscriptionPage, error)
ListDeliveries(context.Context, auth.Token, core.ID, PageInput) (DeliveryPage, error)
GetDeliveryDetail(context.Context, auth.Token, core.ID, core.ID) (DeliveryDetail, error)
CreateSubscription(context.Context, auth.Token, string, CreateInput) (SubscriptionResult, error)
GetSubscription(context.Context, auth.Token, core.ID) (Subscription, error)
ControlSubscription(context.Context, auth.Token, core.ID, string, ControlInput) (SubscriptionResult, error)