From a73bf81902ffd1691a41087dbbab9cda893bc0d2 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 26 Feb 2024 15:13:51 +0100 Subject: [PATCH] feat(backend): added support for conditional recording feature (mobile sessions) --- backend/internal/config/http/config.go | 2 + .../internal/http/router/handlers-mobile.go | 89 +++++++++++-------- backend/internal/http/router/model.go | 2 + backend/internal/http/router/router.go | 9 +- ee/backend/pkg/conditions/conditions.go | 30 ------- 5 files changed, 60 insertions(+), 72 deletions(-) diff --git a/backend/internal/config/http/config.go b/backend/internal/config/http/config.go index 824766c75..466ac1a3a 100644 --- a/backend/internal/config/http/config.go +++ b/backend/internal/config/http/config.go @@ -34,6 +34,8 @@ type Config struct { RecordCanvas bool `env:"RECORD_CANVAS,default=false"` CanvasQuality string `env:"CANVAS_QUALITY,default=low"` CanvasFps int `env:"CANVAS_FPS,default=1"` + MobileQuality string `env:"MOBILE_QUALITY,default=low"` // (low, standard, high) + MobileFps int `env:"MOBILE_FPS,default=1"` WorkerID uint16 } diff --git a/backend/internal/http/router/handlers-mobile.go b/backend/internal/http/router/handlers-mobile.go index 64c024625..fe8011b09 100644 --- a/backend/internal/http/router/handlers-mobile.go +++ b/backend/internal/http/router/handlers-mobile.go @@ -65,6 +65,17 @@ func (e *Router) startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) if err != nil { // Starting the new one dice := byte(rand.Intn(100)) // [0, 100) + // Use condition rate if it's set + if req.Condition != "" { + rate, err := e.services.Conditions.GetRate(p.ProjectID, req.Condition, int(p.SampleRate)) + if err != nil { + log.Printf("can't get condition rate: %s", err) + } else { + p.SampleRate = byte(rate) + } + } else { + log.Printf("project sample rate: %d", p.SampleRate) + } if dice >= p.SampleRate { ResponseWithError(w, http.StatusForbidden, errors.New("cancel"), startTime, r.URL.Path, 0) return @@ -86,44 +97,46 @@ func (e *Router) startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) geoInfo := e.ExtractGeoData(r) - if err := e.services.Sessions.Add(&sessions.Session{ - SessionID: sessionID, - Platform: "ios", - Timestamp: req.Timestamp, - Timezone: req.Timezone, - ProjectID: p.ProjectID, - TrackerVersion: req.TrackerVersion, - RevID: req.RevID, - UserUUID: userUUID, - UserOS: "IOS", - UserOSVersion: req.UserOSVersion, - UserDevice: ios.MapIOSDevice(req.UserDevice), - UserDeviceType: ios.GetIOSDeviceType(req.UserDevice), - UserCountry: geoInfo.Country, - UserState: geoInfo.State, - UserCity: geoInfo.City, - UserDeviceMemorySize: req.DeviceMemory, - UserDeviceHeapSize: req.DeviceMemory, - }); err != nil { - log.Printf("failed to add mobile session to DB: %v", err) - } + if !req.DoNotRecord { + if err := e.services.Sessions.Add(&sessions.Session{ + SessionID: sessionID, + Platform: "ios", + Timestamp: req.Timestamp, + Timezone: req.Timezone, + ProjectID: p.ProjectID, + TrackerVersion: req.TrackerVersion, + RevID: req.RevID, + UserUUID: userUUID, + UserOS: "IOS", + UserOSVersion: req.UserOSVersion, + UserDevice: ios.MapIOSDevice(req.UserDevice), + UserDeviceType: ios.GetIOSDeviceType(req.UserDevice), + UserCountry: geoInfo.Country, + UserState: geoInfo.State, + UserCity: geoInfo.City, + UserDeviceMemorySize: req.DeviceMemory, + UserDeviceHeapSize: req.DeviceMemory, + }); err != nil { + log.Printf("failed to add mobile session to DB: %v", err) + } - sessStart := &messages.IOSSessionStart{ - Timestamp: req.Timestamp, - ProjectID: uint64(p.ProjectID), - TrackerVersion: req.TrackerVersion, - RevID: req.RevID, - UserUUID: userUUID, - UserOS: "IOS", - UserOSVersion: req.UserOSVersion, - UserDevice: ios.MapIOSDevice(req.UserDevice), - UserDeviceType: ios.GetIOSDeviceType(req.UserDevice), - UserCountry: geoInfo.Pack(), - } - log.Printf("mobile session start: %+v", sessStart) + sessStart := &messages.IOSSessionStart{ + Timestamp: req.Timestamp, + ProjectID: uint64(p.ProjectID), + TrackerVersion: req.TrackerVersion, + RevID: req.RevID, + UserUUID: userUUID, + UserOS: "IOS", + UserOSVersion: req.UserOSVersion, + UserDevice: ios.MapIOSDevice(req.UserDevice), + UserDeviceType: ios.GetIOSDeviceType(req.UserDevice), + UserCountry: geoInfo.Pack(), + } + log.Printf("mobile session start: %+v", sessStart) - if err := e.services.Producer.Produce(e.cfg.TopicRawIOS, tokenData.ID, sessStart.Encode()); err != nil { - log.Printf("failed to produce mobile session start message: %v", err) + if err := e.services.Producer.Produce(e.cfg.TopicRawIOS, tokenData.ID, sessStart.Encode()); err != nil { + log.Printf("failed to produce mobile session start message: %v", err) + } } } @@ -132,8 +145,8 @@ func (e *Router) startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) UserUUID: userUUID, SessionID: strconv.FormatUint(tokenData.ID, 10), BeaconSizeLimit: e.cfg.BeaconSizeLimit, - ImageQuality: "standard", // Pull from project settings (low, standard, high) - FrameRate: 3, // Pull from project settings + ImageQuality: e.cfg.MobileQuality, + FrameRate: e.cfg.MobileFps, }, startTime, r.URL.Path, 0) } diff --git a/backend/internal/http/router/model.go b/backend/internal/http/router/model.go index a71b7450d..905ccb5ef 100644 --- a/backend/internal/http/router/model.go +++ b/backend/internal/http/router/model.go @@ -57,6 +57,8 @@ type StartIOSSessionRequest struct { Timestamp uint64 `json:"timestamp"` Timezone string `json:"timezone"` DeviceMemory uint64 `json:"deviceMemory"` + DoNotRecord bool `json:"doNotRecord"` // start record session or not + Condition string `json:"condition"` // condition for start record session } type StartIOSSessionResponse struct { diff --git a/backend/internal/http/router/router.go b/backend/internal/http/router/router.go index f7e3d8fa0..b4815ce65 100644 --- a/backend/internal/http/router/router.go +++ b/backend/internal/http/router/router.go @@ -114,10 +114,11 @@ func (e *Router) init() { "/v1/web/uxt/signals/task": e.sendUXTaskSignal, } getHandlers := map[string]func(http.ResponseWriter, *http.Request){ - "/v1/web/uxt/test/{id}": e.getUXTestInfo, - "/v1/web/uxt/upload-url": e.getUXUploadUrl, - "/v1/web/tags": e.getTags, - "/v1/web/conditions/{project}": e.getConditions, + "/v1/web/uxt/test/{id}": e.getUXTestInfo, + "/v1/web/uxt/upload-url": e.getUXUploadUrl, + "/v1/web/tags": e.getTags, + "/v1/web/conditions/{project}": e.getConditions, + "/v1/mobile/conditions/{project}": e.getConditions, } prefix := "/ingest" diff --git a/ee/backend/pkg/conditions/conditions.go b/ee/backend/pkg/conditions/conditions.go index 59985079a..98a6bc18b 100644 --- a/ee/backend/pkg/conditions/conditions.go +++ b/ee/backend/pkg/conditions/conditions.go @@ -23,36 +23,6 @@ func New(db pool.Pool) Conditions { } } -type ConditionType string - -const ( - VisitedURL ConditionType = "visited_url" - RequestURL ConditionType = "request_url" - ClickLabel ConditionType = "click_label" - ClickSelector ConditionType = "click_selector" - CustomEvent ConditionType = "custom_event" - Exception ConditionType = "exception" - FeatureFlag ConditionType = "feature_flag" - SessionDuration ConditionType = "session_duration" -) - -type ConditionOperator string - -const ( - Is ConditionOperator = "is" - IsNot ConditionOperator = "isNot" - Contains ConditionOperator = "contains" - NotContains ConditionOperator = "notContains" - StartsWith ConditionOperator = "startsWith" - EndsWith ConditionOperator = "endsWith" -) - -type Condition struct { - Type ConditionType `json:"type"` - Operator ConditionOperator `json:"operator"` - Values []string `json:"value"` -} - type ConditionSet struct { Name string `json:"name"` Filters interface{} `json:"filters"`