package handler import ( "net/http" "time" "go.uber.org/zap" "github.com/dbiz/cdp/data-layer/api/internal/middleware" "github.com/dbiz/cdp/data-layer/api/internal/repo" "github.com/dbiz/cdp/data-layer/api/internal/service" ) type AnalyticsHandler struct { svc *service.QueryService log *zap.Logger } func NewAnalyticsHandler(svc *service.QueryService, log *zap.Logger) *AnalyticsHandler { return &AnalyticsHandler{svc: svc, log: log} } // --------------------------------------------------------------------------- // Funnel // --------------------------------------------------------------------------- type funnelRequest struct { Steps []string `json:"steps" validate:"required,min=2,max=10,dive,min=1"` From *time.Time `json:"from" validate:"required"` To *time.Time `json:"to" validate:"required,gtfield=From"` WindowSeconds uint32 `json:"window_seconds" validate:"required,min=1,max=2592000"` // up to 30d } func (h *AnalyticsHandler) Funnel(w http.ResponseWriter, r *http.Request) { var req funnelRequest if err := decodeAndValidate(r, &req); err != nil { writeError(w, err) return } ws := middleware.WorkspaceFromCtx(r.Context()) res, err := h.svc.Funnel(r.Context(), repo.FunnelQuery{ WorkspaceID: ws, Steps: req.Steps, From: *req.From, To: *req.To, WindowSeconds: req.WindowSeconds, }) if err != nil { h.log.Error("funnel", zap.String("workspace_id", ws), zap.Error(err)) writeError(w, err) return } writeJSON(w, http.StatusOK, res) } // --------------------------------------------------------------------------- // Retention // --------------------------------------------------------------------------- type retentionRequest struct { InitialEvent string `json:"initial_event" validate:"required,min=1"` ReturnEvent string `json:"return_event" validate:"required,min=1"` From *time.Time `json:"from" validate:"required"` To *time.Time `json:"to" validate:"required,gtfield=From"` Periods int `json:"periods" validate:"omitempty,min=1,max=90"` } func (h *AnalyticsHandler) Retention(w http.ResponseWriter, r *http.Request) { var req retentionRequest if err := decodeAndValidate(r, &req); err != nil { writeError(w, err) return } ws := middleware.WorkspaceFromCtx(r.Context()) res, err := h.svc.Retention(r.Context(), repo.RetentionQuery{ WorkspaceID: ws, InitialEvent: req.InitialEvent, ReturnEvent: req.ReturnEvent, From: *req.From, To: *req.To, Periods: req.Periods, }) if err != nil { h.log.Error("retention", zap.String("workspace_id", ws), zap.Error(err)) writeError(w, err) return } writeJSON(w, http.StatusOK, res) } // --------------------------------------------------------------------------- // Session // --------------------------------------------------------------------------- type sessionRequest struct { From *time.Time `json:"from" validate:"required"` To *time.Time `json:"to" validate:"required,gtfield=From"` TimeoutSeconds uint32 `json:"timeout_seconds" validate:"omitempty,min=60,max=86400"` UserID string `json:"user_id"` Limit int `json:"limit" validate:"omitempty,min=1,max=1000"` Offset int `json:"offset" validate:"omitempty,min=0"` } func (h *AnalyticsHandler) Session(w http.ResponseWriter, r *http.Request) { var req sessionRequest if err := decodeAndValidate(r, &req); err != nil { writeError(w, err) return } if req.TimeoutSeconds == 0 { req.TimeoutSeconds = 30 * 60 } if req.Limit == 0 { req.Limit = 100 } ws := middleware.WorkspaceFromCtx(r.Context()) res, err := h.svc.Sessions(r.Context(), repo.SessionQuery{ WorkspaceID: ws, UserID: req.UserID, From: *req.From, To: *req.To, TimeoutSeconds: req.TimeoutSeconds, Limit: req.Limit, Offset: req.Offset, }) if err != nil { h.log.Error("session", zap.String("workspace_id", ws), zap.Error(err)) writeError(w, err) return } writeJSON(w, http.StatusOK, res) }