feat: complete cmpp gateway delivery recovery workflows
This commit is contained in:
@@ -9,6 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmpp-platform/gateway/internal/inbound"
|
||||
"cmpp-platform/gateway/internal/queue"
|
||||
"cmpp-platform/gateway/internal/upstream"
|
||||
|
||||
cmpp "github.com/bigwhite/gocmpp"
|
||||
)
|
||||
|
||||
@@ -53,9 +57,17 @@ type ConnectionStateCallback struct {
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
APIBaseURL string
|
||||
HTTPClient *http.Client
|
||||
Dial DialFunc
|
||||
APIBaseURL string
|
||||
HTTPClient *http.Client
|
||||
Dial DialFunc
|
||||
Upstream *upstream.Manager
|
||||
RecoveryCandidates func(context.Context) ([]inbound.DownstreamPresence, error)
|
||||
RecoveryStatuses func(context.Context) ([]inbound.DownstreamRecoveryStatus, error)
|
||||
}
|
||||
|
||||
type DownstreamRecoveryOverview struct {
|
||||
Candidates []inbound.DownstreamPresence `json:"candidates"`
|
||||
Statuses []inbound.DownstreamRecoveryStatus `json:"statuses"`
|
||||
}
|
||||
|
||||
func Register(mux *http.ServeMux, server Server) {
|
||||
@@ -65,7 +77,16 @@ func Register(mux *http.ServeMux, server Server) {
|
||||
if server.Dial == nil {
|
||||
server.Dial = DialCMPP
|
||||
}
|
||||
if server.Upstream == nil {
|
||||
server.Upstream = &upstream.Manager{APIBaseURL: server.APIBaseURL, HTTPClient: server.HTTPClient}
|
||||
}
|
||||
mux.HandleFunc("/connections/connect", server.handleConnectChannel)
|
||||
mux.HandleFunc("/upstream/submit", server.handleUpstreamSubmit)
|
||||
mux.HandleFunc("/downstream/receipt", server.handleDownstreamReceipt)
|
||||
mux.HandleFunc("/downstream/uplink", server.handleDownstreamUplink)
|
||||
mux.HandleFunc("/downstream/recovery-candidates", server.handleDownstreamRecoveryCandidates)
|
||||
mux.HandleFunc("/downstream/recovery-statuses", server.handleDownstreamRecoveryStatuses)
|
||||
mux.HandleFunc("/downstream/recovery-overview", server.handleDownstreamRecoveryOverview)
|
||||
}
|
||||
|
||||
func (s Server) handleConnectChannel(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -111,6 +132,132 @@ func (s Server) handleConnectChannel(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(status)
|
||||
}
|
||||
|
||||
func (s Server) handleUpstreamSubmit(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var command queue.SubmitCommand
|
||||
if err := json.NewDecoder(r.Body).Decode(&command); err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid submit command: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
result, err := s.Upstream.Submit(r.Context(), command)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
_ = json.NewEncoder(w).Encode(result)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
func (s Server) handleDownstreamReceipt(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var event inbound.DownstreamReceipt
|
||||
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid downstream receipt: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
delivered, err := inbound.PushReceipt(event)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"delivered": delivered})
|
||||
}
|
||||
|
||||
func (s Server) handleDownstreamUplink(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var event inbound.DownstreamUplink
|
||||
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid downstream uplink: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
delivered, err := inbound.PushUplink(event)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"delivered": delivered})
|
||||
}
|
||||
|
||||
func (s Server) handleDownstreamRecoveryCandidates(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if s.RecoveryCandidates == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode([]inbound.DownstreamPresence{})
|
||||
return
|
||||
}
|
||||
candidates, err := s.RecoveryCandidates(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to load recovery candidates: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(candidates)
|
||||
}
|
||||
|
||||
func (s Server) handleDownstreamRecoveryStatuses(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if s.RecoveryStatuses == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode([]inbound.DownstreamRecoveryStatus{})
|
||||
return
|
||||
}
|
||||
statuses, err := s.RecoveryStatuses(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to load recovery statuses: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(statuses)
|
||||
}
|
||||
|
||||
func (s Server) handleDownstreamRecoveryOverview(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
candidates := []inbound.DownstreamPresence{}
|
||||
if s.RecoveryCandidates != nil {
|
||||
result, err := s.RecoveryCandidates(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to load recovery candidates: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
candidates = result
|
||||
}
|
||||
statuses := []inbound.DownstreamRecoveryStatus{}
|
||||
if s.RecoveryStatuses != nil {
|
||||
result, err := s.RecoveryStatuses(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to load recovery statuses: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
statuses = result
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(DownstreamRecoveryOverview{
|
||||
Candidates: candidates,
|
||||
Statuses: statuses,
|
||||
})
|
||||
}
|
||||
|
||||
func DialCMPP(ctx context.Context, command ConnectChannelCommand) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, defaultConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
Reference in New Issue
Block a user