From d22e4e138e266131abc7aa6eac4fe750d62201e4 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 9 Oct 2023 15:02:20 +0200 Subject: [PATCH] Backend for mobile tracker support (#1456) * feat(backend): handlers for mobile messages * feat(backend): new service template * feat(backend): save mobile session start and send batches to kafka * feat(backend): added mobile support to sink, ender and storage * helm(videostorage): added helm chart for a new service videostorage * fix(backend): added pointer to streing for userBrowser (because it's null for mobile sessions) * feat(backend): added MsgIOSBatchMeta handler to message iterator's logic * feat(backend): added ios ts parser to ender * feat(backend): enabled producing batch of messages to queue * feat(backend): removed iosstart from mob files * feat(backend): added new ios message types * feat(backend): added iosStart and iosEnd * fix(backend): fixed log issue * feat(backend): send tar.gz archives to special queue topic * feat(backend): read raw archives from kafka * fix(backend): added missing file * fix(backend): removed the second file reading * fix(backend): fixed wrong queue topic name * fix(backend): fixed mobile trigger topic name * feat(backend): added tar.gz extractor and iOSSessionEnd handler * feat(backend): debug logs on message uploading * fix(backend): added raw-images topic consumption * feat(backend): now sink send iosSessionEnd to video-storage * feat(backend): added dir creator for new sessions * feat(backend): added correct command to execute * feat(backend): added overwrite option * feat(backend): added s3 uploader for video session replays * feat(backend): new consumer group for mobile sessions * feat(backend): debug logs for uploader * feat(backend): removed unused log * feat(backend): fixed s3 key for video replays * feat(backend): removed debug code * feat(backend): fixed video-storage message filter * fix(backend): added mobileSessionEnd to SessionEnd converter * feat(backend): added first version if db support for mobile events * fix(backend): added swipe events to mob file * feat(backend): added swipe event to pg * feat(backend): split logic into 2 services: image-storage and video-storage * feat(backend): added helm chart for image-storage service * fix(backend): fixed table name for mobile taps * feat(backend): added metadata handler for mobile message parser + fix message filters * feat(backend): added iosRawTopic to DB message consumer * fix(backend): removed value from mobile inputs * feat(backend): removed debug log from iterator * feat(backend): added new apple devices to iOS device parser * fix(backend): added real projectID instead of 0 * feat(backend): extended a list of simulators for device detector * feat(backend): updated networkCall mobile message * fix(backend): added new way to define is network call successed or not * feat(backend): added timezone support for mobile start request * feat(backend): added 2 mobile events Input and Click to mob file * feat(backend): refactored image storage * feat(backend): video storage with 2 workers * feat(backend): added project's platform support * feat(backend): added memory size field for mobile start request * feat(backend): changed video preset for ultrafast * feat(backend): added debug log to http /late handler * feat(backend): added debug log to db service for iosCrash messages * feat(backend): added tapRage event handler to heuristics * feat(backend): changed table and field names for ios crashes * feat(backend): added payload for tapRage events * feat(backend): added TapRage events insert to DB * feat(backend): added fps value to /mobile/start response * feat(backend): added image quality parameter to /mobile/start response * feat(backend): added ScreenLeave handler * feat(backend): removed screenEnter and screenLeave events, added new viewComponent event --------- Co-authored-by: rjshrjndrn --- backend/Dockerfile | 11 +- backend/cmd/db/main.go | 13 +- backend/cmd/ender/main.go | 24 +- backend/cmd/heuristics/main.go | 4 + backend/cmd/imagestorage/main.go | 73 + backend/cmd/sink/main.go | 9 +- backend/cmd/storage/main.go | 11 +- backend/cmd/videostorage/main.go | 87 + backend/internal/config/db/config.go | 1 + backend/internal/config/ender/config.go | 1 + backend/internal/config/http/config.go | 1 + .../internal/config/imagestorage/config.go | 21 + backend/internal/config/sink/config.go | 1 + .../internal/config/videostorage/config.go | 23 + backend/internal/db/datasaver/saver.go | 80 +- backend/internal/http/ios/ios-device.go | 38 +- .../internal/http/router/handlers-mobile.go | 224 ++ backend/internal/http/router/handlers-web.go | 6 + backend/internal/http/router/handlers.go | 14 +- backend/internal/http/router/model.go | 23 + backend/internal/http/router/router.go | 4 + backend/internal/imagestorage/service.go | 133 + backend/internal/sessionender/ender.go | 6 + backend/internal/videostorage/service.go | 114 + backend/pkg/db/postgres/helpers.go | 6 +- backend/pkg/db/postgres/mobile.go | 163 ++ .../handlers/ios/{clickRage.go => tapRage.go} | 65 +- backend/pkg/hashid/hashid.go | 7 + backend/pkg/messages/filters.go | 8 +- backend/pkg/messages/get-timestamp.go | 14 +- backend/pkg/messages/iterator-images.go | 24 + backend/pkg/messages/iterator.go | 14 +- backend/pkg/messages/messages.go | 873 +++--- backend/pkg/messages/read-message.go | 2427 +++++++++-------- backend/pkg/metrics/imagestorage/metrics.go | 155 ++ backend/pkg/metrics/videostorage/metrics.go | 155 ++ backend/pkg/objectstorage/objectstorage.go | 1 + backend/pkg/objectstorage/s3/s3.go | 42 + backend/pkg/projects/model.go | 9 + backend/pkg/projects/storage.go | 8 +- backend/pkg/sessions/storage.go | 7 +- ee/backend/pkg/objectstorage/azure/azure.go | 5 + ee/connectors/msgcodec/messages.py | 75 +- ee/connectors/msgcodec/messages.pyx | 110 +- ee/connectors/msgcodec/msgcodec.py | 54 +- ee/connectors/msgcodec/msgcodec.pyx | 837 +++--- .../web/messages/RawMessageReader.gen.ts | 80 +- .../app/player/web/messages/filters.gen.ts | 2 +- .../app/player/web/messages/message.gen.ts | 9 +- frontend/app/player/web/messages/raw.gen.ts | 48 +- .../player/web/messages/tracker-legacy.gen.ts | 3 +- mobs/ios_messages.rb | 87 +- .../charts/imagestorage/.helmignore | 23 + .../openreplay/charts/imagestorage/Chart.yaml | 24 + .../charts/imagestorage/templates/NOTES.txt | 22 + .../imagestorage/templates/_helpers.tpl | 62 + .../imagestorage/templates/deployment.yaml | 131 + .../charts/imagestorage/templates/hpa.yaml | 29 + .../imagestorage/templates/ingress.yaml | 62 + .../imagestorage/templates/service.yaml | 18 + .../templates/serviceMonitor.yaml | 18 + .../templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../charts/imagestorage/values.yaml | 124 + .../charts/videostorage/.helmignore | 23 + .../openreplay/charts/videostorage/Chart.yaml | 24 + .../charts/videostorage/templates/NOTES.txt | 22 + .../videostorage/templates/_helpers.tpl | 62 + .../videostorage/templates/deployment.yaml | 131 + .../charts/videostorage/templates/hpa.yaml | 29 + .../videostorage/templates/ingress.yaml | 62 + .../videostorage/templates/service.yaml | 18 + .../templates/serviceMonitor.yaml | 18 + .../templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../charts/videostorage/values.yaml | 124 + 76 files changed, 4958 insertions(+), 2334 deletions(-) create mode 100644 backend/cmd/imagestorage/main.go create mode 100644 backend/cmd/videostorage/main.go create mode 100644 backend/internal/config/imagestorage/config.go create mode 100644 backend/internal/config/videostorage/config.go create mode 100644 backend/internal/http/router/handlers-mobile.go create mode 100644 backend/internal/imagestorage/service.go create mode 100644 backend/internal/videostorage/service.go create mode 100644 backend/pkg/db/postgres/mobile.go rename backend/pkg/handlers/ios/{clickRage.go => tapRage.go} (58%) create mode 100644 backend/pkg/messages/iterator-images.go create mode 100644 backend/pkg/metrics/imagestorage/metrics.go create mode 100644 backend/pkg/metrics/videostorage/metrics.go create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/.helmignore create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/Chart.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/NOTES.txt create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/_helpers.tpl create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/ingress.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceMonitor.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/templates/tests/test-connection.yaml create mode 100644 scripts/helmcharts/openreplay/charts/imagestorage/values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/.helmignore create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/Chart.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/NOTES.txt create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/_helpers.tpl create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/ingress.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/serviceMonitor.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/templates/tests/test-connection.yaml create mode 100644 scripts/helmcharts/openreplay/charts/videostorage/values.yaml diff --git a/backend/Dockerfile b/backend/Dockerfile index ce0c60b5f..b18b78ac7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -46,15 +46,19 @@ ENV TZ=UTC \ REDIS_STREAMS_MAX_LEN=10000 \ TOPIC_RAW_WEB=raw \ TOPIC_RAW_IOS=raw-ios \ + TOPIC_RAW_IMAGES=raw-images \ TOPIC_CACHE=cache \ TOPIC_ANALYTICS=analytics \ TOPIC_TRIGGER=trigger \ + TOPIC_MOBILE_TRIGGER=mobile-trigger \ GROUP_SINK=sink \ GROUP_STORAGE=storage \ GROUP_DB=db \ GROUP_ENDER=ender \ GROUP_CACHE=cache \ GROUP_HEURISTICS=heuristics \ + GROUP_IMAGE_STORAGE=image-storage \ + GROUP_VIDEO_STORAGE=video-storage \ AWS_REGION_WEB=eu-central-1 \ AWS_REGION_IOS=eu-west-1 \ AWS_REGION_ASSETS=eu-central-1 \ @@ -91,8 +95,11 @@ ENV TZ=UTC \ RUN if [ "$SERVICE_NAME" = "http" ]; then \ - wget https://raw.githubusercontent.com/ua-parser/uap-core/master/regexes.yaml -O "$UAPARSER_FILE" &&\ - wget https://static.openreplay.com/geoip/GeoLite2-City.mmdb -O "$MAXMINDDB_FILE"; fi + wget https://raw.githubusercontent.com/ua-parser/uap-core/master/regexes.yaml -O "$UAPARSER_FILE" &&\ + wget https://static.openreplay.com/geoip/GeoLite2-City.mmdb -O "$MAXMINDDB_FILE"; \ + elif [ "$SERVICE_NAME" = "videostorage" ]; then \ + apk add --no-cache ffmpeg; \ + fi COPY --from=build /root/service /home/openreplay/service diff --git a/backend/cmd/db/main.go b/backend/cmd/db/main.go index 7ce4c059b..b68047b9b 100644 --- a/backend/cmd/db/main.go +++ b/backend/cmd/db/main.go @@ -52,20 +52,29 @@ func main() { saver := datasaver.New(cfg, pg, sessManager) // Message filter - msgFilter := []int{messages.MsgMetadata, messages.MsgIssueEvent, messages.MsgSessionStart, messages.MsgSessionEnd, + msgFilter := []int{ + // Web messages + messages.MsgMetadata, messages.MsgIssueEvent, messages.MsgSessionStart, messages.MsgSessionEnd, messages.MsgUserID, messages.MsgUserAnonymousID, messages.MsgIntegrationEvent, messages.MsgPerformanceTrackAggr, messages.MsgJSException, messages.MsgResourceTiming, messages.MsgCustomEvent, messages.MsgCustomIssue, messages.MsgFetch, messages.MsgNetworkRequest, messages.MsgGraphQL, messages.MsgStateAction, messages.MsgCreateDocument, messages.MsgMouseClick, messages.MsgSetPageLocation, messages.MsgPageLoadTiming, messages.MsgPageRenderTiming, messages.MsgInputEvent, messages.MsgPageEvent, messages.MsgMouseThrashing, messages.MsgInputChange, - messages.MsgUnbindNodes} + messages.MsgUnbindNodes, + // Mobile messages + messages.MsgIOSSessionStart, messages.MsgIOSSessionEnd, messages.MsgIOSUserID, messages.MsgIOSUserAnonymousID, + messages.MsgIOSMetadata, messages.MsgIOSCustomEvent, messages.MsgIOSNetworkCall, messages.MsgIOSScreenEnter, + messages.MsgIOSScreenLeave, messages.MsgIOSClickEvent, messages.MsgIOSSwipeEvent, messages.MsgIOSInputEvent, + messages.MsgIOSCrash, messages.MsgIOSIssueEvent, + } // Init consumer consumer := queue.NewConsumer( cfg.GroupDB, []string{ cfg.TopicRawWeb, + cfg.TopicRawIOS, cfg.TopicAnalytics, }, messages.NewMessageIterator(saver.Handle, msgFilter, true), diff --git a/backend/cmd/ender/main.go b/backend/cmd/ender/main.go index 23af6a05f..552ac4a10 100644 --- a/backend/cmd/ender/main.go +++ b/backend/cmd/ender/main.go @@ -58,13 +58,18 @@ func main() { return } + mobileMessages := []int{90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 107, 110, 111} + producer := queue.NewProducer(cfg.MessageSizeLimit, true) consumer := queue.NewConsumer( cfg.GroupEnder, - []string{cfg.TopicRawWeb}, + []string{ + cfg.TopicRawWeb, + cfg.TopicRawIOS, + }, messages.NewEnderMessageIterator( func(msg messages.Message) { sessionEndGenerator.UpdateSession(msg) }, - []int{messages.MsgTimestamp}, + append([]int{messages.MsgTimestamp}, mobileMessages...), false), false, cfg.MessageSizeLimit, @@ -168,10 +173,19 @@ func main() { } } } - if err := producer.Produce(cfg.TopicRawWeb, sessionID, msg.Encode()); err != nil { - log.Printf("can't send sessionEnd to topic: %s; sessID: %d", err, sessionID) - return false, 0 + if sess.Platform == "ios" { + msg := &messages.IOSSessionEnd{Timestamp: timestamp} + if err := producer.Produce(cfg.TopicRawIOS, sessionID, msg.Encode()); err != nil { + log.Printf("can't send iOSSessionEnd to topic: %s; sessID: %d", err, sessionID) + return false, 0 + } + } else { + if err := producer.Produce(cfg.TopicRawWeb, sessionID, msg.Encode()); err != nil { + log.Printf("can't send sessionEnd to topic: %s; sessID: %d", err, sessionID) + return false, 0 + } } + if currDuration != 0 { diffDuration[sessionID] = int64(newDuration) - int64(currDuration) updatedDurations++ diff --git a/backend/cmd/heuristics/main.go b/backend/cmd/heuristics/main.go index f5ff67fe6..0689d398b 100644 --- a/backend/cmd/heuristics/main.go +++ b/backend/cmd/heuristics/main.go @@ -7,6 +7,7 @@ import ( "openreplay/backend/pkg/builders" "openreplay/backend/pkg/handlers" "openreplay/backend/pkg/handlers/custom" + "openreplay/backend/pkg/handlers/ios" "openreplay/backend/pkg/handlers/web" "openreplay/backend/pkg/memory" "openreplay/backend/pkg/messages" @@ -34,6 +35,8 @@ func main() { &web.NetworkIssueDetector{}, &web.PerformanceAggregator{}, web.NewAppCrashDetector(), + &ios.TapRageDetector{}, + ios.NewViewComponentDurations(), } } @@ -43,6 +46,7 @@ func main() { cfg.GroupHeuristics, []string{ cfg.TopicRawWeb, + cfg.TopicRawIOS, }, messages.NewMessageIterator(eventBuilder.HandleMessage, nil, true), false, diff --git a/backend/cmd/imagestorage/main.go b/backend/cmd/imagestorage/main.go new file mode 100644 index 000000000..338463f33 --- /dev/null +++ b/backend/cmd/imagestorage/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "log" + "os" + "os/signal" + "syscall" + "time" + + config "openreplay/backend/internal/config/imagestorage" + "openreplay/backend/internal/imagestorage" + "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/metrics" + storageMetrics "openreplay/backend/pkg/metrics/imagestorage" + "openreplay/backend/pkg/queue" +) + +func main() { + m := metrics.New() + m.Register(storageMetrics.List()) + + log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) + + cfg := config.New() + + srv, err := imagestorage.New(cfg) + if err != nil { + log.Printf("can't init storage service: %s", err) + return + } + + consumer := queue.NewConsumer( + cfg.GroupImageStorage, + []string{ + cfg.TopicRawImages, + }, + messages.NewImagesMessageIterator(func(data []byte, sessID uint64) { + if err := srv.Process(sessID, data); err != nil { + log.Printf("can't process image: %s", err) + } + }, nil, true), + false, + cfg.MessageSizeLimit, + ) + + log.Printf("Image storage service started\n") + + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + + counterTick := time.Tick(time.Second * 30) + for { + select { + case sig := <-sigchan: + log.Printf("Caught signal %v: terminating\n", sig) + srv.Wait() + consumer.Close() + os.Exit(0) + case <-counterTick: + srv.Wait() + if err := consumer.Commit(); err != nil { + log.Printf("can't commit messages: %s", err) + } + case msg := <-consumer.Rebalanced(): + log.Println(msg) + default: + err := consumer.ConsumeNext() + if err != nil { + log.Fatalf("Error on images consumption: %v", err) + } + } + } +} diff --git a/backend/cmd/sink/main.go b/backend/cmd/sink/main.go index bb04c13cf..c9bf3abe8 100644 --- a/backend/cmd/sink/main.go +++ b/backend/cmd/sink/main.go @@ -75,10 +75,16 @@ func main() { sinkMetrics.IncreaseTotalMessages() // Send SessionEnd trigger to storage service - if msg.TypeID() == messages.MsgSessionEnd { + if msg.TypeID() == messages.MsgSessionEnd || msg.TypeID() == messages.MsgIOSSessionEnd { if err := producer.Produce(cfg.TopicTrigger, msg.SessionID(), msg.Encode()); err != nil { log.Printf("can't send SessionEnd to trigger topic: %s; sessID: %d", err, msg.SessionID()) } + // duplicate session end message to mobile trigger topic to build video replay for mobile sessions + if msg.TypeID() == messages.MsgIOSSessionEnd { + if err := producer.Produce(cfg.TopicMobileTrigger, msg.SessionID(), msg.Encode()); err != nil { + log.Printf("can't send iOSSessionEnd to mobile trigger topic: %s; sessID: %d", err, msg.SessionID()) + } + } writer.Close(msg.SessionID()) return } @@ -178,6 +184,7 @@ func main() { cfg.GroupSink, []string{ cfg.TopicRawWeb, + cfg.TopicRawIOS, }, messages.NewSinkMessageIterator(msgHandler, nil, false), false, diff --git a/backend/cmd/storage/main.go b/backend/cmd/storage/main.go index baadd0007..548babc3f 100644 --- a/backend/cmd/storage/main.go +++ b/backend/cmd/storage/main.go @@ -48,6 +48,15 @@ func main() { }, messages.NewMessageIterator( func(msg messages.Message) { + // Convert IOSSessionEnd to SessionEnd + if msg.TypeID() == messages.MsgIOSSessionEnd { + mobileEnd, oldMeta := msg.(*messages.IOSSessionEnd), msg.Meta() + msg = &messages.SessionEnd{ + Timestamp: mobileEnd.Timestamp, + } + msg.Meta().SetMeta(oldMeta) + } + // Process session to save mob files to s3 sesEnd := msg.(*messages.SessionEnd) if err := srv.Process(sesEnd); err != nil { log.Printf("upload session err: %s, sessID: %d", err, msg.SessionID()) @@ -56,7 +65,7 @@ func main() { // Log timestamp of last processed session counter.Update(msg.SessionID(), time.UnixMilli(msg.Meta().Batch().Timestamp())) }, - []int{messages.MsgSessionEnd}, + []int{messages.MsgSessionEnd, messages.MsgIOSSessionEnd}, true, ), false, diff --git a/backend/cmd/videostorage/main.go b/backend/cmd/videostorage/main.go new file mode 100644 index 000000000..eb909ff8d --- /dev/null +++ b/backend/cmd/videostorage/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "log" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + config "openreplay/backend/internal/config/videostorage" + "openreplay/backend/internal/videostorage" + "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/metrics" + storageMetrics "openreplay/backend/pkg/metrics/videostorage" + "openreplay/backend/pkg/objectstorage/store" + "openreplay/backend/pkg/queue" +) + +func main() { + m := metrics.New() + m.Register(storageMetrics.List()) + + log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) + + cfg := config.New() + + objStore, err := store.NewStore(&cfg.ObjectsConfig) + if err != nil { + log.Fatalf("can't init object storage: %s", err) + } + srv, err := videostorage.New(cfg, objStore) + if err != nil { + log.Printf("can't init storage service: %s", err) + return + } + + workDir := cfg.FSDir + + consumer := queue.NewConsumer( + cfg.GroupVideoStorage, + []string{ + cfg.TopicMobileTrigger, + }, + messages.NewMessageIterator( + func(msg messages.Message) { + sesEnd := msg.(*messages.IOSSessionEnd) + log.Printf("recieved mobile session end: %d", sesEnd.SessionID()) + if err := srv.Process(sesEnd.SessionID(), workDir+"/screenshots/"+strconv.FormatUint(sesEnd.SessionID(), 10)+"/"); err != nil { + log.Printf("upload session err: %s, sessID: %d", err, msg.SessionID()) + } + }, + []int{messages.MsgIOSSessionEnd}, + true, + ), + false, + cfg.MessageSizeLimit, + ) + + log.Printf("Video storage service started\n") + + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + + counterTick := time.Tick(time.Second * 30) + for { + select { + case sig := <-sigchan: + log.Printf("Caught signal %v: terminating\n", sig) + srv.Wait() + consumer.Close() + os.Exit(0) + case <-counterTick: + srv.Wait() + if err := consumer.Commit(); err != nil { + log.Printf("can't commit messages: %s", err) + } + case msg := <-consumer.Rebalanced(): + log.Println(msg) + default: + err = consumer.ConsumeNext() + if err != nil { + log.Fatalf("Error on end event consumption: %v", err) + } + } + } +} diff --git a/backend/internal/config/db/config.go b/backend/internal/config/db/config.go index 470fffe51..48642df33 100644 --- a/backend/internal/config/db/config.go +++ b/backend/internal/config/db/config.go @@ -16,6 +16,7 @@ type Config struct { GroupDB string `env:"GROUP_DB,required"` TopicRawWeb string `env:"TOPIC_RAW_WEB,required"` TopicAnalytics string `env:"TOPIC_ANALYTICS,required"` + TopicRawIOS string `env:"TOPIC_RAW_IOS,required"` CommitBatchTimeout time.Duration `env:"COMMIT_BATCH_TIMEOUT,default=15s"` BatchQueueLimit int `env:"DB_BATCH_QUEUE_LIMIT,required"` BatchSizeLimit int `env:"DB_BATCH_SIZE_LIMIT,required"` diff --git a/backend/internal/config/ender/config.go b/backend/internal/config/ender/config.go index 7b99fbba3..b3182567a 100644 --- a/backend/internal/config/ender/config.go +++ b/backend/internal/config/ender/config.go @@ -15,6 +15,7 @@ type Config struct { GroupEnder string `env:"GROUP_ENDER,required"` LoggerTimeout int `env:"LOG_QUEUE_STATS_INTERVAL_SEC,required"` TopicRawWeb string `env:"TOPIC_RAW_WEB,required"` + TopicRawIOS string `env:"TOPIC_RAW_IOS,required"` ProducerTimeout int `env:"PRODUCER_TIMEOUT,default=2000"` PartitionsNumber int `env:"PARTITIONS_NUMBER,required"` UseEncryption bool `env:"USE_ENCRYPTION,default=false"` diff --git a/backend/internal/config/http/config.go b/backend/internal/config/http/config.go index 3d83866d9..76c1662d6 100644 --- a/backend/internal/config/http/config.go +++ b/backend/internal/config/http/config.go @@ -17,6 +17,7 @@ type Config struct { HTTPTimeout time.Duration `env:"HTTP_TIMEOUT,default=60s"` TopicRawWeb string `env:"TOPIC_RAW_WEB,required"` TopicRawIOS string `env:"TOPIC_RAW_IOS,required"` + TopicRawImages string `env:"TOPIC_RAW_IMAGES,required"` BeaconSizeLimit int64 `env:"BEACON_SIZE_LIMIT,required"` CompressionThreshold int64 `env:"COMPRESSION_THRESHOLD,default=20000"` JsonSizeLimit int64 `env:"JSON_SIZE_LIMIT,default=1000"` diff --git a/backend/internal/config/imagestorage/config.go b/backend/internal/config/imagestorage/config.go new file mode 100644 index 000000000..888e8d135 --- /dev/null +++ b/backend/internal/config/imagestorage/config.go @@ -0,0 +1,21 @@ +package imagestorage + +import ( + "openreplay/backend/internal/config/common" + "openreplay/backend/internal/config/configurator" +) + +type Config struct { + common.Config + FSDir string `env:"FS_DIR,required"` + ScreenshotsDir string `env:"SCREENSHOTS_DIR,default=screenshots"` + TopicRawImages string `env:"TOPIC_RAW_IMAGES,required"` + GroupImageStorage string `env:"GROUP_IMAGE_STORAGE,required"` + UseProfiler bool `env:"PROFILER_ENABLED,default=false"` +} + +func New() *Config { + cfg := &Config{} + configurator.Process(cfg) + return cfg +} diff --git a/backend/internal/config/sink/config.go b/backend/internal/config/sink/config.go index 802dfb54b..c63d88ee6 100644 --- a/backend/internal/config/sink/config.go +++ b/backend/internal/config/sink/config.go @@ -16,6 +16,7 @@ type Config struct { TopicRawIOS string `env:"TOPIC_RAW_IOS,required"` TopicCache string `env:"TOPIC_CACHE,required"` TopicTrigger string `env:"TOPIC_TRIGGER,required"` + TopicMobileTrigger string `env:"TOPIC_MOBILE_TRIGGER,required"` CacheAssets bool `env:"CACHE_ASSETS,required"` AssetsOrigin string `env:"ASSETS_ORIGIN,required"` ProducerCloseTimeout int `env:"PRODUCER_CLOSE_TIMEOUT,default=15000"` diff --git a/backend/internal/config/videostorage/config.go b/backend/internal/config/videostorage/config.go new file mode 100644 index 000000000..a2b495e55 --- /dev/null +++ b/backend/internal/config/videostorage/config.go @@ -0,0 +1,23 @@ +package videostorage + +import ( + "openreplay/backend/internal/config/common" + "openreplay/backend/internal/config/configurator" + "openreplay/backend/internal/config/objectstorage" +) + +type Config struct { + common.Config + objectstorage.ObjectsConfig + FSDir string `env:"FS_DIR,required"` + GroupVideoStorage string `env:"GROUP_VIDEO_STORAGE,required"` + TopicMobileTrigger string `env:"TOPIC_MOBILE_TRIGGER,required"` + VideoReplayFPS int `env:"VIDEO_REPLAY_FPS,default=3"` + UseProfiler bool `env:"PROFILER_ENABLED,default=false"` +} + +func New() *Config { + cfg := &Config{} + configurator.Process(cfg) + return cfg +} diff --git a/backend/internal/db/datasaver/saver.go b/backend/internal/db/datasaver/saver.go index 31482611e..95d32f902 100644 --- a/backend/internal/db/datasaver/saver.go +++ b/backend/internal/db/datasaver/saver.go @@ -40,6 +40,15 @@ func (s *saverImpl) Handle(msg Message) { if msg.TypeID() == MsgCustomEvent { defer s.Handle(types.WrapCustomEvent(msg.(*CustomEvent))) } + if IsIOSType(msg.TypeID()) { + // Handle iOS messages + if err := s.handleMobileMessage(msg); err != nil { + if !postgres.IsPkeyViolation(err) { + log.Printf("iOS Message Insertion Error %v, SessionID: %v, Message: %v", err, msg.SessionID(), msg) + } + return + } + } if err := s.handleMessage(msg); err != nil { if !postgres.IsPkeyViolation(err) { log.Printf("Message Insertion Error %v, SessionID: %v, Message: %v", err, msg.SessionID(), msg) @@ -52,6 +61,73 @@ func (s *saverImpl) Handle(msg Message) { return } +func (s *saverImpl) handleMobileMessage(msg Message) error { + session, err := s.sessions.Get(msg.SessionID()) + if err != nil { + return err + } + switch m := msg.(type) { + case *IOSSessionStart: + return s.pg.InsertIOSSessionStart(m.SessionID(), m) + case *IOSSessionEnd: + return s.pg.InsertIOSSessionEnd(m.SessionID(), m) + case *IOSUserID: + if err = s.sessions.UpdateUserID(session.SessionID, m.Value); err != nil { + return err + } + s.pg.InsertAutocompleteValue(session.SessionID, session.ProjectID, "USERID_IOS", m.Value) + return nil + case *IOSUserAnonymousID: + if err = s.sessions.UpdateAnonymousID(session.SessionID, m.Value); err != nil { + return err + } + s.pg.InsertAutocompleteValue(session.SessionID, session.ProjectID, "USERANONYMOUSID_IOS", m.Value) + return nil + case *IOSMetadata: + return s.sessions.UpdateMetadata(m.SessionID(), m.Key, m.Value) + case *IOSCustomEvent: + return s.pg.InsertIOSCustomEvent(session, m) + case *IOSClickEvent: + if err := s.pg.InsertIOSClickEvent(session, m); err != nil { + return err + } + return s.sessions.UpdateEventsStats(session.SessionID, 1, 0) + case *IOSSwipeEvent: + if err := s.pg.InsertIOSSwipeEvent(session, m); err != nil { + return err + } + return s.sessions.UpdateEventsStats(session.SessionID, 1, 0) + case *IOSInputEvent: + if err := s.pg.InsertIOSInputEvent(session, m); err != nil { + return err + } + return s.sessions.UpdateEventsStats(session.SessionID, 1, 0) + case *IOSNetworkCall: + return s.pg.InsertIOSNetworkCall(session, m) + case *IOSScreenEnter: + if err := s.pg.InsertIOSScreenEnter(session, m); err != nil { + return err + } + return s.sessions.UpdateEventsStats(session.SessionID, 1, 1) + case *IOSScreenLeave: + if err := s.pg.InsertIOSScreenLeave(session, m); err != nil { + return err + } + return s.sessions.UpdateEventsStats(session.SessionID, 1, 0) + case *IOSCrash: + if err := s.pg.InsertIOSCrash(session.SessionID, session.ProjectID, m); err != nil { + return err + } + return s.sessions.UpdateIssuesStats(session.SessionID, 1, 1000) + case *IOSIssueEvent: + if err = s.pg.InsertIOSIssueEvent(session, m); err != nil { + return err + } + return s.sessions.UpdateIssuesStats(session.SessionID, 0, postgres.GetIssueScore(m.Type)) + } + return nil +} + func (s *saverImpl) handleMessage(msg Message) error { session, err := s.sessions.Get(msg.SessionID()) if err != nil { @@ -69,7 +145,7 @@ func (s *saverImpl) handleMessage(msg Message) error { if err != nil { return err } - return s.sessions.UpdateIssuesStats(session.SessionID, 0, postgres.GetIssueScore(m)) + return s.sessions.UpdateIssuesStats(session.SessionID, 0, postgres.GetIssueScore(m.Type)) case *CustomIssue: ie := &IssueEvent{ Type: "custom", @@ -82,7 +158,7 @@ func (s *saverImpl) handleMessage(msg Message) error { if err = s.pg.InsertIssueEvent(session, ie); err != nil { return err } - return s.sessions.UpdateIssuesStats(session.SessionID, 0, postgres.GetIssueScore(ie)) + return s.sessions.UpdateIssuesStats(session.SessionID, 0, postgres.GetIssueScore(ie.Type)) case *UserID: if err = s.sessions.UpdateUserID(session.SessionID, m.ID); err != nil { return err diff --git a/backend/internal/http/ios/ios-device.go b/backend/internal/http/ios/ios-device.go index 8df33035b..4e81844e1 100644 --- a/backend/internal/http/ios/ios-device.go +++ b/backend/internal/http/ios/ios-device.go @@ -64,6 +64,24 @@ func MapIOSDevice(identifier string) string { return "iPhone 12 Pro" case "iPhone13,4": return "iPhone 12 Pro Max" + case "iPhone14,4": + return "iPhone 13 mini" + case "iPhone14,5": + return "iPhone 13" + case "iPhone14,2": + return "iPhone 13 Pro" + case "iPhone14,3": + return "iPhone 13 Pro Max" + case "iPhone14,7": + return "iPhone 14" + case "iPhone14,8": + return "iPhone 14 Plus" + case "iPhone15,2": + return "iPhone 14 Pro" + case "iPhone15,3": + return "iPhone 14 Pro Max" + case "iPhone14,6": + return "iPhone SE (3rd generation)" case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2" case "iPad3,1", "iPad3,2", "iPad3,3": @@ -78,6 +96,10 @@ func MapIOSDevice(identifier string) string { return "iPad (7th generation)" case "iPad11,6", "iPad11,7": return "iPad (8th generation)" + case "iPad12,1", "iPad12,2": + return "iPad (9th generation)" + case "iPad13,18", "iPad13,19": + return "iPad (10th generation)" case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" case "iPad5,3", "iPad5,4": @@ -86,6 +108,8 @@ func MapIOSDevice(identifier string) string { return "iPad Air (3rd generation)" case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)" + case "iPad13,16", "iPad13,17": + return "iPad Air (5th generation)" case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini" case "iPad4,4", "iPad4,5", "iPad4,6": @@ -96,6 +120,8 @@ func MapIOSDevice(identifier string) string { return "iPad mini 4" case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)" + case "iPad14,1", "iPad14,2": + return "iPad mini (6th generation)" case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)" case "iPad7,3", "iPad7,4": @@ -104,6 +130,10 @@ func MapIOSDevice(identifier string) string { return "iPad Pro (11-inch) (1st generation)" case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)" + case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": + return "iPad Pro (11-inch) (3rd generation)" + case "iPad14,3", "iPad14,4": + return "iPad Pro (11-inch) (4th generation)" case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)" case "iPad7,1", "iPad7,2": @@ -112,6 +142,10 @@ func MapIOSDevice(identifier string) string { return "iPad Pro (12.9-inch) (3rd generation)" case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)" + case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11": + return "iPad Pro (12.9-inch) (5th generation)" + case "iPad14,5", "iPad14,6": + return "iPad Pro (12.9-inch) (6th generation)" case "AppleTV5,3": return "Apple TV" case "AppleTV6,2": @@ -120,7 +154,7 @@ func MapIOSDevice(identifier string) string { return "HomePod" case "AudioAccessory5,1": return "HomePod mini" - case "i386", "x86_64": + case "i386", "x86_64", "arm64": return "Simulator" default: return identifier @@ -129,7 +163,7 @@ func MapIOSDevice(identifier string) string { func GetIOSDeviceType(identifier string) string { if strings.Contains(identifier, "iPhone") { - return "mobile" //"phone" + return "mobile" } if strings.Contains(identifier, "iPad") { return "tablet" diff --git a/backend/internal/http/router/handlers-mobile.go b/backend/internal/http/router/handlers-mobile.go new file mode 100644 index 000000000..8e8ba0058 --- /dev/null +++ b/backend/internal/http/router/handlers-mobile.go @@ -0,0 +1,224 @@ +package router + +import ( + "encoding/json" + "errors" + "io/ioutil" + "log" + "math/rand" + "net/http" + "openreplay/backend/internal/http/ios" + "openreplay/backend/internal/http/util" + "openreplay/backend/internal/http/uuid" + "openreplay/backend/pkg/db/postgres" + "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/sessions" + "openreplay/backend/pkg/token" + "strconv" + "time" +) + +func (e *Router) startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + req := &StartIOSSessionRequest{} + + if r.Body == nil { + ResponseWithError(w, http.StatusBadRequest, errors.New("request body is empty"), startTime, r.URL.Path, 0) + return + } + body := http.MaxBytesReader(w, r.Body, e.cfg.JsonSizeLimit) + defer body.Close() + + if err := json.NewDecoder(body).Decode(req); err != nil { + ResponseWithError(w, http.StatusBadRequest, err, startTime, r.URL.Path, 0) + return + } + + if req.ProjectKey == nil { + ResponseWithError(w, http.StatusForbidden, errors.New("ProjectKey value required"), startTime, r.URL.Path, 0) + return + } + + p, err := e.services.Projects.GetProjectByKey(*req.ProjectKey) + if err != nil { + if postgres.IsNoRowsErr(err) { + ResponseWithError(w, http.StatusNotFound, errors.New("Project doesn't exist or is not active"), startTime, r.URL.Path, 0) + } else { + ResponseWithError(w, http.StatusInternalServerError, err, startTime, r.URL.Path, 0) // TODO: send error here only on staging + } + return + } + + // Check if the project supports mobile sessions + if !p.IsMobile() { + ResponseWithError(w, http.StatusForbidden, errors.New("project doesn't support mobile sessions"), startTime, r.URL.Path, 0) + return + } + + userUUID := uuid.GetUUID(req.UserUUID) + tokenData, err := e.services.Tokenizer.Parse(req.Token) + + if err != nil { // Starting the new one + dice := byte(rand.Intn(100)) // [0, 100) + if dice >= p.SampleRate { + ResponseWithError(w, http.StatusForbidden, errors.New("cancel"), startTime, r.URL.Path, 0) + return + } + + ua := e.services.UaParser.ParseFromHTTPRequest(r) + if ua == nil { + ResponseWithError(w, http.StatusForbidden, errors.New("browser not recognized"), startTime, r.URL.Path, 0) + return + } + sessionID, err := e.services.Flaker.Compose(uint64(startTime.UnixMilli())) + if err != nil { + ResponseWithError(w, http.StatusInternalServerError, err, startTime, r.URL.Path, 0) + return + } + // TODO: if EXPIRED => send message for two sessions association + expTime := startTime.Add(time.Duration(p.MaxSessionDuration) * time.Millisecond) + tokenData = &token.TokenData{sessionID, 0, expTime.UnixMilli()} + + 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) + } + + 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) + } + } + + ResponseWithJSON(w, &StartIOSSessionResponse{ + Token: e.services.Tokenizer.Compose(*tokenData), + 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 + }, startTime, r.URL.Path, 0) +} + +func (e *Router) pushMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + sessionData, err := e.services.Tokenizer.ParseFromHTTPRequest(r) + if err != nil { + ResponseWithError(w, http.StatusUnauthorized, err, startTime, r.URL.Path, 0) + return + } + e.pushMessages(w, r, sessionData.ID, e.cfg.TopicRawIOS) +} + +func (e *Router) pushLateMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + sessionData, err := e.services.Tokenizer.ParseFromHTTPRequest(r) + if err != nil && err != token.EXPIRED { + ResponseWithError(w, http.StatusUnauthorized, err, startTime, r.URL.Path, 0) + return + } + // Check timestamps here? + e.pushMessages(w, r, sessionData.ID, e.cfg.TopicRawIOS) +} + +func (e *Router) imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + log.Printf("recieved imagerequest") + + sessionData, err := e.services.Tokenizer.ParseFromHTTPRequest(r) + if err != nil { // Should accept expired token? + ResponseWithError(w, http.StatusUnauthorized, err, startTime, r.URL.Path, 0) + return + } + + if r.Body == nil { + ResponseWithError(w, http.StatusBadRequest, errors.New("request body is empty"), startTime, r.URL.Path, 0) + return + } + r.Body = http.MaxBytesReader(w, r.Body, e.cfg.FileSizeLimit) + defer r.Body.Close() + + err = r.ParseMultipartForm(5 * 1e6) // ~5Mb + if err == http.ErrNotMultipart || err == http.ErrMissingBoundary { + ResponseWithError(w, http.StatusUnsupportedMediaType, err, startTime, r.URL.Path, 0) + return + // } else if err == multipart.ErrMessageTooLarge // if non-files part exceeds 10 MB + } else if err != nil { + ResponseWithError(w, http.StatusInternalServerError, err, startTime, r.URL.Path, 0) // TODO: send error here only on staging + return + } + + if r.MultipartForm == nil { + ResponseWithError(w, http.StatusInternalServerError, errors.New("Multipart not parsed"), startTime, r.URL.Path, 0) + return + } + + if len(r.MultipartForm.Value["projectKey"]) == 0 { + ResponseWithError(w, http.StatusBadRequest, errors.New("projectKey parameter missing"), startTime, r.URL.Path, 0) // status for missing/wrong parameter? + return + } + + //prefix := r.MultipartForm.Value["projectKey"][0] + "/" + strconv.FormatUint(sessionData.ID, 10) + "/" + + for _, fileHeaderList := range r.MultipartForm.File { + for _, fileHeader := range fileHeaderList { + file, err := fileHeader.Open() + if err != nil { + continue // TODO: send server error or accumulate successful files + } + //key := prefix + fileHeader.Filename + + data, err := ioutil.ReadAll(file) + if err != nil { + log.Fatalf("failed reading data: %s", err) + } + + log.Printf("Uploading image... %v, len: %d", util.SafeString(fileHeader.Filename), len(data)) + + if err := e.services.Producer.Produce(e.cfg.TopicRawImages, sessionData.ID, data); err != nil { + log.Printf("failed to produce mobile session start message: %v", err) + } + log.Printf("Image uploaded") + //go func() { //TODO: mime type from header + // log.Printf("Uploading image... %v", file) + // //if err := e.services.Storage.Upload(file, key, "image/jpeg", false); err != nil { + // // log.Printf("Upload ios screen error. %v", err) + // //} + //}() + } + } + + w.WriteHeader(http.StatusOK) +} diff --git a/backend/internal/http/router/handlers-web.go b/backend/internal/http/router/handlers-web.go index 481c58c1f..21c484e86 100644 --- a/backend/internal/http/router/handlers-web.go +++ b/backend/internal/http/router/handlers-web.go @@ -115,6 +115,12 @@ func (e *Router) startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) return } + // Check if the project supports mobile sessions + if !p.IsWeb() { + ResponseWithError(w, http.StatusForbidden, errors.New("project doesn't support web sessions"), startTime, r.URL.Path, bodySize) + return + } + ua := e.services.UaParser.ParseFromHTTPRequest(r) if ua == nil { ResponseWithError(w, http.StatusForbidden, errors.New("browser not recognized"), startTime, r.URL.Path, bodySize) diff --git a/backend/internal/http/router/handlers.go b/backend/internal/http/router/handlers.go index 425177341..6250b378d 100644 --- a/backend/internal/http/router/handlers.go +++ b/backend/internal/http/router/handlers.go @@ -23,20 +23,24 @@ func (e *Router) pushMessages(w http.ResponseWriter, r *http.Request, sessionID reader, err = gzip.NewReader(body) if err != nil { - ResponseWithError(w, http.StatusInternalServerError, err, start, r.URL.Path, 0) // TODO: stage-dependent response + ResponseWithError(w, http.StatusInternalServerError, err, start, r.URL.Path, 0) return } - //log.Println("Gzip reader init", reader) + log.Println("Gzip reader init", reader) defer reader.Close() default: reader = body } - //log.Println("Reader after switch:", reader) + log.Println("Reader after switch:", reader) buf, err := ioutil.ReadAll(reader) if err != nil { - ResponseWithError(w, http.StatusInternalServerError, err, start, r.URL.Path, 0) // TODO: send error here only on staging + ResponseWithError(w, http.StatusInternalServerError, err, start, r.URL.Path, 0) + return + } + log.Println("Produce message: ", buf, string(buf)) + if err := e.services.Producer.Produce(topicName, sessionID, buf); err != nil { + ResponseWithError(w, http.StatusInternalServerError, err, start, r.URL.Path, 0) return } - e.services.Producer.Produce(topicName, sessionID, buf) // What if not able to send? w.WriteHeader(http.StatusOK) } diff --git a/backend/internal/http/router/model.go b/backend/internal/http/router/model.go index 3a6cac5dc..c2f555d49 100644 --- a/backend/internal/http/router/model.go +++ b/backend/internal/http/router/model.go @@ -38,3 +38,26 @@ type NotStartedRequest struct { TrackerVersion string `json:"trackerVersion"` DoNotTrack bool `json:"DoNotTrack"` } + +type StartIOSSessionRequest struct { + Token string `json:"token"` + ProjectKey *string `json:"projectKey"` + TrackerVersion string `json:"trackerVersion"` + RevID string `json:"revID"` + UserUUID *string `json:"userUUID"` + UserOSVersion string `json:"userOSVersion"` + UserDevice string `json:"userDevice"` + Timestamp uint64 `json:"timestamp"` + Timezone string `json:"timezone"` + DeviceMemory uint64 `json:"deviceMemory"` +} + +type StartIOSSessionResponse struct { + Token string `json:"token"` + ImagesHashList []string `json:"imagesHashList"` + UserUUID string `json:"userUUID"` + BeaconSizeLimit int64 `json:"beaconSizeLimit"` + SessionID string `json:"sessionID"` + ImageQuality string `json:"quality"` + FrameRate int `json:"fps"` +} diff --git a/backend/internal/http/router/router.go b/backend/internal/http/router/router.go index 822d05900..e1eacc65d 100644 --- a/backend/internal/http/router/router.go +++ b/backend/internal/http/router/router.go @@ -105,6 +105,10 @@ func (e *Router) init() { "/v1/web/start": e.startSessionHandlerWeb, "/v1/web/i": e.pushMessagesHandlerWeb, "/v1/web/feature-flags": e.featureFlagsHandlerWeb, + "/v1/mobile/start": e.startSessionHandlerIOS, + "/v1/mobile/i": e.pushMessagesHandlerIOS, + "/v1/mobile/late": e.pushLateMessagesHandlerIOS, + "/v1/mobile/images": e.imagesUploadHandlerIOS, } prefix := "/ingest" diff --git a/backend/internal/imagestorage/service.go b/backend/internal/imagestorage/service.go new file mode 100644 index 000000000..030376bab --- /dev/null +++ b/backend/internal/imagestorage/service.go @@ -0,0 +1,133 @@ +package imagestorage + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "log" + "os" + "strconv" + "time" + + gzip "github.com/klauspost/pgzip" + config "openreplay/backend/internal/config/imagestorage" +) + +type Task struct { + sessionID uint64 // to generate path + images map[string]*bytes.Buffer + isBreakTask bool +} + +func NewBreakTask() *Task { + return &Task{isBreakTask: true} +} + +type ImageStorage struct { + cfg *config.Config + writeToDiskTasks chan *Task + workersStopped chan struct{} +} + +func New(cfg *config.Config) (*ImageStorage, error) { + switch { + case cfg == nil: + return nil, fmt.Errorf("config is empty") + } + newStorage := &ImageStorage{ + cfg: cfg, + writeToDiskTasks: make(chan *Task, 1), + workersStopped: make(chan struct{}), + } + go newStorage.runWorker() + return newStorage, nil +} + +func (v *ImageStorage) Wait() { + // send stop signal + v.writeToDiskTasks <- NewBreakTask() + // wait for workers to stop + <-v.workersStopped +} + +func (v *ImageStorage) Process(sessID uint64, data []byte) error { + start := time.Now() + if err := v.extract(sessID, data); err != nil { + return err + } + log.Printf("sessID: %d, arch size: %d, extracted archive in: %s", sessID, len(data), time.Since(start)) + return nil +} + +func (v *ImageStorage) extract(sessID uint64, data []byte) error { + images := make(map[string]*bytes.Buffer) + uncompressedStream, err := gzip.NewReader(bytes.NewReader(data)) + if err != nil { + return fmt.Errorf("can't create gzip reader: %s", err.Error()) + } + tarReader := tar.NewReader(uncompressedStream) + + for { + header, err := tarReader.Next() + if err != nil { + if err == io.EOF { + break + } + return fmt.Errorf("can't read tar header: %s", err.Error()) + } + + if header.Typeflag == tar.TypeReg { + var buf bytes.Buffer + if _, err := buf.ReadFrom(tarReader); err != nil { + return fmt.Errorf("can't copy file: %s", err.Error()) + } + images[header.Name] = &buf + } else { + log.Printf("ExtractTarGz: uknown type: %d in %s", header.Typeflag, header.Name) + } + } + + v.writeToDiskTasks <- &Task{sessionID: sessID, images: images} + return nil +} + +func (v *ImageStorage) writeToDisk(task *Task) { + // Build the directory path + path := v.cfg.FSDir + "/" + if v.cfg.ScreenshotsDir != "" { + path += v.cfg.ScreenshotsDir + "/" + } + path += strconv.FormatUint(task.sessionID, 10) + "/" + + // Ensure the directory exists + if err := os.MkdirAll(path, 0755); err != nil { + log.Fatalf("Error creating directories: %v", err) + } + + // Write images to disk + for name, img := range task.images { + outFile, err := os.Create(path + name) // or open file in rewrite mode + if err != nil { + log.Printf("can't create file: %s", err.Error()) + } + if _, err := io.Copy(outFile, img); err != nil { + log.Printf("can't copy file: %s", err.Error()) + } + outFile.Close() + } + return +} + +func (v *ImageStorage) runWorker() { + for { + select { + case task := <-v.writeToDiskTasks: + if task.isBreakTask { + v.workersStopped <- struct{}{} + continue + } + v.writeToDisk(task) + } + } +} diff --git a/backend/internal/sessionender/ender.go b/backend/internal/sessionender/ender.go index 999f68b75..cc36c99f1 100644 --- a/backend/internal/sessionender/ender.go +++ b/backend/internal/sessionender/ender.go @@ -17,6 +17,7 @@ type session struct { lastUpdate int64 // local timestamp lastUserTime uint64 isEnded bool + isMobile bool } // SessionEnder updates timestamp of last message for each session @@ -73,6 +74,10 @@ func (se *SessionEnder) UpdateSession(msg messages.Message) { msgTimestamp = msg.Meta().Timestamp localTimestamp = time.Now().UnixMilli() ) + if messages.IsIOSType(msg.TypeID()) { + msgTimestamp = messages.GetTimestamp(msg) + log.Printf("got timestamp from iOS message, session: %d, ts: %d", msg.SessionID(), msgTimestamp) + } if batchTimestamp == 0 { log.Printf("got empty timestamp for sessionID: %d", sessionID) return @@ -86,6 +91,7 @@ func (se *SessionEnder) UpdateSession(msg messages.Message) { lastUpdate: localTimestamp, lastUserTime: msgTimestamp, // last timestamp from user's machine isEnded: false, + isMobile: messages.IsIOSType(msg.TypeID()), } ender.IncreaseActiveSessions() ender.IncreaseTotalSessions() diff --git a/backend/internal/videostorage/service.go b/backend/internal/videostorage/service.go new file mode 100644 index 000000000..c75304839 --- /dev/null +++ b/backend/internal/videostorage/service.go @@ -0,0 +1,114 @@ +package videostorage + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + config "openreplay/backend/internal/config/videostorage" + "openreplay/backend/pkg/objectstorage" + "os/exec" + "strconv" + "time" +) + +type Task struct { + sessionID string + path string + isBreakTask bool +} + +func NewBreakTask() *Task { + return &Task{isBreakTask: true} +} + +type VideoStorage struct { + cfg *config.Config + framerate string + objStorage objectstorage.ObjectStorage + sendToS3Tasks chan *Task + workersStopped chan struct{} +} + +func New(cfg *config.Config, objStorage objectstorage.ObjectStorage) (*VideoStorage, error) { + switch { + case cfg == nil: + return nil, fmt.Errorf("config is empty") + case objStorage == nil: + return nil, fmt.Errorf("object storage is empty") + case cfg.VideoReplayFPS <= 0: + return nil, fmt.Errorf("video replay fps is invalid: %d", cfg.VideoReplayFPS) + } + newStorage := &VideoStorage{ + cfg: cfg, + framerate: strconv.Itoa(cfg.VideoReplayFPS), + objStorage: objStorage, + sendToS3Tasks: make(chan *Task, 1), + workersStopped: make(chan struct{}), + } + go newStorage.runWorker() + return newStorage, nil +} + +func (v *VideoStorage) makeVideo(sessID uint64, filesPath string) error { + files, _ := ioutil.ReadDir(filesPath) + log.Printf("There are %d screenshot of session %d\n", len(files), sessID) + + // Try to call ffmpeg and print the result + start := time.Now() + sessionID := strconv.FormatUint(sessID, 10) + imagesPath := "/mnt/efs/screenshots/" + sessionID + "/%06d.jpeg" + videoPath := "/mnt/efs/screenshots/" + sessionID + "/replay.mp4" + cmd := exec.Command("ffmpeg", "-y", "-f", "image2", "-framerate", v.framerate, "-start_number", "000000", "-i", + imagesPath, "-vf", "scale=-2:1064", "-c:v", "libx264", "-preset", "ultrafast", "-crf", "23", + videoPath) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + log.Fatalf("Failed to execute command: %v, stderr: %v", err, stderr.String()) + } + log.Printf("made video replay in %v", time.Since(start)) + v.sendToS3Tasks <- &Task{sessionID: sessionID, path: videoPath} + return nil +} + +func (v *VideoStorage) sendToS3(task *Task) { + start := time.Now() + // Read video file from disk + video, err := ioutil.ReadFile(task.path) + if err != nil { + log.Fatalf("Failed to read video file: %v", err) + } + // Upload video file to S3 + if err := v.objStorage.Upload(bytes.NewReader(video), task.sessionID+"/replay.mp4", "video/mp4", objectstorage.NoCompression); err != nil { + log.Fatalf("Storage: start upload video replay failed. %s", err) + } + log.Printf("Video file (size: %d) uploaded successfully in %v", len(video), time.Since(start)) + return +} + +func (v *VideoStorage) Process(sessID uint64, filesPath string) error { + return v.makeVideo(sessID, filesPath) +} + +func (v *VideoStorage) Wait() { + v.sendToS3Tasks <- NewBreakTask() + <-v.workersStopped +} + +func (v *VideoStorage) runWorker() { + for { + select { + case task := <-v.sendToS3Tasks: + if task.isBreakTask { + v.workersStopped <- struct{}{} + continue + } + v.sendToS3(task) + } + } +} diff --git a/backend/pkg/db/postgres/helpers.go b/backend/pkg/db/postgres/helpers.go index 922199156..4bec6dcb3 100644 --- a/backend/pkg/db/postgres/helpers.go +++ b/backend/pkg/db/postgres/helpers.go @@ -6,11 +6,11 @@ import ( "openreplay/backend/pkg/messages" ) -func GetIssueScore(issueEvent *messages.IssueEvent) int { - switch issueEvent.Type { +func GetIssueScore(issueType string) int { + switch issueType { case "crash", "dead_click", "memory", "cpu": return 1000 - case "bad_request", "excessive_scrolling", "click_rage", "missing_resource": + case "bad_request", "excessive_scrolling", "click_rage", "missing_resource", "tap_rage": return 500 case "slow_resource", "slow_page_load": return 100 diff --git a/backend/pkg/db/postgres/mobile.go b/backend/pkg/db/postgres/mobile.go new file mode 100644 index 000000000..dd0179cb8 --- /dev/null +++ b/backend/pkg/db/postgres/mobile.go @@ -0,0 +1,163 @@ +package postgres + +import ( + "log" + "openreplay/backend/pkg/hashid" + "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/sessions" + "openreplay/backend/pkg/url" +) + +func (conn *Conn) InsertIOSSessionStart(sessionID uint64, e *messages.IOSSessionStart) error { + log.Printf("handle ios session %d start: %v", sessionID, e) + return nil +} + +func (conn *Conn) InsertIOSSessionEnd(sessionID uint64, e *messages.IOSSessionEnd) error { + log.Printf("handle ios session %d end: %v", sessionID, e) + return nil +} + +func (conn *Conn) InsertIOSCustomEvent(session *sessions.Session, e *messages.IOSCustomEvent) error { + if err := conn.InsertCustomEvent(session.SessionID, e.Timestamp, truncSqIdx(e.Index), e.Name, e.Payload); err != nil { + return err + } + conn.InsertAutocompleteValue(session.SessionID, session.ProjectID, "CUSTOM_IOS", e.Name) + return nil +} + +func (conn *Conn) InsertIOSNetworkCall(sess *sessions.Session, e *messages.IOSNetworkCall) error { + err := conn.InsertRequest(sess.SessionID, e.Timestamp, truncSqIdx(e.Index), e.URL, e.Duration, e.Status < 400) + if err == nil { + conn.InsertAutocompleteValue(sess.SessionID, sess.ProjectID, "REQUEST_IOS", url.DiscardURLQuery(e.URL)) + } + return err +} + +// TODO: add title column to views table and view_type (screen_enter, screen_leave) +func (conn *Conn) InsertIOSScreenEnter(sess *sessions.Session, screenEnter *messages.IOSScreenEnter) error { + if err := conn.Pool.Exec(` + INSERT INTO events_ios.views ( + session_id, timestamp, seq_index, name + ) VALUES ( + $1, $2, $3, $4 + )`, + sess.SessionID, screenEnter.Timestamp, screenEnter.Index, screenEnter.ViewName, + ); err != nil { + return err + } + conn.InsertAutocompleteValue(sess.SessionID, sess.ProjectID, "VIEW_IOS", screenEnter.ViewName) + return nil +} + +func (conn *Conn) InsertIOSScreenLeave(sess *sessions.Session, screenEnter *messages.IOSScreenLeave) error { + if err := conn.Pool.Exec(` + INSERT INTO events_ios.views ( + session_id, timestamp, seq_index, name + ) VALUES ( + $1, $2, $3, $4 + )`, + sess.SessionID, screenEnter.Timestamp, screenEnter.Index, screenEnter.ViewName, + ); err != nil { + return err + } + return nil +} + +func (conn *Conn) InsertIOSClickEvent(sess *sessions.Session, clickEvent *messages.IOSClickEvent) error { + if err := conn.Pool.Exec(` + INSERT INTO events_ios.taps ( + session_id, timestamp, seq_index, label + ) VALUES ( + $1, $2, $3, $4 + )`, + sess.SessionID, clickEvent.Timestamp, clickEvent.Index, clickEvent.Label, + ); err != nil { + return err + } + conn.InsertAutocompleteValue(sess.SessionID, sess.ProjectID, "CLICK_IOS", clickEvent.Label) + return nil +} + +func (conn *Conn) InsertIOSSwipeEvent(sess *sessions.Session, swipeEvent *messages.IOSSwipeEvent) error { + if err := conn.Pool.Exec(` + INSERT INTO events_ios.swipes ( + session_id, timestamp, seq_index, label, direction + ) VALUES ( + $1, $2, $3, $4, $5 + )`, + sess.SessionID, swipeEvent.Timestamp, swipeEvent.Index, swipeEvent.Label, swipeEvent.Direction, + ); err != nil { + return err + } + conn.InsertAutocompleteValue(sess.SessionID, sess.ProjectID, "SWIPE_IOS", swipeEvent.Label) + return nil +} + +func (conn *Conn) InsertIOSInputEvent(sess *sessions.Session, inputEvent *messages.IOSInputEvent) error { + if err := conn.Pool.Exec(` + INSERT INTO events_ios.inputs ( + session_id, timestamp, seq_index, label + ) VALUES ( + $1, $2, $3, $4 + )`, + sess.SessionID, inputEvent.Timestamp, inputEvent.Index, inputEvent.Label, + ); err != nil { + return err + } + conn.InsertAutocompleteValue(sess.SessionID, sess.ProjectID, "INPUT_IOS", inputEvent.Label) + return nil +} + +func (conn *Conn) InsertIOSCrash(sessionID uint64, projectID uint32, crash *messages.IOSCrash) error { + crashID := hashid.IOSCrashID(projectID, crash) + + if err := conn.Pool.Exec(` + INSERT INTO public.crashes_ios ( + project_id, crash_ios_id, name, reason, stacktrace + ) VALUES ( + $1, $2, $3, $4, $5 + ) ON CONFLICT DO NOTHING`, + projectID, crashID, crash.Name, crash.Reason, crash.Stacktrace, + ); err != nil { + return err + } + if err := conn.Pool.Exec(` + INSERT INTO events_common.crashes ( + session_id, timestamp, seq_index, crash_ios_id + ) VALUES ( + $1, $2, $3, $4 + )`, + sessionID, crash.Timestamp, crash.Index, crashID, + ); err != nil { + return err + } + return nil +} + +func (conn *Conn) InsertIOSIssueEvent(sess *sessions.Session, e *messages.IOSIssueEvent) error { + issueID := hashid.IOSIssueID(sess.ProjectID, e) + payload := &e.Payload + if *payload == "" || *payload == "{}" { + payload = nil + } + + if err := conn.bulks.Get("webIssues").Append(sess.ProjectID, issueID, e.Type, e.ContextString); err != nil { + log.Printf("insert web issue err: %s", err) + } + if err := conn.bulks.Get("webIssueEvents").Append(sess.SessionID, issueID, e.Timestamp, truncSqIdx(e.Index), payload); err != nil { + log.Printf("insert web issue event err: %s", err) + } + return nil +} + +type IOSCrash struct { + Timestamp uint64 `json:"timestamp"` + Name string `json:"name"` + Reason string `json:"reason"` + Stacktrace string `json:"stacktrace"` +} + +type WebCrash struct { + Timestamp uint64 `json:"timestamp"` +} diff --git a/backend/pkg/handlers/ios/clickRage.go b/backend/pkg/handlers/ios/tapRage.go similarity index 58% rename from backend/pkg/handlers/ios/clickRage.go rename to backend/pkg/handlers/ios/tapRage.go index 91283de90..9d6530109 100644 --- a/backend/pkg/handlers/ios/clickRage.go +++ b/backend/pkg/handlers/ios/tapRage.go @@ -1,21 +1,16 @@ package ios import ( + "encoding/json" + "log" "openreplay/backend/pkg/handlers" - "openreplay/backend/pkg/handlers/web" . "openreplay/backend/pkg/messages" ) -/* - Handler name: ClickRage - Input events: IOSClickEvent, - IOSSessionEnd - Output event: IOSIssueEvent -*/ +const TapTimeDiff = 300 +const MinTapsInARow = 3 -const CLICK_TIME_DIFF = 200 - -type ClickRageDetector struct { +type TapRageDetector struct { handlers.ReadyMessageStore lastTimestamp uint64 lastLabel string @@ -24,11 +19,39 @@ type ClickRageDetector struct { countsInARow int } -func (h *ClickRageDetector) Handle(message Message, messageID uint64, timestamp uint64) Message { +func (h *TapRageDetector) createPayload() string { + p, err := json.Marshal(struct{ Count int }{h.countsInARow}) + if err != nil { + log.Printf("can't marshal TapRage payload to json: %s", err) + return "" + } + return string(p) +} + +func (h *TapRageDetector) Build() Message { + if h.countsInARow >= MinTapsInARow { + event := &IOSIssueEvent{ + Type: "tap_rage", + ContextString: h.lastLabel, + Timestamp: h.firstInARawTimestamp, + Payload: h.createPayload(), + } + event.Index = h.firstInARawSeqIndex // Associated Index/ MessageID ? + return event + } + h.lastTimestamp = 0 + h.lastLabel = "" + h.firstInARawTimestamp = 0 + h.firstInARawSeqIndex = 0 + h.countsInARow = 0 + return nil +} + +func (h *TapRageDetector) Handle(message Message, timestamp uint64) Message { var event Message = nil switch m := message.(type) { case *IOSClickEvent: - if h.lastTimestamp+CLICK_TIME_DIFF < m.Timestamp && h.lastLabel == m.Label { + if h.lastTimestamp+TapTimeDiff < m.Timestamp && h.lastLabel == m.Label { h.lastTimestamp = m.Timestamp h.countsInARow += 1 return nil @@ -46,21 +69,3 @@ func (h *ClickRageDetector) Handle(message Message, messageID uint64, timestamp } return event } - -func (h *ClickRageDetector) Build() Message { - if h.countsInARow >= web.MinClicksInARow { - event := &IOSIssueEvent{ - Type: "click_rage", - ContextString: h.lastLabel, - } - event.Timestamp = h.firstInARawTimestamp - event.Index = h.firstInARawSeqIndex // Associated Index/ MessageID ? - return event - } - h.lastTimestamp = 0 - h.lastLabel = "" - h.firstInARawTimestamp = 0 - h.firstInARawSeqIndex = 0 - h.countsInARow = 0 - return nil -} diff --git a/backend/pkg/hashid/hashid.go b/backend/pkg/hashid/hashid.go index 5bcb23578..ecba9eb09 100644 --- a/backend/pkg/hashid/hashid.go +++ b/backend/pkg/hashid/hashid.go @@ -16,6 +16,13 @@ func IssueID(projectID uint32, e *messages.IssueEvent) string { return strconv.FormatUint(uint64(projectID), 16) + hex.EncodeToString(hash.Sum(nil)) } +func IOSIssueID(projectID uint32, e *messages.IOSIssueEvent) string { + hash := fnv.New128a() + hash.Write([]byte(e.Type)) + hash.Write([]byte(e.ContextString)) + return strconv.FormatUint(uint64(projectID), 16) + hex.EncodeToString(hash.Sum(nil)) +} + func IOSCrashID(projectID uint32, crash *messages.IOSCrash) string { hash := fnv.New128a() hash.Write([]byte(crash.Name)) diff --git a/backend/pkg/messages/filters.go b/backend/pkg/messages/filters.go index 540ebd06c..179af6cef 100644 --- a/backend/pkg/messages/filters.go +++ b/backend/pkg/messages/filters.go @@ -2,13 +2,13 @@ package messages func IsReplayerType(id int) bool { - return 1 != id && 3 != id && 17 != id && 23 != id && 24 != id && 25 != id && 26 != id && 27 != id && 28 != id && 29 != id && 30 != id && 31 != id && 32 != id && 42 != id && 56 != id && 62 != id && 63 != id && 64 != id && 66 != id && 78 != id && 80 != id && 81 != id && 82 != id && 112 != id && 115 != id && 125 != id && 126 != id && 127 != id && 107 != id && 91 != id && 92 != id && 94 != id && 95 != id && 97 != id && 98 != id && 99 != id && 101 != id && 104 != id && 110 != id && 111 != id + return 1 != id && 3 != id && 17 != id && 23 != id && 24 != id && 25 != id && 26 != id && 27 != id && 28 != id && 29 != id && 30 != id && 31 != id && 32 != id && 42 != id && 56 != id && 62 != id && 63 != id && 64 != id && 66 != id && 78 != id && 80 != id && 81 != id && 82 != id && 112 != id && 115 != id && 125 != id && 126 != id && 127 != id && 90 != id && 91 != id && 92 != id && 94 != id && 95 != id && 97 != id && 98 != id && 104 != id && 107 != id && 110 != id && 111 != id } func IsIOSType(id int) bool { - return 107 == id || 90 == id || 91 == id || 92 == id || 93 == id || 94 == id || 95 == id || 96 == id || 97 == id || 98 == id || 99 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 110 == id || 111 == id + return 90 == id || 91 == id || 92 == id || 93 == id || 94 == id || 95 == id || 96 == id || 97 == id || 98 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 106 == id || 107 == id || 110 == id || 111 == id } func IsDOMType(id int) bool { - return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 117 == id || 118 == id || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id -} + return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 117 == id || 118 == id || 93 == id || 96 == id || 100 == id || 101 == id || 102 == id || 103 == id || 105 == id || 106 == id +} \ No newline at end of file diff --git a/backend/pkg/messages/get-timestamp.go b/backend/pkg/messages/get-timestamp.go index 8b44764a7..21d9aa57e 100644 --- a/backend/pkg/messages/get-timestamp.go +++ b/backend/pkg/messages/get-timestamp.go @@ -4,9 +4,6 @@ package messages func GetTimestamp(message Message) uint64 { switch msg := message.(type) { - case *IOSBatchMeta: - return msg.Timestamp - case *IOSSessionStart: return msg.Timestamp @@ -31,10 +28,7 @@ func GetTimestamp(message Message) uint64 { case *IOSCrash: return msg.Timestamp - case *IOSScreenEnter: - return msg.Timestamp - - case *IOSScreenLeave: + case *IOSViewComponentEvent: return msg.Timestamp case *IOSClickEvent: @@ -55,6 +49,12 @@ func GetTimestamp(message Message) uint64 { case *IOSNetworkCall: return msg.Timestamp + case *IOSSwipeEvent: + return msg.Timestamp + + case *IOSBatchMeta: + return msg.Timestamp + case *IOSIssueEvent: return msg.Timestamp diff --git a/backend/pkg/messages/iterator-images.go b/backend/pkg/messages/iterator-images.go new file mode 100644 index 000000000..ec63fd328 --- /dev/null +++ b/backend/pkg/messages/iterator-images.go @@ -0,0 +1,24 @@ +package messages + +type imagesIteratorImpl struct { + coreIterator MessageIterator + handler ImageMessageHandler +} + +type ImageMessageHandler func(data []byte, sessID uint64) + +func NewImagesMessageIterator(messageHandler ImageMessageHandler, messageFilter []int, autoDecode bool) MessageIterator { + enderIter := &imagesIteratorImpl{ + handler: messageHandler, + } + //enderIter.coreIterator = NewMessageIterator(enderIter.handle, messageFilter, autoDecode) + return enderIter +} + +func (e *imagesIteratorImpl) handle(message Message) { + // +} + +func (e *imagesIteratorImpl) Iterate(batchData []byte, batchInfo *BatchInfo) { + e.handler(batchData, batchInfo.sessionID) +} diff --git a/backend/pkg/messages/iterator.go b/backend/pkg/messages/iterator.go index 4a39a7fce..c6ba792f4 100644 --- a/backend/pkg/messages/iterator.go +++ b/backend/pkg/messages/iterator.go @@ -43,6 +43,7 @@ func NewMessageIterator(messageHandler MessageHandler, messageFilter []int, auto iter.preFilter = map[int]struct{}{ MsgBatchMetadata: {}, MsgBatchMeta: {}, MsgTimestamp: {}, MsgSessionStart: {}, MsgSessionEnd: {}, MsgSetPageLocation: {}, + MsgIOSBatchMeta: {}, } return iter } @@ -110,7 +111,8 @@ func (i *messageIteratorImpl) Iterate(batchData []byte, batchInfo *BatchInfo) { // Update timestamp value for iOS message types if IsIOSType(msgType) { - msg.Meta().Timestamp = i.getIOSTimestamp(msg) + msgTime := i.getIOSTimestamp(msg) + msg.Meta().Timestamp = msgTime } // Process message @@ -184,6 +186,16 @@ func (i *messageIteratorImpl) preprocessing(msg Message) error { i.messageInfo.Url = m.URL // Save session page url in cache for using in next batches i.urls.Set(i.messageInfo.batch.sessionID, m.URL) + + case *IOSBatchMeta: + if i.messageInfo.Index > 1 { // Might be several 0-0 BatchMeta in a row without an error though + return fmt.Errorf("batchMeta found at the end of the batch, info: %s", i.batchInfo.Info()) + } + i.messageInfo.Index = m.FirstIndex + i.messageInfo.Timestamp = m.Timestamp + if m.Timestamp == 0 { + i.zeroTsLog("IOSBatchMeta") + } } return nil } diff --git a/backend/pkg/messages/messages.go b/backend/pkg/messages/messages.go index 2a4264fae..61b3a5c66 100644 --- a/backend/pkg/messages/messages.go +++ b/backend/pkg/messages/messages.go @@ -2,111 +2,112 @@ package messages const ( - MsgTimestamp = 0 - MsgSessionStart = 1 - MsgSessionEndDeprecated = 3 - MsgSetPageLocation = 4 - MsgSetViewportSize = 5 - MsgSetViewportScroll = 6 - MsgCreateDocument = 7 - MsgCreateElementNode = 8 - MsgCreateTextNode = 9 - MsgMoveNode = 10 - MsgRemoveNode = 11 - MsgSetNodeAttribute = 12 - MsgRemoveNodeAttribute = 13 - MsgSetNodeData = 14 - MsgSetCSSData = 15 - MsgSetNodeScroll = 16 - MsgSetInputTarget = 17 - MsgSetInputValue = 18 - MsgSetInputChecked = 19 - MsgMouseMove = 20 - MsgNetworkRequest = 21 - MsgConsoleLog = 22 - MsgPageLoadTiming = 23 - MsgPageRenderTiming = 24 - MsgJSExceptionDeprecated = 25 - MsgIntegrationEvent = 26 - MsgCustomEvent = 27 - MsgUserID = 28 - MsgUserAnonymousID = 29 - MsgMetadata = 30 - MsgPageEvent = 31 - MsgInputEvent = 32 - MsgCSSInsertRule = 37 - MsgCSSDeleteRule = 38 - MsgFetch = 39 - MsgProfiler = 40 - MsgOTable = 41 - MsgStateAction = 42 - MsgRedux = 44 - MsgVuex = 45 - MsgMobX = 46 - MsgNgRx = 47 - MsgGraphQL = 48 - MsgPerformanceTrack = 49 - MsgStringDict = 50 - MsgSetNodeAttributeDict = 51 - MsgResourceTimingDeprecated = 53 - MsgConnectionInformation = 54 - MsgSetPageVisibility = 55 - MsgPerformanceTrackAggr = 56 - MsgLoadFontFace = 57 - MsgSetNodeFocus = 58 - MsgLongTask = 59 - MsgSetNodeAttributeURLBased = 60 - MsgSetCSSDataURLBased = 61 - MsgIssueEventDeprecated = 62 - MsgTechnicalInfo = 63 - MsgCustomIssue = 64 - MsgAssetCache = 66 - MsgCSSInsertRuleURLBased = 67 - MsgMouseClick = 69 - MsgCreateIFrameDocument = 70 - MsgAdoptedSSReplaceURLBased = 71 - MsgAdoptedSSReplace = 72 - MsgAdoptedSSInsertRuleURLBased = 73 - MsgAdoptedSSInsertRule = 74 - MsgAdoptedSSDeleteRule = 75 - MsgAdoptedSSAddOwner = 76 - MsgAdoptedSSRemoveOwner = 77 - MsgJSException = 78 - MsgZustand = 79 - MsgBatchMeta = 80 - MsgBatchMetadata = 81 - MsgPartitionedMessage = 82 - MsgInputChange = 112 - MsgSelectionChange = 113 - MsgMouseThrashing = 114 - MsgUnbindNodes = 115 - MsgResourceTiming = 116 - MsgTabChange = 117 - MsgTabData = 118 - MsgIssueEvent = 125 - MsgSessionEnd = 126 - MsgSessionSearch = 127 - MsgIOSBatchMeta = 107 - MsgIOSSessionStart = 90 - MsgIOSSessionEnd = 91 - MsgIOSMetadata = 92 - MsgIOSCustomEvent = 93 - MsgIOSUserID = 94 - MsgIOSUserAnonymousID = 95 - MsgIOSScreenChanges = 96 - MsgIOSCrash = 97 - MsgIOSScreenEnter = 98 - MsgIOSScreenLeave = 99 - MsgIOSClickEvent = 100 - MsgIOSInputEvent = 101 - MsgIOSPerformanceEvent = 102 - MsgIOSLog = 103 - MsgIOSInternalError = 104 - MsgIOSNetworkCall = 105 - MsgIOSPerformanceAggregated = 110 - MsgIOSIssueEvent = 111 + MsgTimestamp = 0 + MsgSessionStart = 1 + MsgSessionEndDeprecated = 3 + MsgSetPageLocation = 4 + MsgSetViewportSize = 5 + MsgSetViewportScroll = 6 + MsgCreateDocument = 7 + MsgCreateElementNode = 8 + MsgCreateTextNode = 9 + MsgMoveNode = 10 + MsgRemoveNode = 11 + MsgSetNodeAttribute = 12 + MsgRemoveNodeAttribute = 13 + MsgSetNodeData = 14 + MsgSetCSSData = 15 + MsgSetNodeScroll = 16 + MsgSetInputTarget = 17 + MsgSetInputValue = 18 + MsgSetInputChecked = 19 + MsgMouseMove = 20 + MsgNetworkRequest = 21 + MsgConsoleLog = 22 + MsgPageLoadTiming = 23 + MsgPageRenderTiming = 24 + MsgJSExceptionDeprecated = 25 + MsgIntegrationEvent = 26 + MsgCustomEvent = 27 + MsgUserID = 28 + MsgUserAnonymousID = 29 + MsgMetadata = 30 + MsgPageEvent = 31 + MsgInputEvent = 32 + MsgCSSInsertRule = 37 + MsgCSSDeleteRule = 38 + MsgFetch = 39 + MsgProfiler = 40 + MsgOTable = 41 + MsgStateAction = 42 + MsgRedux = 44 + MsgVuex = 45 + MsgMobX = 46 + MsgNgRx = 47 + MsgGraphQL = 48 + MsgPerformanceTrack = 49 + MsgStringDict = 50 + MsgSetNodeAttributeDict = 51 + MsgResourceTimingDeprecated = 53 + MsgConnectionInformation = 54 + MsgSetPageVisibility = 55 + MsgPerformanceTrackAggr = 56 + MsgLoadFontFace = 57 + MsgSetNodeFocus = 58 + MsgLongTask = 59 + MsgSetNodeAttributeURLBased = 60 + MsgSetCSSDataURLBased = 61 + MsgIssueEventDeprecated = 62 + MsgTechnicalInfo = 63 + MsgCustomIssue = 64 + MsgAssetCache = 66 + MsgCSSInsertRuleURLBased = 67 + MsgMouseClick = 69 + MsgCreateIFrameDocument = 70 + MsgAdoptedSSReplaceURLBased = 71 + MsgAdoptedSSReplace = 72 + MsgAdoptedSSInsertRuleURLBased = 73 + MsgAdoptedSSInsertRule = 74 + MsgAdoptedSSDeleteRule = 75 + MsgAdoptedSSAddOwner = 76 + MsgAdoptedSSRemoveOwner = 77 + MsgJSException = 78 + MsgZustand = 79 + MsgBatchMeta = 80 + MsgBatchMetadata = 81 + MsgPartitionedMessage = 82 + MsgInputChange = 112 + MsgSelectionChange = 113 + MsgMouseThrashing = 114 + MsgUnbindNodes = 115 + MsgResourceTiming = 116 + MsgTabChange = 117 + MsgTabData = 118 + MsgIssueEvent = 125 + MsgSessionEnd = 126 + MsgSessionSearch = 127 + MsgIOSSessionStart = 90 + MsgIOSSessionEnd = 91 + MsgIOSMetadata = 92 + MsgIOSCustomEvent = 93 + MsgIOSUserID = 94 + MsgIOSUserAnonymousID = 95 + MsgIOSScreenChanges = 96 + MsgIOSCrash = 97 + MsgIOSViewComponentEvent = 98 + MsgIOSClickEvent = 100 + MsgIOSInputEvent = 101 + MsgIOSPerformanceEvent = 102 + MsgIOSLog = 103 + MsgIOSInternalError = 104 + MsgIOSNetworkCall = 105 + MsgIOSSwipeEvent = 106 + MsgIOSBatchMeta = 107 + MsgIOSPerformanceAggregated = 110 + MsgIOSIssueEvent = 111 ) + type Timestamp struct { message Timestamp uint64 @@ -130,22 +131,22 @@ func (msg *Timestamp) TypeID() int { type SessionStart struct { message - Timestamp uint64 - ProjectID uint64 - TrackerVersion string - RevID string - UserUUID string - UserAgent string - UserOS string - UserOSVersion string - UserBrowser string - UserBrowserVersion string - UserDevice string - UserDeviceType string + Timestamp uint64 + ProjectID uint64 + TrackerVersion string + RevID string + UserUUID string + UserAgent string + UserOS string + UserOSVersion string + UserBrowser string + UserBrowserVersion string + UserDevice string + UserDeviceType string UserDeviceMemorySize uint64 - UserDeviceHeapSize uint64 - UserCountry string - UserID string + UserDeviceHeapSize uint64 + UserCountry string + UserID string } func (msg *SessionStart) Encode() []byte { @@ -202,8 +203,8 @@ func (msg *SessionEndDeprecated) TypeID() int { type SetPageLocation struct { message - URL string - Referrer string + URL string + Referrer string NavigationStart uint64 } @@ -227,7 +228,7 @@ func (msg *SetPageLocation) TypeID() int { type SetViewportSize struct { message - Width uint64 + Width uint64 Height uint64 } @@ -273,6 +274,7 @@ func (msg *SetViewportScroll) TypeID() int { type CreateDocument struct { message + } func (msg *CreateDocument) Encode() []byte { @@ -293,11 +295,11 @@ func (msg *CreateDocument) TypeID() int { type CreateElementNode struct { message - ID uint64 + ID uint64 ParentID uint64 - index uint64 - Tag string - SVG bool + index uint64 + Tag string + SVG bool } func (msg *CreateElementNode) Encode() []byte { @@ -322,9 +324,9 @@ func (msg *CreateElementNode) TypeID() int { type CreateTextNode struct { message - ID uint64 + ID uint64 ParentID uint64 - Index uint64 + Index uint64 } func (msg *CreateTextNode) Encode() []byte { @@ -347,9 +349,9 @@ func (msg *CreateTextNode) TypeID() int { type MoveNode struct { message - ID uint64 + ID uint64 ParentID uint64 - Index uint64 + Index uint64 } func (msg *MoveNode) Encode() []byte { @@ -393,8 +395,8 @@ func (msg *RemoveNode) TypeID() int { type SetNodeAttribute struct { message - ID uint64 - Name string + ID uint64 + Name string Value string } @@ -418,7 +420,7 @@ func (msg *SetNodeAttribute) TypeID() int { type RemoveNodeAttribute struct { message - ID uint64 + ID uint64 Name string } @@ -441,7 +443,7 @@ func (msg *RemoveNodeAttribute) TypeID() int { type SetNodeData struct { message - ID uint64 + ID uint64 Data string } @@ -464,7 +466,7 @@ func (msg *SetNodeData) TypeID() int { type SetCSSData struct { message - ID uint64 + ID uint64 Data string } @@ -488,8 +490,8 @@ func (msg *SetCSSData) TypeID() int { type SetNodeScroll struct { message ID uint64 - X int64 - Y int64 + X int64 + Y int64 } func (msg *SetNodeScroll) Encode() []byte { @@ -512,7 +514,7 @@ func (msg *SetNodeScroll) TypeID() int { type SetInputTarget struct { message - ID uint64 + ID uint64 Label string } @@ -535,9 +537,9 @@ func (msg *SetInputTarget) TypeID() int { type SetInputValue struct { message - ID uint64 + ID uint64 Value string - Mask int64 + Mask int64 } func (msg *SetInputValue) Encode() []byte { @@ -560,7 +562,7 @@ func (msg *SetInputValue) TypeID() int { type SetInputChecked struct { message - ID uint64 + ID uint64 Checked bool } @@ -606,14 +608,14 @@ func (msg *MouseMove) TypeID() int { type NetworkRequest struct { message - Type string - Method string - URL string - Request string - Response string - Status uint64 + Type string + Method string + URL string + Request string + Response string + Status uint64 Timestamp uint64 - Duration uint64 + Duration uint64 } func (msg *NetworkRequest) Encode() []byte { @@ -664,15 +666,15 @@ func (msg *ConsoleLog) TypeID() int { type PageLoadTiming struct { message - RequestStart uint64 - ResponseStart uint64 - ResponseEnd uint64 + RequestStart uint64 + ResponseStart uint64 + ResponseEnd uint64 DomContentLoadedEventStart uint64 - DomContentLoadedEventEnd uint64 - LoadEventStart uint64 - LoadEventEnd uint64 - FirstPaint uint64 - FirstContentfulPaint uint64 + DomContentLoadedEventEnd uint64 + LoadEventStart uint64 + LoadEventEnd uint64 + FirstPaint uint64 + FirstContentfulPaint uint64 } func (msg *PageLoadTiming) Encode() []byte { @@ -701,8 +703,8 @@ func (msg *PageLoadTiming) TypeID() int { type PageRenderTiming struct { message - SpeedIndex uint64 - VisuallyComplete uint64 + SpeedIndex uint64 + VisuallyComplete uint64 TimeToInteractive uint64 } @@ -726,7 +728,7 @@ func (msg *PageRenderTiming) TypeID() int { type JSExceptionDeprecated struct { message - Name string + Name string Message string Payload string } @@ -752,10 +754,10 @@ func (msg *JSExceptionDeprecated) TypeID() int { type IntegrationEvent struct { message Timestamp uint64 - Source string - Name string - Message string - Payload string + Source string + Name string + Message string + Payload string } func (msg *IntegrationEvent) Encode() []byte { @@ -780,7 +782,7 @@ func (msg *IntegrationEvent) TypeID() int { type CustomEvent struct { message - Name string + Name string Payload string } @@ -845,7 +847,7 @@ func (msg *UserAnonymousID) TypeID() int { type Metadata struct { message - Key string + Key string Value string } @@ -868,23 +870,23 @@ func (msg *Metadata) TypeID() int { type PageEvent struct { message - MessageID uint64 - Timestamp uint64 - URL string - Referrer string - Loaded bool - RequestStart uint64 - ResponseStart uint64 - ResponseEnd uint64 + MessageID uint64 + Timestamp uint64 + URL string + Referrer string + Loaded bool + RequestStart uint64 + ResponseStart uint64 + ResponseEnd uint64 DomContentLoadedEventStart uint64 - DomContentLoadedEventEnd uint64 - LoadEventStart uint64 - LoadEventEnd uint64 - FirstPaint uint64 - FirstContentfulPaint uint64 - SpeedIndex uint64 - VisuallyComplete uint64 - TimeToInteractive uint64 + DomContentLoadedEventEnd uint64 + LoadEventStart uint64 + LoadEventEnd uint64 + FirstPaint uint64 + FirstContentfulPaint uint64 + SpeedIndex uint64 + VisuallyComplete uint64 + TimeToInteractive uint64 } func (msg *PageEvent) Encode() []byte { @@ -921,11 +923,11 @@ func (msg *PageEvent) TypeID() int { type InputEvent struct { message - MessageID uint64 - Timestamp uint64 - Value string + MessageID uint64 + Timestamp uint64 + Value string ValueMasked bool - Label string + Label string } func (msg *InputEvent) Encode() []byte { @@ -950,8 +952,8 @@ func (msg *InputEvent) TypeID() int { type CSSInsertRule struct { message - ID uint64 - Rule string + ID uint64 + Rule string Index uint64 } @@ -975,7 +977,7 @@ func (msg *CSSInsertRule) TypeID() int { type CSSDeleteRule struct { message - ID uint64 + ID uint64 Index uint64 } @@ -998,13 +1000,13 @@ func (msg *CSSDeleteRule) TypeID() int { type Fetch struct { message - Method string - URL string - Request string - Response string - Status uint64 + Method string + URL string + Request string + Response string + Status uint64 Timestamp uint64 - Duration uint64 + Duration uint64 } func (msg *Fetch) Encode() []byte { @@ -1031,10 +1033,10 @@ func (msg *Fetch) TypeID() int { type Profiler struct { message - Name string + Name string Duration uint64 - Args string - Result string + Args string + Result string } func (msg *Profiler) Encode() []byte { @@ -1058,7 +1060,7 @@ func (msg *Profiler) TypeID() int { type OTable struct { message - Key string + Key string Value string } @@ -1102,8 +1104,8 @@ func (msg *StateAction) TypeID() int { type Redux struct { message - Action string - State string + Action string + State string Duration uint64 } @@ -1128,7 +1130,7 @@ func (msg *Redux) TypeID() int { type Vuex struct { message Mutation string - State string + State string } func (msg *Vuex) Encode() []byte { @@ -1150,7 +1152,7 @@ func (msg *Vuex) TypeID() int { type MobX struct { message - Type string + Type string Payload string } @@ -1173,8 +1175,8 @@ func (msg *MobX) TypeID() int { type NgRx struct { message - Action string - State string + Action string + State string Duration uint64 } @@ -1200,8 +1202,8 @@ type GraphQL struct { message OperationKind string OperationName string - Variables string - Response string + Variables string + Response string } func (msg *GraphQL) Encode() []byte { @@ -1225,10 +1227,10 @@ func (msg *GraphQL) TypeID() int { type PerformanceTrack struct { message - Frames int64 - Ticks int64 + Frames int64 + Ticks int64 TotalJSHeapSize uint64 - UsedJSHeapSize uint64 + UsedJSHeapSize uint64 } func (msg *PerformanceTrack) Encode() []byte { @@ -1252,7 +1254,7 @@ func (msg *PerformanceTrack) TypeID() int { type StringDict struct { message - Key uint64 + Key uint64 Value string } @@ -1275,8 +1277,8 @@ func (msg *StringDict) TypeID() int { type SetNodeAttributeDict struct { message - ID uint64 - NameKey uint64 + ID uint64 + NameKey uint64 ValueKey uint64 } @@ -1300,14 +1302,14 @@ func (msg *SetNodeAttributeDict) TypeID() int { type ResourceTimingDeprecated struct { message - Timestamp uint64 - Duration uint64 - TTFB uint64 - HeaderSize uint64 + Timestamp uint64 + Duration uint64 + TTFB uint64 + HeaderSize uint64 EncodedBodySize uint64 DecodedBodySize uint64 - URL string - Initiator string + URL string + Initiator string } func (msg *ResourceTimingDeprecated) Encode() []byte { @@ -1336,7 +1338,7 @@ func (msg *ResourceTimingDeprecated) TypeID() int { type ConnectionInformation struct { message Downlink uint64 - Type string + Type string } func (msg *ConnectionInformation) Encode() []byte { @@ -1379,20 +1381,20 @@ func (msg *SetPageVisibility) TypeID() int { type PerformanceTrackAggr struct { message - TimestampStart uint64 - TimestampEnd uint64 - MinFPS uint64 - AvgFPS uint64 - MaxFPS uint64 - MinCPU uint64 - AvgCPU uint64 - MaxCPU uint64 + TimestampStart uint64 + TimestampEnd uint64 + MinFPS uint64 + AvgFPS uint64 + MaxFPS uint64 + MinCPU uint64 + AvgCPU uint64 + MaxCPU uint64 MinTotalJSHeapSize uint64 AvgTotalJSHeapSize uint64 MaxTotalJSHeapSize uint64 - MinUsedJSHeapSize uint64 - AvgUsedJSHeapSize uint64 - MaxUsedJSHeapSize uint64 + MinUsedJSHeapSize uint64 + AvgUsedJSHeapSize uint64 + MaxUsedJSHeapSize uint64 } func (msg *PerformanceTrackAggr) Encode() []byte { @@ -1426,9 +1428,9 @@ func (msg *PerformanceTrackAggr) TypeID() int { type LoadFontFace struct { message - ParentID uint64 - Family string - Source string + ParentID uint64 + Family string + Source string Descriptors string } @@ -1474,12 +1476,12 @@ func (msg *SetNodeFocus) TypeID() int { type LongTask struct { message - Timestamp uint64 - Duration uint64 - Context uint64 + Timestamp uint64 + Duration uint64 + Context uint64 ContainerType uint64 - ContainerSrc string - ContainerId string + ContainerSrc string + ContainerId string ContainerName string } @@ -1507,9 +1509,9 @@ func (msg *LongTask) TypeID() int { type SetNodeAttributeURLBased struct { message - ID uint64 - Name string - Value string + ID uint64 + Name string + Value string BaseURL string } @@ -1534,8 +1536,8 @@ func (msg *SetNodeAttributeURLBased) TypeID() int { type SetCSSDataURLBased struct { message - ID uint64 - Data string + ID uint64 + Data string BaseURL string } @@ -1559,12 +1561,12 @@ func (msg *SetCSSDataURLBased) TypeID() int { type IssueEventDeprecated struct { message - MessageID uint64 - Timestamp uint64 - Type string + MessageID uint64 + Timestamp uint64 + Type string ContextString string - Context string - Payload string + Context string + Payload string } func (msg *IssueEventDeprecated) Encode() []byte { @@ -1590,7 +1592,7 @@ func (msg *IssueEventDeprecated) TypeID() int { type TechnicalInfo struct { message - Type string + Type string Value string } @@ -1613,7 +1615,7 @@ func (msg *TechnicalInfo) TypeID() int { type CustomIssue struct { message - Name string + Name string Payload string } @@ -1657,9 +1659,9 @@ func (msg *AssetCache) TypeID() int { type CSSInsertRuleURLBased struct { message - ID uint64 - Rule string - Index uint64 + ID uint64 + Rule string + Index uint64 BaseURL string } @@ -1684,10 +1686,10 @@ func (msg *CSSInsertRuleURLBased) TypeID() int { type MouseClick struct { message - ID uint64 + ID uint64 HesitationTime uint64 - Label string - Selector string + Label string + Selector string } func (msg *MouseClick) Encode() []byte { @@ -1712,7 +1714,7 @@ func (msg *MouseClick) TypeID() int { type CreateIFrameDocument struct { message FrameID uint64 - ID uint64 + ID uint64 } func (msg *CreateIFrameDocument) Encode() []byte { @@ -1735,7 +1737,7 @@ func (msg *CreateIFrameDocument) TypeID() int { type AdoptedSSReplaceURLBased struct { message SheetID uint64 - Text string + Text string BaseURL string } @@ -1760,7 +1762,7 @@ func (msg *AdoptedSSReplaceURLBased) TypeID() int { type AdoptedSSReplace struct { message SheetID uint64 - Text string + Text string } func (msg *AdoptedSSReplace) Encode() []byte { @@ -1783,8 +1785,8 @@ func (msg *AdoptedSSReplace) TypeID() int { type AdoptedSSInsertRuleURLBased struct { message SheetID uint64 - Rule string - Index uint64 + Rule string + Index uint64 BaseURL string } @@ -1810,8 +1812,8 @@ func (msg *AdoptedSSInsertRuleURLBased) TypeID() int { type AdoptedSSInsertRule struct { message SheetID uint64 - Rule string - Index uint64 + Rule string + Index uint64 } func (msg *AdoptedSSInsertRule) Encode() []byte { @@ -1835,7 +1837,7 @@ func (msg *AdoptedSSInsertRule) TypeID() int { type AdoptedSSDeleteRule struct { message SheetID uint64 - Index uint64 + Index uint64 } func (msg *AdoptedSSDeleteRule) Encode() []byte { @@ -1858,7 +1860,7 @@ func (msg *AdoptedSSDeleteRule) TypeID() int { type AdoptedSSAddOwner struct { message SheetID uint64 - ID uint64 + ID uint64 } func (msg *AdoptedSSAddOwner) Encode() []byte { @@ -1881,7 +1883,7 @@ func (msg *AdoptedSSAddOwner) TypeID() int { type AdoptedSSRemoveOwner struct { message SheetID uint64 - ID uint64 + ID uint64 } func (msg *AdoptedSSRemoveOwner) Encode() []byte { @@ -1903,9 +1905,9 @@ func (msg *AdoptedSSRemoveOwner) TypeID() int { type JSException struct { message - Name string - Message string - Payload string + Name string + Message string + Payload string Metadata string } @@ -1931,7 +1933,7 @@ func (msg *JSException) TypeID() int { type Zustand struct { message Mutation string - State string + State string } func (msg *Zustand) Encode() []byte { @@ -1953,9 +1955,9 @@ func (msg *Zustand) TypeID() int { type BatchMeta struct { message - PageNo uint64 + PageNo uint64 FirstIndex uint64 - Timestamp int64 + Timestamp int64 } func (msg *BatchMeta) Encode() []byte { @@ -1978,11 +1980,11 @@ func (msg *BatchMeta) TypeID() int { type BatchMetadata struct { message - Version uint64 - PageNo uint64 + Version uint64 + PageNo uint64 FirstIndex uint64 - Timestamp int64 - Location string + Timestamp int64 + Location string } func (msg *BatchMetadata) Encode() []byte { @@ -2007,7 +2009,7 @@ func (msg *BatchMetadata) TypeID() int { type PartitionedMessage struct { message - PartNo uint64 + PartNo uint64 PartTotal uint64 } @@ -2030,12 +2032,12 @@ func (msg *PartitionedMessage) TypeID() int { type InputChange struct { message - ID uint64 - Value string - ValueMasked bool - Label string + ID uint64 + Value string + ValueMasked bool + Label string HesitationTime int64 - InputDuration int64 + InputDuration int64 } func (msg *InputChange) Encode() []byte { @@ -2062,8 +2064,8 @@ func (msg *InputChange) TypeID() int { type SelectionChange struct { message SelectionStart uint64 - SelectionEnd uint64 - Selection string + SelectionEnd uint64 + Selection string } func (msg *SelectionChange) Encode() []byte { @@ -2128,16 +2130,16 @@ func (msg *UnbindNodes) TypeID() int { type ResourceTiming struct { message - Timestamp uint64 - Duration uint64 - TTFB uint64 - HeaderSize uint64 + Timestamp uint64 + Duration uint64 + TTFB uint64 + HeaderSize uint64 EncodedBodySize uint64 DecodedBodySize uint64 - URL string - Initiator string + URL string + Initiator string TransferredSize uint64 - Cached bool + Cached bool } func (msg *ResourceTiming) Encode() []byte { @@ -2209,13 +2211,13 @@ func (msg *TabData) TypeID() int { type IssueEvent struct { message - MessageID uint64 - Timestamp uint64 - Type string + MessageID uint64 + Timestamp uint64 + Type string ContextString string - Context string - Payload string - URL string + Context string + Payload string + URL string } func (msg *IssueEvent) Encode() []byte { @@ -2242,7 +2244,7 @@ func (msg *IssueEvent) TypeID() int { type SessionEnd struct { message - Timestamp uint64 + Timestamp uint64 EncryptionKey string } @@ -2286,43 +2288,18 @@ func (msg *SessionSearch) TypeID() int { return 127 } -type IOSBatchMeta struct { - message - Timestamp uint64 - Length uint64 - FirstIndex uint64 -} - -func (msg *IOSBatchMeta) Encode() []byte { - buf := make([]byte, 31) - buf[0] = 107 - p := 1 - p = WriteUint(msg.Timestamp, buf, p) - p = WriteUint(msg.Length, buf, p) - p = WriteUint(msg.FirstIndex, buf, p) - return buf[:p] -} - -func (msg *IOSBatchMeta) Decode() Message { - return msg -} - -func (msg *IOSBatchMeta) TypeID() int { - return 107 -} - type IOSSessionStart struct { message - Timestamp uint64 - ProjectID uint64 + Timestamp uint64 + ProjectID uint64 TrackerVersion string - RevID string - UserUUID string - UserOS string - UserOSVersion string - UserDevice string + RevID string + UserUUID string + UserOS string + UserOSVersion string + UserDevice string UserDeviceType string - UserCountry string + UserCountry string } func (msg *IOSSessionStart) Encode() []byte { @@ -2374,9 +2351,9 @@ func (msg *IOSSessionEnd) TypeID() int { type IOSMetadata struct { message Timestamp uint64 - Length uint64 - Key string - Value string + Length uint64 + Key string + Value string } func (msg *IOSMetadata) Encode() []byte { @@ -2401,9 +2378,9 @@ func (msg *IOSMetadata) TypeID() int { type IOSCustomEvent struct { message Timestamp uint64 - Length uint64 - Name string - Payload string + Length uint64 + Name string + Payload string } func (msg *IOSCustomEvent) Encode() []byte { @@ -2428,8 +2405,8 @@ func (msg *IOSCustomEvent) TypeID() int { type IOSUserID struct { message Timestamp uint64 - Length uint64 - Value string + Length uint64 + Value string } func (msg *IOSUserID) Encode() []byte { @@ -2453,8 +2430,8 @@ func (msg *IOSUserID) TypeID() int { type IOSUserAnonymousID struct { message Timestamp uint64 - Length uint64 - Value string + Length uint64 + Value string } func (msg *IOSUserAnonymousID) Encode() []byte { @@ -2478,11 +2455,11 @@ func (msg *IOSUserAnonymousID) TypeID() int { type IOSScreenChanges struct { message Timestamp uint64 - Length uint64 - X uint64 - Y uint64 - Width uint64 - Height uint64 + Length uint64 + X uint64 + Y uint64 + Width uint64 + Height uint64 } func (msg *IOSScreenChanges) Encode() []byte { @@ -2508,10 +2485,10 @@ func (msg *IOSScreenChanges) TypeID() int { type IOSCrash struct { message - Timestamp uint64 - Length uint64 - Name string - Reason string + Timestamp uint64 + Length uint64 + Name string + Reason string Stacktrace string } @@ -2535,67 +2512,42 @@ func (msg *IOSCrash) TypeID() int { return 97 } -type IOSScreenEnter struct { +type IOSViewComponentEvent struct { message Timestamp uint64 - Length uint64 - Title string - ViewName string + Length uint64 + ScreenName string + ViewName string + Visible bool } -func (msg *IOSScreenEnter) Encode() []byte { - buf := make([]byte, 41+len(msg.Title)+len(msg.ViewName)) +func (msg *IOSViewComponentEvent) Encode() []byte { + buf := make([]byte, 51+len(msg.ScreenName)+len(msg.ViewName)) buf[0] = 98 p := 1 p = WriteUint(msg.Timestamp, buf, p) p = WriteUint(msg.Length, buf, p) - p = WriteString(msg.Title, buf, p) + p = WriteString(msg.ScreenName, buf, p) p = WriteString(msg.ViewName, buf, p) + p = WriteBoolean(msg.Visible, buf, p) return buf[:p] } -func (msg *IOSScreenEnter) Decode() Message { +func (msg *IOSViewComponentEvent) Decode() Message { return msg } -func (msg *IOSScreenEnter) TypeID() int { +func (msg *IOSViewComponentEvent) TypeID() int { return 98 } -type IOSScreenLeave struct { - message - Timestamp uint64 - Length uint64 - Title string - ViewName string -} - -func (msg *IOSScreenLeave) Encode() []byte { - buf := make([]byte, 41+len(msg.Title)+len(msg.ViewName)) - buf[0] = 99 - p := 1 - p = WriteUint(msg.Timestamp, buf, p) - p = WriteUint(msg.Length, buf, p) - p = WriteString(msg.Title, buf, p) - p = WriteString(msg.ViewName, buf, p) - return buf[:p] -} - -func (msg *IOSScreenLeave) Decode() Message { - return msg -} - -func (msg *IOSScreenLeave) TypeID() int { - return 99 -} - type IOSClickEvent struct { message Timestamp uint64 - Length uint64 - Label string - X uint64 - Y uint64 + Length uint64 + Label string + X uint64 + Y uint64 } func (msg *IOSClickEvent) Encode() []byte { @@ -2620,11 +2572,11 @@ func (msg *IOSClickEvent) TypeID() int { type IOSInputEvent struct { message - Timestamp uint64 - Length uint64 - Value string + Timestamp uint64 + Length uint64 + Value string ValueMasked bool - Label string + Label string } func (msg *IOSInputEvent) Encode() []byte { @@ -2650,9 +2602,9 @@ func (msg *IOSInputEvent) TypeID() int { type IOSPerformanceEvent struct { message Timestamp uint64 - Length uint64 - Name string - Value uint64 + Length uint64 + Name string + Value uint64 } func (msg *IOSPerformanceEvent) Encode() []byte { @@ -2677,9 +2629,9 @@ func (msg *IOSPerformanceEvent) TypeID() int { type IOSLog struct { message Timestamp uint64 - Length uint64 - Severity string - Content string + Length uint64 + Severity string + Content string } func (msg *IOSLog) Encode() []byte { @@ -2704,8 +2656,8 @@ func (msg *IOSLog) TypeID() int { type IOSInternalError struct { message Timestamp uint64 - Length uint64 - Content string + Length uint64 + Content string } func (msg *IOSInternalError) Encode() []byte { @@ -2729,29 +2681,29 @@ func (msg *IOSInternalError) TypeID() int { type IOSNetworkCall struct { message Timestamp uint64 - Length uint64 - Duration uint64 - Headers string - Body string - URL string - Success bool - Method string - Status uint64 + Length uint64 + Type string + Method string + URL string + Request string + Response string + Status uint64 + Duration uint64 } func (msg *IOSNetworkCall) Encode() []byte { - buf := make([]byte, 91+len(msg.Headers)+len(msg.Body)+len(msg.URL)+len(msg.Method)) + buf := make([]byte, 91+len(msg.Type)+len(msg.Method)+len(msg.URL)+len(msg.Request)+len(msg.Response)) buf[0] = 105 p := 1 p = WriteUint(msg.Timestamp, buf, p) p = WriteUint(msg.Length, buf, p) - p = WriteUint(msg.Duration, buf, p) - p = WriteString(msg.Headers, buf, p) - p = WriteString(msg.Body, buf, p) - p = WriteString(msg.URL, buf, p) - p = WriteBoolean(msg.Success, buf, p) + p = WriteString(msg.Type, buf, p) p = WriteString(msg.Method, buf, p) + p = WriteString(msg.URL, buf, p) + p = WriteString(msg.Request, buf, p) + p = WriteString(msg.Response, buf, p) p = WriteUint(msg.Status, buf, p) + p = WriteUint(msg.Duration, buf, p) return buf[:p] } @@ -2763,22 +2715,78 @@ func (msg *IOSNetworkCall) TypeID() int { return 105 } +type IOSSwipeEvent struct { + message + Timestamp uint64 + Length uint64 + Label string + X uint64 + Y uint64 + Direction string +} + +func (msg *IOSSwipeEvent) Encode() []byte { + buf := make([]byte, 61+len(msg.Label)+len(msg.Direction)) + buf[0] = 106 + p := 1 + p = WriteUint(msg.Timestamp, buf, p) + p = WriteUint(msg.Length, buf, p) + p = WriteString(msg.Label, buf, p) + p = WriteUint(msg.X, buf, p) + p = WriteUint(msg.Y, buf, p) + p = WriteString(msg.Direction, buf, p) + return buf[:p] +} + +func (msg *IOSSwipeEvent) Decode() Message { + return msg +} + +func (msg *IOSSwipeEvent) TypeID() int { + return 106 +} + +type IOSBatchMeta struct { + message + Timestamp uint64 + Length uint64 + FirstIndex uint64 +} + +func (msg *IOSBatchMeta) Encode() []byte { + buf := make([]byte, 31) + buf[0] = 107 + p := 1 + p = WriteUint(msg.Timestamp, buf, p) + p = WriteUint(msg.Length, buf, p) + p = WriteUint(msg.FirstIndex, buf, p) + return buf[:p] +} + +func (msg *IOSBatchMeta) Decode() Message { + return msg +} + +func (msg *IOSBatchMeta) TypeID() int { + return 107 +} + type IOSPerformanceAggregated struct { message TimestampStart uint64 - TimestampEnd uint64 - MinFPS uint64 - AvgFPS uint64 - MaxFPS uint64 - MinCPU uint64 - AvgCPU uint64 - MaxCPU uint64 - MinMemory uint64 - AvgMemory uint64 - MaxMemory uint64 - MinBattery uint64 - AvgBattery uint64 - MaxBattery uint64 + TimestampEnd uint64 + MinFPS uint64 + AvgFPS uint64 + MaxFPS uint64 + MinCPU uint64 + AvgCPU uint64 + MaxCPU uint64 + MinMemory uint64 + AvgMemory uint64 + MaxMemory uint64 + MinBattery uint64 + AvgBattery uint64 + MaxBattery uint64 } func (msg *IOSPerformanceAggregated) Encode() []byte { @@ -2812,11 +2820,11 @@ func (msg *IOSPerformanceAggregated) TypeID() int { type IOSIssueEvent struct { message - Timestamp uint64 - Type string + Timestamp uint64 + Type string ContextString string - Context string - Payload string + Context string + Payload string } func (msg *IOSIssueEvent) Encode() []byte { @@ -2838,3 +2846,4 @@ func (msg *IOSIssueEvent) Decode() Message { func (msg *IOSIssueEvent) TypeID() int { return 111 } + diff --git a/backend/pkg/messages/read-message.go b/backend/pkg/messages/read-message.go index 8c9aef886..8e151d158 100644 --- a/backend/pkg/messages/read-message.go +++ b/backend/pkg/messages/read-message.go @@ -6,1783 +6,1792 @@ import ( ) func DecodeTimestamp(reader BytesReader) (Message, error) { - var err error = nil - msg := &Timestamp{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &Timestamp{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeSessionStart(reader BytesReader) (Message, error) { - var err error = nil - msg := &SessionStart{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SessionStart{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ProjectID, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TrackerVersion, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.RevID, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserUUID, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserAgent, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserOS, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserOSVersion, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserBrowser, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserBrowserVersion, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDevice, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDeviceType, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDeviceMemorySize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDeviceHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.UserCountry, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserID, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSessionEndDeprecated(reader BytesReader) (Message, error) { - var err error = nil - msg := &SessionEndDeprecated{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &SessionEndDeprecated{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeSetPageLocation(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetPageLocation{} - if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &SetPageLocation{} + if msg.URL, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Referrer, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.NavigationStart, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetViewportSize(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetViewportSize{} - if msg.Width, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetViewportSize{} + if msg.Width, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Height, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetViewportScroll(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetViewportScroll{} - if msg.X, err = reader.ReadInt(); err != nil { - return nil, err - } + var err error = nil + msg := &SetViewportScroll{} + if msg.X, err = reader.ReadInt(); err != nil { + return nil, err + } if msg.Y, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCreateDocument(reader BytesReader) (Message, error) { - var err error = nil - msg := &CreateDocument{} - - return msg, err + var err error = nil + msg := &CreateDocument{} + + return msg, err } func DecodeCreateElementNode(reader BytesReader) (Message, error) { - var err error = nil - msg := &CreateElementNode{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CreateElementNode{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ParentID, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.index, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Tag, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.SVG, err = reader.ReadBoolean(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCreateTextNode(reader BytesReader) (Message, error) { - var err error = nil - msg := &CreateTextNode{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CreateTextNode{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ParentID, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeMoveNode(reader BytesReader) (Message, error) { - var err error = nil - msg := &MoveNode{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &MoveNode{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ParentID, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeRemoveNode(reader BytesReader) (Message, error) { - var err error = nil - msg := &RemoveNode{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &RemoveNode{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeSetNodeAttribute(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeAttribute{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetNodeAttribute{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeRemoveNodeAttribute(reader BytesReader) (Message, error) { - var err error = nil - msg := &RemoveNodeAttribute{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &RemoveNodeAttribute{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetNodeData(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeData{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetNodeData{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Data, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetCSSData(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetCSSData{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetCSSData{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Data, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetNodeScroll(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeScroll{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetNodeScroll{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.X, err = reader.ReadInt(); err != nil { - return nil, err - } + return nil, err + } if msg.Y, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetInputTarget(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetInputTarget{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetInputTarget{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetInputValue(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetInputValue{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetInputValue{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Mask, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetInputChecked(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetInputChecked{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetInputChecked{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Checked, err = reader.ReadBoolean(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeMouseMove(reader BytesReader) (Message, error) { - var err error = nil - msg := &MouseMove{} - if msg.X, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &MouseMove{} + if msg.X, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Y, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeNetworkRequest(reader BytesReader) (Message, error) { - var err error = nil - msg := &NetworkRequest{} - if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &NetworkRequest{} + if msg.Type, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Method, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Request, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Response, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Status, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeConsoleLog(reader BytesReader) (Message, error) { - var err error = nil - msg := &ConsoleLog{} - if msg.Level, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &ConsoleLog{} + if msg.Level, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodePageLoadTiming(reader BytesReader) (Message, error) { - var err error = nil - msg := &PageLoadTiming{} - if msg.RequestStart, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &PageLoadTiming{} + if msg.RequestStart, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ResponseStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ResponseEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DomContentLoadedEventStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DomContentLoadedEventEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.LoadEventStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.LoadEventEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.FirstPaint, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.FirstContentfulPaint, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodePageRenderTiming(reader BytesReader) (Message, error) { - var err error = nil - msg := &PageRenderTiming{} - if msg.SpeedIndex, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &PageRenderTiming{} + if msg.SpeedIndex, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.VisuallyComplete, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TimeToInteractive, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeJSExceptionDeprecated(reader BytesReader) (Message, error) { - var err error = nil - msg := &JSExceptionDeprecated{} - if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &JSExceptionDeprecated{} + if msg.Name, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Message, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIntegrationEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IntegrationEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IntegrationEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Source, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Message, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCustomEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &CustomEvent{} - if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &CustomEvent{} + if msg.Name, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeUserID(reader BytesReader) (Message, error) { - var err error = nil - msg := &UserID{} - if msg.ID, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &UserID{} + if msg.ID, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeUserAnonymousID(reader BytesReader) (Message, error) { - var err error = nil - msg := &UserAnonymousID{} - if msg.ID, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &UserAnonymousID{} + if msg.ID, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeMetadata(reader BytesReader) (Message, error) { - var err error = nil - msg := &Metadata{} - if msg.Key, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Metadata{} + if msg.Key, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodePageEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &PageEvent{} - if msg.MessageID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &PageEvent{} + if msg.MessageID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Referrer, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Loaded, err = reader.ReadBoolean(); err != nil { - return nil, err - } + return nil, err + } if msg.RequestStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ResponseStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ResponseEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DomContentLoadedEventStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DomContentLoadedEventEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.LoadEventStart, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.LoadEventEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.FirstPaint, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.FirstContentfulPaint, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.SpeedIndex, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.VisuallyComplete, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TimeToInteractive, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeInputEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &InputEvent{} - if msg.MessageID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &InputEvent{} + if msg.MessageID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ValueMasked, err = reader.ReadBoolean(); err != nil { - return nil, err - } + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCSSInsertRule(reader BytesReader) (Message, error) { - var err error = nil - msg := &CSSInsertRule{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CSSInsertRule{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Rule, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCSSDeleteRule(reader BytesReader) (Message, error) { - var err error = nil - msg := &CSSDeleteRule{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CSSDeleteRule{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeFetch(reader BytesReader) (Message, error) { - var err error = nil - msg := &Fetch{} - if msg.Method, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Fetch{} + if msg.Method, err = reader.ReadString(); err != nil { + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Request, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Response, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Status, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeProfiler(reader BytesReader) (Message, error) { - var err error = nil - msg := &Profiler{} - if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Profiler{} + if msg.Name, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Args, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Result, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeOTable(reader BytesReader) (Message, error) { - var err error = nil - msg := &OTable{} - if msg.Key, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &OTable{} + if msg.Key, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeStateAction(reader BytesReader) (Message, error) { - var err error = nil - msg := &StateAction{} - if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &StateAction{} + if msg.Type, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeRedux(reader BytesReader) (Message, error) { - var err error = nil - msg := &Redux{} - if msg.Action, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Redux{} + if msg.Action, err = reader.ReadString(); err != nil { + return nil, err + } if msg.State, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeVuex(reader BytesReader) (Message, error) { - var err error = nil - msg := &Vuex{} - if msg.Mutation, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Vuex{} + if msg.Mutation, err = reader.ReadString(); err != nil { + return nil, err + } if msg.State, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeMobX(reader BytesReader) (Message, error) { - var err error = nil - msg := &MobX{} - if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &MobX{} + if msg.Type, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeNgRx(reader BytesReader) (Message, error) { - var err error = nil - msg := &NgRx{} - if msg.Action, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &NgRx{} + if msg.Action, err = reader.ReadString(); err != nil { + return nil, err + } if msg.State, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeGraphQL(reader BytesReader) (Message, error) { - var err error = nil - msg := &GraphQL{} - if msg.OperationKind, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &GraphQL{} + if msg.OperationKind, err = reader.ReadString(); err != nil { + return nil, err + } if msg.OperationName, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Variables, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Response, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodePerformanceTrack(reader BytesReader) (Message, error) { - var err error = nil - msg := &PerformanceTrack{} - if msg.Frames, err = reader.ReadInt(); err != nil { - return nil, err - } + var err error = nil + msg := &PerformanceTrack{} + if msg.Frames, err = reader.ReadInt(); err != nil { + return nil, err + } if msg.Ticks, err = reader.ReadInt(); err != nil { - return nil, err - } + return nil, err + } if msg.TotalJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.UsedJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeStringDict(reader BytesReader) (Message, error) { - var err error = nil - msg := &StringDict{} - if msg.Key, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &StringDict{} + if msg.Key, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetNodeAttributeDict(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeAttributeDict{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetNodeAttributeDict{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.NameKey, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ValueKey, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeResourceTimingDeprecated(reader BytesReader) (Message, error) { - var err error = nil - msg := &ResourceTimingDeprecated{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &ResourceTimingDeprecated{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TTFB, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.HeaderSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.EncodedBodySize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DecodedBodySize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Initiator, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeConnectionInformation(reader BytesReader) (Message, error) { - var err error = nil - msg := &ConnectionInformation{} - if msg.Downlink, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &ConnectionInformation{} + if msg.Downlink, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetPageVisibility(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetPageVisibility{} - if msg.hidden, err = reader.ReadBoolean(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &SetPageVisibility{} + if msg.hidden, err = reader.ReadBoolean(); err != nil { + return nil, err + } + return msg, err } func DecodePerformanceTrackAggr(reader BytesReader) (Message, error) { - var err error = nil - msg := &PerformanceTrackAggr{} - if msg.TimestampStart, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &PerformanceTrackAggr{} + if msg.TimestampStart, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.TimestampEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinTotalJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgTotalJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxTotalJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinUsedJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgUsedJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxUsedJSHeapSize, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeLoadFontFace(reader BytesReader) (Message, error) { - var err error = nil - msg := &LoadFontFace{} - if msg.ParentID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &LoadFontFace{} + if msg.ParentID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Family, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Source, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Descriptors, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetNodeFocus(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeFocus{} - if msg.ID, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &SetNodeFocus{} + if msg.ID, err = reader.ReadInt(); err != nil { + return nil, err + } + return msg, err } func DecodeLongTask(reader BytesReader) (Message, error) { - var err error = nil - msg := &LongTask{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &LongTask{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Context, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ContainerType, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.ContainerSrc, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ContainerId, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ContainerName, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetNodeAttributeURLBased(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetNodeAttributeURLBased{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetNodeAttributeURLBased{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.BaseURL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSetCSSDataURLBased(reader BytesReader) (Message, error) { - var err error = nil - msg := &SetCSSDataURLBased{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SetCSSDataURLBased{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Data, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.BaseURL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIssueEventDeprecated(reader BytesReader) (Message, error) { - var err error = nil - msg := &IssueEventDeprecated{} - if msg.MessageID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IssueEventDeprecated{} + if msg.MessageID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ContextString, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Context, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeTechnicalInfo(reader BytesReader) (Message, error) { - var err error = nil - msg := &TechnicalInfo{} - if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &TechnicalInfo{} + if msg.Type, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCustomIssue(reader BytesReader) (Message, error) { - var err error = nil - msg := &CustomIssue{} - if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &CustomIssue{} + if msg.Name, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAssetCache(reader BytesReader) (Message, error) { - var err error = nil - msg := &AssetCache{} - if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &AssetCache{} + if msg.URL, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeCSSInsertRuleURLBased(reader BytesReader) (Message, error) { - var err error = nil - msg := &CSSInsertRuleURLBased{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CSSInsertRuleURLBased{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Rule, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.BaseURL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeMouseClick(reader BytesReader) (Message, error) { - var err error = nil - msg := &MouseClick{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &MouseClick{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.HesitationTime, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Selector, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeCreateIFrameDocument(reader BytesReader) (Message, error) { - var err error = nil - msg := &CreateIFrameDocument{} - if msg.FrameID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &CreateIFrameDocument{} + if msg.FrameID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSReplaceURLBased(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSReplaceURLBased{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSReplaceURLBased{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Text, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.BaseURL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSReplace(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSReplace{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSReplace{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Text, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSInsertRuleURLBased(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSInsertRuleURLBased{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSInsertRuleURLBased{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Rule, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.BaseURL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSInsertRule(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSInsertRule{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSInsertRule{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Rule, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSDeleteRule(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSDeleteRule{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSDeleteRule{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Index, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSAddOwner(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSAddOwner{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSAddOwner{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeAdoptedSSRemoveOwner(reader BytesReader) (Message, error) { - var err error = nil - msg := &AdoptedSSRemoveOwner{} - if msg.SheetID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &AdoptedSSRemoveOwner{} + if msg.SheetID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeJSException(reader BytesReader) (Message, error) { - var err error = nil - msg := &JSException{} - if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &JSException{} + if msg.Name, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Message, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Metadata, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeZustand(reader BytesReader) (Message, error) { - var err error = nil - msg := &Zustand{} - if msg.Mutation, err = reader.ReadString(); err != nil { - return nil, err - } + var err error = nil + msg := &Zustand{} + if msg.Mutation, err = reader.ReadString(); err != nil { + return nil, err + } if msg.State, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeBatchMeta(reader BytesReader) (Message, error) { - var err error = nil - msg := &BatchMeta{} - if msg.PageNo, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &BatchMeta{} + if msg.PageNo, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.FirstIndex, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Timestamp, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeBatchMetadata(reader BytesReader) (Message, error) { - var err error = nil - msg := &BatchMetadata{} - if msg.Version, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &BatchMetadata{} + if msg.Version, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.PageNo, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.FirstIndex, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Timestamp, err = reader.ReadInt(); err != nil { - return nil, err - } + return nil, err + } if msg.Location, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodePartitionedMessage(reader BytesReader) (Message, error) { - var err error = nil - msg := &PartitionedMessage{} - if msg.PartNo, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &PartitionedMessage{} + if msg.PartNo, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.PartTotal, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeInputChange(reader BytesReader) (Message, error) { - var err error = nil - msg := &InputChange{} - if msg.ID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &InputChange{} + if msg.ID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ValueMasked, err = reader.ReadBoolean(); err != nil { - return nil, err - } + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.HesitationTime, err = reader.ReadInt(); err != nil { - return nil, err - } + return nil, err + } if msg.InputDuration, err = reader.ReadInt(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSelectionChange(reader BytesReader) (Message, error) { - var err error = nil - msg := &SelectionChange{} - if msg.SelectionStart, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SelectionChange{} + if msg.SelectionStart, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.SelectionEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Selection, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeMouseThrashing(reader BytesReader) (Message, error) { - var err error = nil - msg := &MouseThrashing{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &MouseThrashing{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeUnbindNodes(reader BytesReader) (Message, error) { - var err error = nil - msg := &UnbindNodes{} - if msg.TotalRemovedPercent, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &UnbindNodes{} + if msg.TotalRemovedPercent, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeResourceTiming(reader BytesReader) (Message, error) { - var err error = nil - msg := &ResourceTiming{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &ResourceTiming{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TTFB, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.HeaderSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.EncodedBodySize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.DecodedBodySize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Initiator, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.TransferredSize, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Cached, err = reader.ReadBoolean(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeTabChange(reader BytesReader) (Message, error) { - var err error = nil - msg := &TabChange{} - if msg.TabId, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &TabChange{} + if msg.TabId, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeTabData(reader BytesReader) (Message, error) { - var err error = nil - msg := &TabData{} - if msg.TabId, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &TabData{} + if msg.TabId, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err } func DecodeIssueEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IssueEvent{} - if msg.MessageID, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IssueEvent{} + if msg.MessageID, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ContextString, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Context, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSessionEnd(reader BytesReader) (Message, error) { - var err error = nil - msg := &SessionEnd{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SessionEnd{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.EncryptionKey, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeSessionSearch(reader BytesReader) (Message, error) { - var err error = nil - msg := &SessionSearch{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &SessionSearch{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Partition, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err -} - -func DecodeIOSBatchMeta(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSBatchMeta{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.FirstIndex, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSSessionStart(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSSessionStart{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSSessionStart{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.ProjectID, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.TrackerVersion, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.RevID, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserUUID, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserOS, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserOSVersion, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDevice, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserDeviceType, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.UserCountry, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSSessionEnd(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSSessionEnd{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + var err error = nil + msg := &IOSSessionEnd{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeIOSMetadata(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSMetadata{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSMetadata{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Key, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSCustomEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSCustomEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSCustomEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSUserID(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSUserID{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSUserID{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSUserAnonymousID(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSUserAnonymousID{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSUserAnonymousID{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSScreenChanges(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSScreenChanges{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSScreenChanges{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.X, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Y, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Width, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Height, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSCrash(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSCrash{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSCrash{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Reason, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Stacktrace, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } -func DecodeIOSScreenEnter(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSScreenEnter{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } +func DecodeIOSViewComponentEvent(reader BytesReader) (Message, error) { + var err error = nil + msg := &IOSViewComponentEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Title, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } + if msg.ScreenName, err = reader.ReadString(); err != nil { + return nil, err + } if msg.ViewName, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err -} - -func DecodeIOSScreenLeave(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSScreenLeave{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Title, err = reader.ReadString(); err != nil { - return nil, err - } - if msg.ViewName, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + if msg.Visible, err = reader.ReadBoolean(); err != nil { + return nil, err + } + return msg, err } func DecodeIOSClickEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSClickEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSClickEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.X, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Y, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSInputEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSInputEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSInputEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ValueMasked, err = reader.ReadBoolean(); err != nil { - return nil, err - } + return nil, err + } if msg.Label, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSPerformanceEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSPerformanceEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSPerformanceEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Name, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Value, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSLog(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSLog{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSLog{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Severity, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Content, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSInternalError(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSInternalError{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSInternalError{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.Content, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSNetworkCall(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSNetworkCall{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSNetworkCall{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Length, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Duration, err = reader.ReadUint(); err != nil { - return nil, err - } - if msg.Headers, err = reader.ReadString(); err != nil { - return nil, err - } - if msg.Body, err = reader.ReadString(); err != nil { - return nil, err - } - if msg.URL, err = reader.ReadString(); err != nil { - return nil, err - } - if msg.Success, err = reader.ReadBoolean(); err != nil { - return nil, err - } + return nil, err + } + if msg.Type, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Method, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } + if msg.URL, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Request, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Response, err = reader.ReadString(); err != nil { + return nil, err + } if msg.Status, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + if msg.Duration, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err +} + +func DecodeIOSSwipeEvent(reader BytesReader) (Message, error) { + var err error = nil + msg := &IOSSwipeEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Length, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Label, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.X, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Y, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Direction, err = reader.ReadString(); err != nil { + return nil, err + } + return msg, err +} + +func DecodeIOSBatchMeta(reader BytesReader) (Message, error) { + var err error = nil + msg := &IOSBatchMeta{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.Length, err = reader.ReadUint(); err != nil { + return nil, err + } + if msg.FirstIndex, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err } func DecodeIOSPerformanceAggregated(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSPerformanceAggregated{} - if msg.TimestampStart, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSPerformanceAggregated{} + if msg.TimestampStart, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.TimestampEnd, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxFPS, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxCPU, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinMemory, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgMemory, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxMemory, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MinBattery, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.AvgBattery, err = reader.ReadUint(); err != nil { - return nil, err - } + return nil, err + } if msg.MaxBattery, err = reader.ReadUint(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func DecodeIOSIssueEvent(reader BytesReader) (Message, error) { - var err error = nil - msg := &IOSIssueEvent{} - if msg.Timestamp, err = reader.ReadUint(); err != nil { - return nil, err - } + var err error = nil + msg := &IOSIssueEvent{} + if msg.Timestamp, err = reader.ReadUint(); err != nil { + return nil, err + } if msg.Type, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.ContextString, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Context, err = reader.ReadString(); err != nil { - return nil, err - } + return nil, err + } if msg.Payload, err = reader.ReadString(); err != nil { - return nil, err - } - return msg, err + return nil, err + } + return msg, err } func ReadMessage(t uint64, reader BytesReader) (Message, error) { @@ -1955,8 +1964,6 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { return DecodeSessionEnd(reader) case 127: return DecodeSessionSearch(reader) - case 107: - return DecodeIOSBatchMeta(reader) case 90: return DecodeIOSSessionStart(reader) case 91: @@ -1974,9 +1981,7 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { case 97: return DecodeIOSCrash(reader) case 98: - return DecodeIOSScreenEnter(reader) - case 99: - return DecodeIOSScreenLeave(reader) + return DecodeIOSViewComponentEvent(reader) case 100: return DecodeIOSClickEvent(reader) case 101: @@ -1989,6 +1994,10 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { return DecodeIOSInternalError(reader) case 105: return DecodeIOSNetworkCall(reader) + case 106: + return DecodeIOSSwipeEvent(reader) + case 107: + return DecodeIOSBatchMeta(reader) case 110: return DecodeIOSPerformanceAggregated(reader) case 111: diff --git a/backend/pkg/metrics/imagestorage/metrics.go b/backend/pkg/metrics/imagestorage/metrics.go new file mode 100644 index 000000000..7ab2864ed --- /dev/null +++ b/backend/pkg/metrics/imagestorage/metrics.go @@ -0,0 +1,155 @@ +package imagestorage + +import ( + "github.com/prometheus/client_golang/prometheus" + "openreplay/backend/pkg/metrics/common" +) + +var storageSessionSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "session_size_bytes", + Help: "A histogram displaying the size of each session file in bytes prior to any manipulation.", + Buckets: common.DefaultSizeBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionSize(fileSize float64, fileType string) { + storageSessionSize.WithLabelValues(fileType).Observe(fileSize) +} + +var storageTotalSessions = prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: "storage", + Name: "sessions_total", + Help: "A counter displaying the total number of all processed sessions.", + }, +) + +func IncreaseStorageTotalSessions() { + storageTotalSessions.Inc() +} + +var storageSkippedSessionSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "session_size_bytes", + Help: "A histogram displaying the size of each skipped session file in bytes.", + Buckets: common.DefaultSizeBuckets, + }, + []string{"file_type"}, +) + +func RecordSkippedSessionSize(fileSize float64, fileType string) { + storageSkippedSessionSize.WithLabelValues(fileType).Observe(fileSize) +} + +var storageTotalSkippedSessions = prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: "storage", + Name: "sessions_skipped_total", + Help: "A counter displaying the total number of all skipped sessions because of the size limits.", + }, +) + +func IncreaseStorageTotalSkippedSessions() { + storageTotalSkippedSessions.Inc() +} + +var storageSessionReadDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "read_duration_seconds", + Help: "A histogram displaying the duration of reading for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionReadDuration(durMillis float64, fileType string) { + storageSessionReadDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionSortDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "sort_duration_seconds", + Help: "A histogram displaying the duration of sorting for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionSortDuration(durMillis float64, fileType string) { + storageSessionSortDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionEncryptionDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "encryption_duration_seconds", + Help: "A histogram displaying the duration of encoding for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionEncryptionDuration(durMillis float64, fileType string) { + storageSessionEncryptionDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionCompressDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "compress_duration_seconds", + Help: "A histogram displaying the duration of compressing for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionCompressDuration(durMillis float64, fileType string) { + storageSessionCompressDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionUploadDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "upload_duration_seconds", + Help: "A histogram displaying the duration of uploading to s3 for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionUploadDuration(durMillis float64, fileType string) { + storageSessionUploadDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionCompressionRatio = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "compression_ratio", + Help: "A histogram displaying the compression ratio of mob files for each session.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionCompressionRatio(ratio float64, fileType string) { + storageSessionCompressionRatio.WithLabelValues(fileType).Observe(ratio) +} + +func List() []prometheus.Collector { + return []prometheus.Collector{ + storageSessionSize, + storageTotalSessions, + storageSessionReadDuration, + storageSessionSortDuration, + storageSessionEncryptionDuration, + storageSessionCompressDuration, + storageSessionUploadDuration, + storageSessionCompressionRatio, + } +} diff --git a/backend/pkg/metrics/videostorage/metrics.go b/backend/pkg/metrics/videostorage/metrics.go new file mode 100644 index 000000000..6991e01c5 --- /dev/null +++ b/backend/pkg/metrics/videostorage/metrics.go @@ -0,0 +1,155 @@ +package videostorage + +import ( + "github.com/prometheus/client_golang/prometheus" + "openreplay/backend/pkg/metrics/common" +) + +var storageSessionSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "session_size_bytes", + Help: "A histogram displaying the size of each session file in bytes prior to any manipulation.", + Buckets: common.DefaultSizeBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionSize(fileSize float64, fileType string) { + storageSessionSize.WithLabelValues(fileType).Observe(fileSize) +} + +var storageTotalSessions = prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: "storage", + Name: "sessions_total", + Help: "A counter displaying the total number of all processed sessions.", + }, +) + +func IncreaseStorageTotalSessions() { + storageTotalSessions.Inc() +} + +var storageSkippedSessionSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "session_size_bytes", + Help: "A histogram displaying the size of each skipped session file in bytes.", + Buckets: common.DefaultSizeBuckets, + }, + []string{"file_type"}, +) + +func RecordSkippedSessionSize(fileSize float64, fileType string) { + storageSkippedSessionSize.WithLabelValues(fileType).Observe(fileSize) +} + +var storageTotalSkippedSessions = prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: "storage", + Name: "sessions_skipped_total", + Help: "A counter displaying the total number of all skipped sessions because of the size limits.", + }, +) + +func IncreaseStorageTotalSkippedSessions() { + storageTotalSkippedSessions.Inc() +} + +var storageSessionReadDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "read_duration_seconds", + Help: "A histogram displaying the duration of reading for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionReadDuration(durMillis float64, fileType string) { + storageSessionReadDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionSortDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "sort_duration_seconds", + Help: "A histogram displaying the duration of sorting for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionSortDuration(durMillis float64, fileType string) { + storageSessionSortDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionEncryptionDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "encryption_duration_seconds", + Help: "A histogram displaying the duration of encoding for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionEncryptionDuration(durMillis float64, fileType string) { + storageSessionEncryptionDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionCompressDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "compress_duration_seconds", + Help: "A histogram displaying the duration of compressing for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionCompressDuration(durMillis float64, fileType string) { + storageSessionCompressDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionUploadDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "upload_duration_seconds", + Help: "A histogram displaying the duration of uploading to s3 for each session in seconds.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionUploadDuration(durMillis float64, fileType string) { + storageSessionUploadDuration.WithLabelValues(fileType).Observe(durMillis / 1000.0) +} + +var storageSessionCompressionRatio = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "storage", + Name: "compression_ratio", + Help: "A histogram displaying the compression ratio of mob files for each session.", + Buckets: common.DefaultDurationBuckets, + }, + []string{"file_type"}, +) + +func RecordSessionCompressionRatio(ratio float64, fileType string) { + storageSessionCompressionRatio.WithLabelValues(fileType).Observe(ratio) +} + +func List() []prometheus.Collector { + return []prometheus.Collector{ + storageSessionSize, + storageTotalSessions, + storageSessionReadDuration, + storageSessionSortDuration, + storageSessionEncryptionDuration, + storageSessionCompressDuration, + storageSessionUploadDuration, + storageSessionCompressionRatio, + } +} diff --git a/backend/pkg/objectstorage/objectstorage.go b/backend/pkg/objectstorage/objectstorage.go index 2504ba02c..79ac2b9fe 100644 --- a/backend/pkg/objectstorage/objectstorage.go +++ b/backend/pkg/objectstorage/objectstorage.go @@ -16,6 +16,7 @@ const ( type ObjectStorage interface { Upload(reader io.Reader, key string, contentType string, compression CompressionType) error Get(key string) (io.ReadCloser, error) + GetAll(key string) ([]io.ReadCloser, error) Exists(key string) bool GetCreationTime(key string) *time.Time } diff --git a/backend/pkg/objectstorage/s3/s3.go b/backend/pkg/objectstorage/s3/s3.go index b1e8b2b9e..27e68698d 100644 --- a/backend/pkg/objectstorage/s3/s3.go +++ b/backend/pkg/objectstorage/s3/s3.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "fmt" "io" + "log" "net/http" "net/url" "os" @@ -102,6 +103,47 @@ func (s *storageImpl) Get(key string) (io.ReadCloser, error) { return out.Body, nil } +func (s *storageImpl) GetAll(key string) ([]io.ReadCloser, error) { + out, err := s.svc.GetObject(&s3.GetObjectInput{ + Bucket: s.bucket, + Key: &key, + }) + if err != nil { + return nil, err + } + return []io.ReadCloser{out.Body}, nil +} + +func downloadS3Files(bucket, prefix string) { + sess := _session.Must(_session.NewSession(&aws.Config{ + Region: aws.String("us-west-1"), // Change this to your region + })) + svc := s3.New(sess) + + resp, err := svc.ListObjects(&s3.ListObjectsInput{Bucket: &bucket, Prefix: &prefix}) + if err != nil { + log.Fatal(err) + } + + for _, item := range resp.Contents { + file, err := os.Create(*item.Key) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + downloader := s3manager.NewDownloader(sess) + _, err = downloader.Download(file, + &s3.GetObjectInput{ + Bucket: &bucket, + Key: item.Key, + }) + if err != nil { + log.Fatal(err) + } + } +} + func (s *storageImpl) Exists(key string) bool { _, err := s.svc.HeadObject(&s3.HeadObjectInput{ Bucket: s.bucket, diff --git a/backend/pkg/projects/model.go b/backend/pkg/projects/model.go index 2e64de0ca..4b238e01d 100644 --- a/backend/pkg/projects/model.go +++ b/backend/pkg/projects/model.go @@ -9,6 +9,7 @@ type Project struct { SampleRate byte SaveRequestPayloads bool BeaconSize int64 + Platform string Metadata1 *string Metadata2 *string Metadata3 *string @@ -58,3 +59,11 @@ func (p *Project) GetMetadataNo(key string) uint { } return 0 } + +func (p *Project) IsMobile() bool { + return p.Platform == "ios" || p.Platform == "android" +} + +func (p *Project) IsWeb() bool { + return p.Platform == "web" +} diff --git a/backend/pkg/projects/storage.go b/backend/pkg/projects/storage.go index a4d8d9bcc..80ec74983 100644 --- a/backend/pkg/projects/storage.go +++ b/backend/pkg/projects/storage.go @@ -3,14 +3,14 @@ package projects func (c *projectsImpl) getProjectByKey(projectKey string) (*Project, error) { p := &Project{ProjectKey: projectKey} if err := c.db.QueryRow(` - SELECT project_id, max_session_duration, save_request_payloads, sample_rate, beacon_size, + SELECT project_id, max_session_duration, save_request_payloads, sample_rate, beacon_size, platform, metadata_1, metadata_2, metadata_3, metadata_4, metadata_5, metadata_6, metadata_7, metadata_8, metadata_9, metadata_10 FROM projects WHERE project_key=$1 AND active = true `, projectKey, - ).Scan(&p.ProjectID, &p.MaxSessionDuration, &p.SaveRequestPayloads, &p.SampleRate, &p.BeaconSize, + ).Scan(&p.ProjectID, &p.MaxSessionDuration, &p.SaveRequestPayloads, &p.SampleRate, &p.BeaconSize, &p.Platform, &p.Metadata1, &p.Metadata2, &p.Metadata3, &p.Metadata4, &p.Metadata5, &p.Metadata6, &p.Metadata7, &p.Metadata8, &p.Metadata9, &p.Metadata10); err != nil { return nil, err @@ -21,14 +21,14 @@ func (c *projectsImpl) getProjectByKey(projectKey string) (*Project, error) { func (c *projectsImpl) getProject(projectID uint32) (*Project, error) { p := &Project{ProjectID: projectID} if err := c.db.QueryRow(` - SELECT project_key, max_session_duration, save_request_payloads, sample_rate, beacon_size, + SELECT project_key, max_session_duration, save_request_payloads, sample_rate, beacon_size, platform, metadata_1, metadata_2, metadata_3, metadata_4, metadata_5, metadata_6, metadata_7, metadata_8, metadata_9, metadata_10 FROM projects WHERE project_id=$1 AND active = true `, projectID, - ).Scan(&p.ProjectKey, &p.MaxSessionDuration, &p.SaveRequestPayloads, &p.SampleRate, &p.BeaconSize, + ).Scan(&p.ProjectKey, &p.MaxSessionDuration, &p.SaveRequestPayloads, &p.SampleRate, &p.BeaconSize, &p.Platform, &p.Metadata1, &p.Metadata2, &p.Metadata3, &p.Metadata4, &p.Metadata5, &p.Metadata6, &p.Metadata7, &p.Metadata8, &p.Metadata9, &p.Metadata10); err != nil { return nil, err diff --git a/backend/pkg/sessions/storage.go b/backend/pkg/sessions/storage.go index a9d015a34..88266dd79 100644 --- a/backend/pkg/sessions/storage.go +++ b/backend/pkg/sessions/storage.go @@ -93,7 +93,7 @@ func (s *storageImpl) AddUnStarted(sess *UnStartedSession) error { func (s *storageImpl) Get(sessionID uint64) (*Session, error) { sess := &Session{SessionID: sessionID} - var revID, userOSVersion, userBrowserVersion, userState, userCity *string + var revID, userOSVersion, userBrowser, userBrowserVersion, userState, userCity *string var issueTypes pgtype.EnumArray if err := s.db.QueryRow(` SELECT platform, @@ -117,7 +117,7 @@ func (s *storageImpl) Get(sessionID uint64) (*Session, error) { &revID, &sess.TrackerVersion, &sess.UserID, &sess.UserAnonymousID, &sess.Referrer, &sess.PagesCount, &sess.EventsCount, &sess.ErrorsCount, &issueTypes, - &sess.UserBrowser, &userBrowserVersion, &sess.IssueScore, + &userBrowser, &userBrowserVersion, &sess.IssueScore, &sess.Metadata1, &sess.Metadata2, &sess.Metadata3, &sess.Metadata4, &sess.Metadata5, &sess.Metadata6, &sess.Metadata7, &sess.Metadata8, &sess.Metadata9, &sess.Metadata10); err != nil { return nil, err @@ -125,6 +125,9 @@ func (s *storageImpl) Get(sessionID uint64) (*Session, error) { if userOSVersion != nil { sess.UserOSVersion = *userOSVersion } + if userBrowser != nil { + sess.UserBrowser = *userBrowser + } if userBrowserVersion != nil { sess.UserBrowserVersion = *userBrowserVersion } diff --git a/ee/backend/pkg/objectstorage/azure/azure.go b/ee/backend/pkg/objectstorage/azure/azure.go index bbe9adb9d..3dda73c92 100644 --- a/ee/backend/pkg/objectstorage/azure/azure.go +++ b/ee/backend/pkg/objectstorage/azure/azure.go @@ -3,6 +3,7 @@ package azure import ( "bytes" "context" + "errors" "fmt" "io" "log" @@ -87,6 +88,10 @@ func (s *storageImpl) Get(key string) (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(downloadedData.Bytes())), err } +func (s *storageImpl) GetAll(key string) ([]io.ReadCloser, error) { + return nil, errors.New("not implemented") +} + func (s *storageImpl) Exists(key string) bool { ctx := context.Background() get, err := s.client.DownloadStream(ctx, s.container, key, nil) diff --git a/ee/connectors/msgcodec/messages.py b/ee/connectors/msgcodec/messages.py index 7e2f28152..df3eb83d9 100644 --- a/ee/connectors/msgcodec/messages.py +++ b/ee/connectors/msgcodec/messages.py @@ -71,7 +71,7 @@ class CreateDocument(Message): __id__ = 7 def __init__(self, ): - pass + class CreateElementNode(Message): @@ -759,6 +759,20 @@ class ResourceTiming(Message): self.cached = cached +class TabChange(Message): + __id__ = 117 + + def __init__(self, tab_id): + self.tab_id = tab_id + + +class TabData(Message): + __id__ = 118 + + def __init__(self, tab_id): + self.tab_id = tab_id + + class IssueEvent(Message): __id__ = 125 @@ -788,15 +802,6 @@ class SessionSearch(Message): self.partition = partition -class IOSBatchMeta(Message): - __id__ = 107 - - def __init__(self, timestamp, length, first_index): - self.timestamp = timestamp - self.length = length - self.first_index = first_index - - class IOSSessionStart(Message): __id__ = 90 @@ -881,24 +886,15 @@ class IOSCrash(Message): self.stacktrace = stacktrace -class IOSScreenEnter(Message): +class IOSViewComponentEvent(Message): __id__ = 98 - def __init__(self, timestamp, length, title, view_name): + def __init__(self, timestamp, length, screen_name, view_name, visible): self.timestamp = timestamp self.length = length - self.title = title - self.view_name = view_name - - -class IOSScreenLeave(Message): - __id__ = 99 - - def __init__(self, timestamp, length, title, view_name): - self.timestamp = timestamp - self.length = length - self.title = title + self.screen_name = screen_name self.view_name = view_name + self.visible = visible class IOSClickEvent(Message): @@ -955,16 +951,37 @@ class IOSInternalError(Message): class IOSNetworkCall(Message): __id__ = 105 - def __init__(self, timestamp, length, duration, headers, body, url, success, method, status): + def __init__(self, timestamp, length, type, method, url, request, response, status, duration): self.timestamp = timestamp self.length = length - self.duration = duration - self.headers = headers - self.body = body - self.url = url - self.success = success + self.type = type self.method = method + self.url = url + self.request = request + self.response = response self.status = status + self.duration = duration + + +class IOSSwipeEvent(Message): + __id__ = 106 + + def __init__(self, timestamp, length, label, x, y, direction): + self.timestamp = timestamp + self.length = length + self.label = label + self.x = x + self.y = y + self.direction = direction + + +class IOSBatchMeta(Message): + __id__ = 107 + + def __init__(self, timestamp, length, first_index): + self.timestamp = timestamp + self.length = length + self.first_index = first_index class IOSPerformanceAggregated(Message): diff --git a/ee/connectors/msgcodec/messages.pyx b/ee/connectors/msgcodec/messages.pyx index b73479dba..35299e7ad 100644 --- a/ee/connectors/msgcodec/messages.pyx +++ b/ee/connectors/msgcodec/messages.pyx @@ -1121,6 +1121,24 @@ cdef class ResourceTiming(PyMessage): self.cached = cached +cdef class TabChange(PyMessage): + cdef public int __id__ + cdef public str tab_id + + def __init__(self, str tab_id): + self.__id__ = 117 + self.tab_id = tab_id + + +cdef class TabData(PyMessage): + cdef public int __id__ + cdef public str tab_id + + def __init__(self, str tab_id): + self.__id__ = 118 + self.tab_id = tab_id + + cdef class IssueEvent(PyMessage): cdef public int __id__ cdef public unsigned long message_id @@ -1164,19 +1182,6 @@ cdef class SessionSearch(PyMessage): self.partition = partition -cdef class IOSBatchMeta(PyMessage): - cdef public int __id__ - cdef public unsigned long timestamp - cdef public unsigned long length - cdef public unsigned long first_index - - def __init__(self, unsigned long timestamp, unsigned long length, unsigned long first_index): - self.__id__ = 107 - self.timestamp = timestamp - self.length = length - self.first_index = first_index - - cdef class IOSSessionStart(PyMessage): cdef public int __id__ cdef public unsigned long timestamp @@ -1305,34 +1310,21 @@ cdef class IOSCrash(PyMessage): self.stacktrace = stacktrace -cdef class IOSScreenEnter(PyMessage): +cdef class IOSViewComponentEvent(PyMessage): cdef public int __id__ cdef public unsigned long timestamp cdef public unsigned long length - cdef public str title + cdef public str screen_name cdef public str view_name + cdef public bint visible - def __init__(self, unsigned long timestamp, unsigned long length, str title, str view_name): + def __init__(self, unsigned long timestamp, unsigned long length, str screen_name, str view_name, bint visible): self.__id__ = 98 self.timestamp = timestamp self.length = length - self.title = title - self.view_name = view_name - - -cdef class IOSScreenLeave(PyMessage): - cdef public int __id__ - cdef public unsigned long timestamp - cdef public unsigned long length - cdef public str title - cdef public str view_name - - def __init__(self, unsigned long timestamp, unsigned long length, str title, str view_name): - self.__id__ = 99 - self.timestamp = timestamp - self.length = length - self.title = title + self.screen_name = screen_name self.view_name = view_name + self.visible = visible cdef class IOSClickEvent(PyMessage): @@ -1416,25 +1408,57 @@ cdef class IOSNetworkCall(PyMessage): cdef public int __id__ cdef public unsigned long timestamp cdef public unsigned long length - cdef public unsigned long duration - cdef public str headers - cdef public str body - cdef public str url - cdef public bint success + cdef public str type cdef public str method + cdef public str url + cdef public str request + cdef public str response cdef public unsigned long status + cdef public unsigned long duration - def __init__(self, unsigned long timestamp, unsigned long length, unsigned long duration, str headers, str body, str url, bint success, str method, unsigned long status): + def __init__(self, unsigned long timestamp, unsigned long length, str type, str method, str url, str request, str response, unsigned long status, unsigned long duration): self.__id__ = 105 self.timestamp = timestamp self.length = length - self.duration = duration - self.headers = headers - self.body = body - self.url = url - self.success = success + self.type = type self.method = method + self.url = url + self.request = request + self.response = response self.status = status + self.duration = duration + + +cdef class IOSSwipeEvent(PyMessage): + cdef public int __id__ + cdef public unsigned long timestamp + cdef public unsigned long length + cdef public str label + cdef public unsigned long x + cdef public unsigned long y + cdef public str direction + + def __init__(self, unsigned long timestamp, unsigned long length, str label, unsigned long x, unsigned long y, str direction): + self.__id__ = 106 + self.timestamp = timestamp + self.length = length + self.label = label + self.x = x + self.y = y + self.direction = direction + + +cdef class IOSBatchMeta(PyMessage): + cdef public int __id__ + cdef public unsigned long timestamp + cdef public unsigned long length + cdef public unsigned long first_index + + def __init__(self, unsigned long timestamp, unsigned long length, unsigned long first_index): + self.__id__ = 107 + self.timestamp = timestamp + self.length = length + self.first_index = first_index cdef class IOSPerformanceAggregated(PyMessage): diff --git a/ee/connectors/msgcodec/msgcodec.py b/ee/connectors/msgcodec/msgcodec.py index 8da9bd7f5..4785bea53 100644 --- a/ee/connectors/msgcodec/msgcodec.py +++ b/ee/connectors/msgcodec/msgcodec.py @@ -43,8 +43,7 @@ class MessageCodec(Codec): try: decoded = int.from_bytes(b, "little", signed=False) except Exception as e: - print(f"Error while decoding message key (SessionID) from {b}\n{e}") - raise e + raise UnicodeDecodeError(f"Error while decoding message key (SessionID) from {b}\n{e}") return decoded def decode_detailed(self, b: bytes) -> List[Message]: @@ -722,13 +721,6 @@ class MessageCodec(Codec): partition=self.read_uint(reader) ) - if message_id == 107: - return IOSBatchMeta( - timestamp=self.read_uint(reader), - length=self.read_uint(reader), - first_index=self.read_uint(reader) - ) - if message_id == 90: return IOSSessionStart( timestamp=self.read_uint(reader), @@ -798,19 +790,12 @@ class MessageCodec(Codec): ) if message_id == 98: - return IOSScreenEnter( + return IOSViewComponentEvent( timestamp=self.read_uint(reader), length=self.read_uint(reader), - title=self.read_string(reader), - view_name=self.read_string(reader) - ) - - if message_id == 99: - return IOSScreenLeave( - timestamp=self.read_uint(reader), - length=self.read_uint(reader), - title=self.read_string(reader), - view_name=self.read_string(reader) + screen_name=self.read_string(reader), + view_name=self.read_string(reader), + visible=self.read_boolean(reader) ) if message_id == 100: @@ -858,13 +843,30 @@ class MessageCodec(Codec): return IOSNetworkCall( timestamp=self.read_uint(reader), length=self.read_uint(reader), - duration=self.read_uint(reader), - headers=self.read_string(reader), - body=self.read_string(reader), - url=self.read_string(reader), - success=self.read_boolean(reader), + type=self.read_string(reader), method=self.read_string(reader), - status=self.read_uint(reader) + url=self.read_string(reader), + request=self.read_string(reader), + response=self.read_string(reader), + status=self.read_uint(reader), + duration=self.read_uint(reader) + ) + + if message_id == 106: + return IOSSwipeEvent( + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + label=self.read_string(reader), + x=self.read_uint(reader), + y=self.read_uint(reader), + direction=self.read_string(reader) + ) + + if message_id == 107: + return IOSBatchMeta( + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + first_index=self.read_uint(reader) ) if message_id == 110: diff --git a/ee/connectors/msgcodec/msgcodec.pyx b/ee/connectors/msgcodec/msgcodec.pyx index bf44236ee..f310c2dba 100644 --- a/ee/connectors/msgcodec/msgcodec.pyx +++ b/ee/connectors/msgcodec/msgcodec.pyx @@ -3,7 +3,6 @@ from messages import * #from io cimport BytesIO from io import BytesIO -import copy from libc.stdlib cimport abort cdef extern from "Python.h": @@ -14,7 +13,6 @@ cdef class PyMsg: pass ctypedef object PyBytesIO -cdef unsigned long c_message_id cdef class MessageCodec: """ @@ -146,14 +144,13 @@ cdef class MessageCodec: return decoded def decode_detailed(self, bytes b): - global c_message_id cdef PyBytesIO reader = BytesIO(b) cdef list messages_list cdef int mode try: messages_list = [self.handler(reader, 0)] except IndexError: - print(f'[WARN] Broken batch') + print('[WARN] Broken batch') return list() if isinstance(messages_list[0], BatchMeta): # Old BatchMeta @@ -165,7 +162,6 @@ cdef class MessageCodec: else: mode = 1 else: - # print(f'{messages_list[0].__id__}') return messages_list while True: try: @@ -177,77 +173,71 @@ cdef class MessageCodec: return messages_list def handler(self, PyBytesIO reader, int mode = 0): - global c_message_id cdef unsigned long message_id = MessageCodec.read_message_id(reader) - c_message_id = message_id cdef int r_size - try: - if mode == 1: - # We read the three bytes representing the length of message. It can be used to skip unwanted messages - r_size = MessageCodec.read_size(reader) - if message_id not in self.msg_selector: - reader.read(r_size) - return None - return MessageCodec.read_head_message(reader, message_id) - elif mode == 0: - # Old format with no bytes for message length - return MessageCodec.read_head_message(reader, message_id) - else: - raise IOError() - except Exception as e: - print(f'[Error-inside] Broken message id {message_id}') - return None + if mode == 1: + # We read the three bytes representing the length of message. It can be used to skip unwanted messages + r_size = MessageCodec.read_size(reader) + if message_id not in self.msg_selector: + reader.read(r_size) + return None + return MessageCodec.read_head_message(reader, message_id) + elif mode == 0: + # Old format with no bytes for message length + return MessageCodec.read_head_message(reader, message_id) + else: + raise IOError() @staticmethod def read_head_message(PyBytesIO reader, unsigned long message_id): if message_id == 0: return Timestamp( - timestamp=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader) ) if message_id == 1: return SessionStart( - timestamp=MessageCodec.read_uint(reader), - project_id=MessageCodec.read_uint(reader), - tracker_version=MessageCodec.read_string(reader), - rev_id=MessageCodec.read_string(reader), - user_uuid=MessageCodec.read_string(reader), - user_agent=MessageCodec.read_string(reader), - user_os=MessageCodec.read_string(reader), - user_os_version=MessageCodec.read_string(reader), - user_browser=MessageCodec.read_string(reader), - user_browser_version=MessageCodec.read_string(reader), - user_device=MessageCodec.read_string(reader), - user_device_type=MessageCodec.read_string(reader), - user_device_memory_size=MessageCodec.read_uint(reader), - user_device_heap_size=MessageCodec.read_uint(reader), - user_country=MessageCodec.read_string(reader), - user_id=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + project_id=self.read_uint(reader), + tracker_version=self.read_string(reader), + rev_id=self.read_string(reader), + user_uuid=self.read_string(reader), + user_agent=self.read_string(reader), + user_os=self.read_string(reader), + user_os_version=self.read_string(reader), + user_browser=self.read_string(reader), + user_browser_version=self.read_string(reader), + user_device=self.read_string(reader), + user_device_type=self.read_string(reader), + user_device_memory_size=self.read_uint(reader), + user_device_heap_size=self.read_uint(reader), + user_country=self.read_string(reader), + user_id=self.read_string(reader) ) if message_id == 3: return SessionEndDeprecated( - timestamp=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader) ) if message_id == 4: return SetPageLocation( - url=MessageCodec.read_string(reader), - referrer=MessageCodec.read_string(reader), - navigation_start=MessageCodec.read_uint(reader) + url=self.read_string(reader), + referrer=self.read_string(reader), + navigation_start=self.read_uint(reader) ) if message_id == 5: return SetViewportSize( - width=MessageCodec.read_uint(reader), - height=MessageCodec.read_uint(reader) + width=self.read_uint(reader), + height=self.read_uint(reader) ) if message_id == 6: return SetViewportScroll( - x=MessageCodec.read_int(reader), - y=MessageCodec.read_int(reader) + x=self.read_int(reader), + y=self.read_int(reader) ) if message_id == 7: @@ -257,737 +247,750 @@ cdef class MessageCodec: if message_id == 8: return CreateElementNode( - id=MessageCodec.read_uint(reader), - parent_id=MessageCodec.read_uint(reader), - index=MessageCodec.read_uint(reader), - tag=MessageCodec.read_string(reader), - svg=MessageCodec.read_boolean(reader) + id=self.read_uint(reader), + parent_id=self.read_uint(reader), + index=self.read_uint(reader), + tag=self.read_string(reader), + svg=self.read_boolean(reader) ) if message_id == 9: return CreateTextNode( - id=MessageCodec.read_uint(reader), - parent_id=MessageCodec.read_uint(reader), - index=MessageCodec.read_uint(reader) + id=self.read_uint(reader), + parent_id=self.read_uint(reader), + index=self.read_uint(reader) ) if message_id == 10: return MoveNode( - id=MessageCodec.read_uint(reader), - parent_id=MessageCodec.read_uint(reader), - index=MessageCodec.read_uint(reader) + id=self.read_uint(reader), + parent_id=self.read_uint(reader), + index=self.read_uint(reader) ) if message_id == 11: return RemoveNode( - id=MessageCodec.read_uint(reader) + id=self.read_uint(reader) ) if message_id == 12: return SetNodeAttribute( - id=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + id=self.read_uint(reader), + name=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 13: return RemoveNodeAttribute( - id=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader) + id=self.read_uint(reader), + name=self.read_string(reader) ) if message_id == 14: return SetNodeData( - id=MessageCodec.read_uint(reader), - data=MessageCodec.read_string(reader) + id=self.read_uint(reader), + data=self.read_string(reader) ) if message_id == 15: return SetCSSData( - id=MessageCodec.read_uint(reader), - data=MessageCodec.read_string(reader) + id=self.read_uint(reader), + data=self.read_string(reader) ) if message_id == 16: return SetNodeScroll( - id=MessageCodec.read_uint(reader), - x=MessageCodec.read_int(reader), - y=MessageCodec.read_int(reader) + id=self.read_uint(reader), + x=self.read_int(reader), + y=self.read_int(reader) ) if message_id == 17: return SetInputTarget( - id=MessageCodec.read_uint(reader), - label=MessageCodec.read_string(reader) + id=self.read_uint(reader), + label=self.read_string(reader) ) if message_id == 18: return SetInputValue( - id=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader), - mask=MessageCodec.read_int(reader) + id=self.read_uint(reader), + value=self.read_string(reader), + mask=self.read_int(reader) ) if message_id == 19: return SetInputChecked( - id=MessageCodec.read_uint(reader), - checked=MessageCodec.read_boolean(reader) + id=self.read_uint(reader), + checked=self.read_boolean(reader) ) if message_id == 20: return MouseMove( - x=MessageCodec.read_uint(reader), - y=MessageCodec.read_uint(reader) + x=self.read_uint(reader), + y=self.read_uint(reader) ) if message_id == 21: return NetworkRequest( - type=MessageCodec.read_string(reader), - method=MessageCodec.read_string(reader), - url=MessageCodec.read_string(reader), - request=MessageCodec.read_string(reader), - response=MessageCodec.read_string(reader), - status=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader) + type=self.read_string(reader), + method=self.read_string(reader), + url=self.read_string(reader), + request=self.read_string(reader), + response=self.read_string(reader), + status=self.read_uint(reader), + timestamp=self.read_uint(reader), + duration=self.read_uint(reader) ) if message_id == 22: return ConsoleLog( - level=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + level=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 23: return PageLoadTiming( - request_start=MessageCodec.read_uint(reader), - response_start=MessageCodec.read_uint(reader), - response_end=MessageCodec.read_uint(reader), - dom_content_loaded_event_start=MessageCodec.read_uint(reader), - dom_content_loaded_event_end=MessageCodec.read_uint(reader), - load_event_start=MessageCodec.read_uint(reader), - load_event_end=MessageCodec.read_uint(reader), - first_paint=MessageCodec.read_uint(reader), - first_contentful_paint=MessageCodec.read_uint(reader) + request_start=self.read_uint(reader), + response_start=self.read_uint(reader), + response_end=self.read_uint(reader), + dom_content_loaded_event_start=self.read_uint(reader), + dom_content_loaded_event_end=self.read_uint(reader), + load_event_start=self.read_uint(reader), + load_event_end=self.read_uint(reader), + first_paint=self.read_uint(reader), + first_contentful_paint=self.read_uint(reader) ) if message_id == 24: return PageRenderTiming( - speed_index=MessageCodec.read_uint(reader), - visually_complete=MessageCodec.read_uint(reader), - time_to_interactive=MessageCodec.read_uint(reader) + speed_index=self.read_uint(reader), + visually_complete=self.read_uint(reader), + time_to_interactive=self.read_uint(reader) ) if message_id == 25: return JSExceptionDeprecated( - name=MessageCodec.read_string(reader), - message=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + name=self.read_string(reader), + message=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 26: return IntegrationEvent( - timestamp=MessageCodec.read_uint(reader), - source=MessageCodec.read_string(reader), - name=MessageCodec.read_string(reader), - message=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + source=self.read_string(reader), + name=self.read_string(reader), + message=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 27: return CustomEvent( - name=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + name=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 28: return UserID( - id=MessageCodec.read_string(reader) + id=self.read_string(reader) ) if message_id == 29: return UserAnonymousID( - id=MessageCodec.read_string(reader) + id=self.read_string(reader) ) if message_id == 30: return Metadata( - key=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + key=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 31: return PageEvent( - message_id=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - url=MessageCodec.read_string(reader), - referrer=MessageCodec.read_string(reader), - loaded=MessageCodec.read_boolean(reader), - request_start=MessageCodec.read_uint(reader), - response_start=MessageCodec.read_uint(reader), - response_end=MessageCodec.read_uint(reader), - dom_content_loaded_event_start=MessageCodec.read_uint(reader), - dom_content_loaded_event_end=MessageCodec.read_uint(reader), - load_event_start=MessageCodec.read_uint(reader), - load_event_end=MessageCodec.read_uint(reader), - first_paint=MessageCodec.read_uint(reader), - first_contentful_paint=MessageCodec.read_uint(reader), - speed_index=MessageCodec.read_uint(reader), - visually_complete=MessageCodec.read_uint(reader), - time_to_interactive=MessageCodec.read_uint(reader) + message_id=self.read_uint(reader), + timestamp=self.read_uint(reader), + url=self.read_string(reader), + referrer=self.read_string(reader), + loaded=self.read_boolean(reader), + request_start=self.read_uint(reader), + response_start=self.read_uint(reader), + response_end=self.read_uint(reader), + dom_content_loaded_event_start=self.read_uint(reader), + dom_content_loaded_event_end=self.read_uint(reader), + load_event_start=self.read_uint(reader), + load_event_end=self.read_uint(reader), + first_paint=self.read_uint(reader), + first_contentful_paint=self.read_uint(reader), + speed_index=self.read_uint(reader), + visually_complete=self.read_uint(reader), + time_to_interactive=self.read_uint(reader) ) if message_id == 32: return InputEvent( - message_id=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader), - value_masked=MessageCodec.read_boolean(reader), - label=MessageCodec.read_string(reader) + message_id=self.read_uint(reader), + timestamp=self.read_uint(reader), + value=self.read_string(reader), + value_masked=self.read_boolean(reader), + label=self.read_string(reader) ) if message_id == 37: return CSSInsertRule( - id=MessageCodec.read_uint(reader), - rule=MessageCodec.read_string(reader), - index=MessageCodec.read_uint(reader) + id=self.read_uint(reader), + rule=self.read_string(reader), + index=self.read_uint(reader) ) if message_id == 38: return CSSDeleteRule( - id=MessageCodec.read_uint(reader), - index=MessageCodec.read_uint(reader) + id=self.read_uint(reader), + index=self.read_uint(reader) ) if message_id == 39: return Fetch( - method=MessageCodec.read_string(reader), - url=MessageCodec.read_string(reader), - request=MessageCodec.read_string(reader), - response=MessageCodec.read_string(reader), - status=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader) + method=self.read_string(reader), + url=self.read_string(reader), + request=self.read_string(reader), + response=self.read_string(reader), + status=self.read_uint(reader), + timestamp=self.read_uint(reader), + duration=self.read_uint(reader) ) if message_id == 40: return Profiler( - name=MessageCodec.read_string(reader), - duration=MessageCodec.read_uint(reader), - args=MessageCodec.read_string(reader), - result=MessageCodec.read_string(reader) + name=self.read_string(reader), + duration=self.read_uint(reader), + args=self.read_string(reader), + result=self.read_string(reader) ) if message_id == 41: return OTable( - key=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + key=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 42: return StateAction( - type=MessageCodec.read_string(reader) + type=self.read_string(reader) ) if message_id == 44: return Redux( - action=MessageCodec.read_string(reader), - state=MessageCodec.read_string(reader), - duration=MessageCodec.read_uint(reader) + action=self.read_string(reader), + state=self.read_string(reader), + duration=self.read_uint(reader) ) if message_id == 45: return Vuex( - mutation=MessageCodec.read_string(reader), - state=MessageCodec.read_string(reader) + mutation=self.read_string(reader), + state=self.read_string(reader) ) if message_id == 46: return MobX( - type=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + type=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 47: return NgRx( - action=MessageCodec.read_string(reader), - state=MessageCodec.read_string(reader), - duration=MessageCodec.read_uint(reader) + action=self.read_string(reader), + state=self.read_string(reader), + duration=self.read_uint(reader) ) if message_id == 48: return GraphQL( - operation_kind=MessageCodec.read_string(reader), - operation_name=MessageCodec.read_string(reader), - variables=MessageCodec.read_string(reader), - response=MessageCodec.read_string(reader) + operation_kind=self.read_string(reader), + operation_name=self.read_string(reader), + variables=self.read_string(reader), + response=self.read_string(reader) ) if message_id == 49: return PerformanceTrack( - frames=MessageCodec.read_int(reader), - ticks=MessageCodec.read_int(reader), - total_js_heap_size=MessageCodec.read_uint(reader), - used_js_heap_size=MessageCodec.read_uint(reader) + frames=self.read_int(reader), + ticks=self.read_int(reader), + total_js_heap_size=self.read_uint(reader), + used_js_heap_size=self.read_uint(reader) ) if message_id == 50: return StringDict( - key=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader) + key=self.read_uint(reader), + value=self.read_string(reader) ) if message_id == 51: return SetNodeAttributeDict( - id=MessageCodec.read_uint(reader), - name_key=MessageCodec.read_uint(reader), - value_key=MessageCodec.read_uint(reader) + id=self.read_uint(reader), + name_key=self.read_uint(reader), + value_key=self.read_uint(reader) ) if message_id == 53: return ResourceTimingDeprecated( - timestamp=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader), - ttfb=MessageCodec.read_uint(reader), - header_size=MessageCodec.read_uint(reader), - encoded_body_size=MessageCodec.read_uint(reader), - decoded_body_size=MessageCodec.read_uint(reader), - url=MessageCodec.read_string(reader), - initiator=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + duration=self.read_uint(reader), + ttfb=self.read_uint(reader), + header_size=self.read_uint(reader), + encoded_body_size=self.read_uint(reader), + decoded_body_size=self.read_uint(reader), + url=self.read_string(reader), + initiator=self.read_string(reader) ) if message_id == 54: return ConnectionInformation( - downlink=MessageCodec.read_uint(reader), - type=MessageCodec.read_string(reader) + downlink=self.read_uint(reader), + type=self.read_string(reader) ) if message_id == 55: return SetPageVisibility( - hidden=MessageCodec.read_boolean(reader) + hidden=self.read_boolean(reader) ) if message_id == 56: return PerformanceTrackAggr( - timestamp_start=MessageCodec.read_uint(reader), - timestamp_end=MessageCodec.read_uint(reader), - min_fps=MessageCodec.read_uint(reader), - avg_fps=MessageCodec.read_uint(reader), - max_fps=MessageCodec.read_uint(reader), - min_cpu=MessageCodec.read_uint(reader), - avg_cpu=MessageCodec.read_uint(reader), - max_cpu=MessageCodec.read_uint(reader), - min_total_js_heap_size=MessageCodec.read_uint(reader), - avg_total_js_heap_size=MessageCodec.read_uint(reader), - max_total_js_heap_size=MessageCodec.read_uint(reader), - min_used_js_heap_size=MessageCodec.read_uint(reader), - avg_used_js_heap_size=MessageCodec.read_uint(reader), - max_used_js_heap_size=MessageCodec.read_uint(reader) + timestamp_start=self.read_uint(reader), + timestamp_end=self.read_uint(reader), + min_fps=self.read_uint(reader), + avg_fps=self.read_uint(reader), + max_fps=self.read_uint(reader), + min_cpu=self.read_uint(reader), + avg_cpu=self.read_uint(reader), + max_cpu=self.read_uint(reader), + min_total_js_heap_size=self.read_uint(reader), + avg_total_js_heap_size=self.read_uint(reader), + max_total_js_heap_size=self.read_uint(reader), + min_used_js_heap_size=self.read_uint(reader), + avg_used_js_heap_size=self.read_uint(reader), + max_used_js_heap_size=self.read_uint(reader) ) if message_id == 57: return LoadFontFace( - parent_id=MessageCodec.read_uint(reader), - family=MessageCodec.read_string(reader), - source=MessageCodec.read_string(reader), - descriptors=MessageCodec.read_string(reader) + parent_id=self.read_uint(reader), + family=self.read_string(reader), + source=self.read_string(reader), + descriptors=self.read_string(reader) ) if message_id == 58: return SetNodeFocus( - id=MessageCodec.read_int(reader) + id=self.read_int(reader) ) if message_id == 59: return LongTask( - timestamp=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader), - context=MessageCodec.read_uint(reader), - container_type=MessageCodec.read_uint(reader), - container_src=MessageCodec.read_string(reader), - container_id=MessageCodec.read_string(reader), - container_name=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + duration=self.read_uint(reader), + context=self.read_uint(reader), + container_type=self.read_uint(reader), + container_src=self.read_string(reader), + container_id=self.read_string(reader), + container_name=self.read_string(reader) ) if message_id == 60: return SetNodeAttributeURLBased( - id=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader), - base_url=MessageCodec.read_string(reader) + id=self.read_uint(reader), + name=self.read_string(reader), + value=self.read_string(reader), + base_url=self.read_string(reader) ) if message_id == 61: return SetCSSDataURLBased( - id=MessageCodec.read_uint(reader), - data=MessageCodec.read_string(reader), - base_url=MessageCodec.read_string(reader) + id=self.read_uint(reader), + data=self.read_string(reader), + base_url=self.read_string(reader) ) if message_id == 62: return IssueEventDeprecated( - message_id=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - type=MessageCodec.read_string(reader), - context_string=MessageCodec.read_string(reader), - context=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + message_id=self.read_uint(reader), + timestamp=self.read_uint(reader), + type=self.read_string(reader), + context_string=self.read_string(reader), + context=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 63: return TechnicalInfo( - type=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + type=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 64: return CustomIssue( - name=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + name=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 66: return AssetCache( - url=MessageCodec.read_string(reader) + url=self.read_string(reader) ) if message_id == 67: return CSSInsertRuleURLBased( - id=MessageCodec.read_uint(reader), - rule=MessageCodec.read_string(reader), - index=MessageCodec.read_uint(reader), - base_url=MessageCodec.read_string(reader) + id=self.read_uint(reader), + rule=self.read_string(reader), + index=self.read_uint(reader), + base_url=self.read_string(reader) ) if message_id == 69: return MouseClick( - id=MessageCodec.read_uint(reader), - hesitation_time=MessageCodec.read_uint(reader), - label=MessageCodec.read_string(reader), - selector=MessageCodec.read_string(reader) + id=self.read_uint(reader), + hesitation_time=self.read_uint(reader), + label=self.read_string(reader), + selector=self.read_string(reader) ) if message_id == 70: return CreateIFrameDocument( - frame_id=MessageCodec.read_uint(reader), - id=MessageCodec.read_uint(reader) + frame_id=self.read_uint(reader), + id=self.read_uint(reader) ) if message_id == 71: return AdoptedSSReplaceURLBased( - sheet_id=MessageCodec.read_uint(reader), - text=MessageCodec.read_string(reader), - base_url=MessageCodec.read_string(reader) + sheet_id=self.read_uint(reader), + text=self.read_string(reader), + base_url=self.read_string(reader) ) if message_id == 72: return AdoptedSSReplace( - sheet_id=MessageCodec.read_uint(reader), - text=MessageCodec.read_string(reader) + sheet_id=self.read_uint(reader), + text=self.read_string(reader) ) if message_id == 73: return AdoptedSSInsertRuleURLBased( - sheet_id=MessageCodec.read_uint(reader), - rule=MessageCodec.read_string(reader), - index=MessageCodec.read_uint(reader), - base_url=MessageCodec.read_string(reader) + sheet_id=self.read_uint(reader), + rule=self.read_string(reader), + index=self.read_uint(reader), + base_url=self.read_string(reader) ) if message_id == 74: return AdoptedSSInsertRule( - sheet_id=MessageCodec.read_uint(reader), - rule=MessageCodec.read_string(reader), - index=MessageCodec.read_uint(reader) + sheet_id=self.read_uint(reader), + rule=self.read_string(reader), + index=self.read_uint(reader) ) if message_id == 75: return AdoptedSSDeleteRule( - sheet_id=MessageCodec.read_uint(reader), - index=MessageCodec.read_uint(reader) + sheet_id=self.read_uint(reader), + index=self.read_uint(reader) ) if message_id == 76: return AdoptedSSAddOwner( - sheet_id=MessageCodec.read_uint(reader), - id=MessageCodec.read_uint(reader) + sheet_id=self.read_uint(reader), + id=self.read_uint(reader) ) if message_id == 77: return AdoptedSSRemoveOwner( - sheet_id=MessageCodec.read_uint(reader), - id=MessageCodec.read_uint(reader) + sheet_id=self.read_uint(reader), + id=self.read_uint(reader) ) if message_id == 78: return JSException( - name=MessageCodec.read_string(reader), - message=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader), - metadata=MessageCodec.read_string(reader) + name=self.read_string(reader), + message=self.read_string(reader), + payload=self.read_string(reader), + metadata=self.read_string(reader) ) if message_id == 79: return Zustand( - mutation=MessageCodec.read_string(reader), - state=MessageCodec.read_string(reader) + mutation=self.read_string(reader), + state=self.read_string(reader) ) if message_id == 80: return BatchMeta( - page_no=MessageCodec.read_uint(reader), - first_index=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_int(reader) + page_no=self.read_uint(reader), + first_index=self.read_uint(reader), + timestamp=self.read_int(reader) ) if message_id == 81: return BatchMetadata( - version=MessageCodec.read_uint(reader), - page_no=MessageCodec.read_uint(reader), - first_index=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_int(reader), - location=MessageCodec.read_string(reader) + version=self.read_uint(reader), + page_no=self.read_uint(reader), + first_index=self.read_uint(reader), + timestamp=self.read_int(reader), + location=self.read_string(reader) ) if message_id == 82: return PartitionedMessage( - part_no=MessageCodec.read_uint(reader), - part_total=MessageCodec.read_uint(reader) + part_no=self.read_uint(reader), + part_total=self.read_uint(reader) ) if message_id == 112: return InputChange( - id=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader), - value_masked=MessageCodec.read_boolean(reader), - label=MessageCodec.read_string(reader), - hesitation_time=MessageCodec.read_int(reader), - input_duration=MessageCodec.read_int(reader) + id=self.read_uint(reader), + value=self.read_string(reader), + value_masked=self.read_boolean(reader), + label=self.read_string(reader), + hesitation_time=self.read_int(reader), + input_duration=self.read_int(reader) ) if message_id == 113: return SelectionChange( - selection_start=MessageCodec.read_uint(reader), - selection_end=MessageCodec.read_uint(reader), - selection=MessageCodec.read_string(reader) + selection_start=self.read_uint(reader), + selection_end=self.read_uint(reader), + selection=self.read_string(reader) ) if message_id == 114: return MouseThrashing( - timestamp=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader) ) if message_id == 115: return UnbindNodes( - total_removed_percent=MessageCodec.read_uint(reader) + total_removed_percent=self.read_uint(reader) ) if message_id == 116: return ResourceTiming( - timestamp=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader), - ttfb=MessageCodec.read_uint(reader), - header_size=MessageCodec.read_uint(reader), - encoded_body_size=MessageCodec.read_uint(reader), - decoded_body_size=MessageCodec.read_uint(reader), - url=MessageCodec.read_string(reader), - initiator=MessageCodec.read_string(reader), - transferred_size=MessageCodec.read_uint(reader), - cached=MessageCodec.read_boolean(reader) + timestamp=self.read_uint(reader), + duration=self.read_uint(reader), + ttfb=self.read_uint(reader), + header_size=self.read_uint(reader), + encoded_body_size=self.read_uint(reader), + decoded_body_size=self.read_uint(reader), + url=self.read_string(reader), + initiator=self.read_string(reader), + transferred_size=self.read_uint(reader), + cached=self.read_boolean(reader) + ) + + if message_id == 117: + return TabChange( + tab_id=self.read_string(reader) + ) + + if message_id == 118: + return TabData( + tab_id=self.read_string(reader) ) if message_id == 125: return IssueEvent( - message_id=MessageCodec.read_uint(reader), - timestamp=MessageCodec.read_uint(reader), - type=MessageCodec.read_string(reader), - context_string=MessageCodec.read_string(reader), - context=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader), - url=MessageCodec.read_string(reader) + message_id=self.read_uint(reader), + timestamp=self.read_uint(reader), + type=self.read_string(reader), + context_string=self.read_string(reader), + context=self.read_string(reader), + payload=self.read_string(reader), + url=self.read_string(reader) ) if message_id == 126: return SessionEnd( - timestamp=MessageCodec.read_uint(reader), - encryption_key=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + encryption_key=self.read_string(reader) ) if message_id == 127: return SessionSearch( - timestamp=MessageCodec.read_uint(reader), - partition=MessageCodec.read_uint(reader) - ) - - if message_id == 107: - return IOSBatchMeta( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - first_index=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader), + partition=self.read_uint(reader) ) if message_id == 90: return IOSSessionStart( - timestamp=MessageCodec.read_uint(reader), - project_id=MessageCodec.read_uint(reader), - tracker_version=MessageCodec.read_string(reader), - rev_id=MessageCodec.read_string(reader), - user_uuid=MessageCodec.read_string(reader), - user_os=MessageCodec.read_string(reader), - user_os_version=MessageCodec.read_string(reader), - user_device=MessageCodec.read_string(reader), - user_device_type=MessageCodec.read_string(reader), - user_country=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + project_id=self.read_uint(reader), + tracker_version=self.read_string(reader), + rev_id=self.read_string(reader), + user_uuid=self.read_string(reader), + user_os=self.read_string(reader), + user_os_version=self.read_string(reader), + user_device=self.read_string(reader), + user_device_type=self.read_string(reader), + user_country=self.read_string(reader) ) if message_id == 91: return IOSSessionEnd( - timestamp=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader) ) if message_id == 92: return IOSMetadata( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - key=MessageCodec.read_string(reader), - value=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + key=self.read_string(reader), + value=self.read_string(reader) ) if message_id == 93: return IOSCustomEvent( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + name=self.read_string(reader), + payload=self.read_string(reader) ) if message_id == 94: return IOSUserID( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + value=self.read_string(reader) ) if message_id == 95: return IOSUserAnonymousID( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + value=self.read_string(reader) ) if message_id == 96: return IOSScreenChanges( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - x=MessageCodec.read_uint(reader), - y=MessageCodec.read_uint(reader), - width=MessageCodec.read_uint(reader), - height=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + x=self.read_uint(reader), + y=self.read_uint(reader), + width=self.read_uint(reader), + height=self.read_uint(reader) ) if message_id == 97: return IOSCrash( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader), - reason=MessageCodec.read_string(reader), - stacktrace=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + name=self.read_string(reader), + reason=self.read_string(reader), + stacktrace=self.read_string(reader) ) if message_id == 98: - return IOSScreenEnter( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - title=MessageCodec.read_string(reader), - view_name=MessageCodec.read_string(reader) - ) - - if message_id == 99: - return IOSScreenLeave( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - title=MessageCodec.read_string(reader), - view_name=MessageCodec.read_string(reader) + return IOSViewComponentEvent( + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + screen_name=self.read_string(reader), + view_name=self.read_string(reader), + visible=self.read_boolean(reader) ) if message_id == 100: return IOSClickEvent( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - label=MessageCodec.read_string(reader), - x=MessageCodec.read_uint(reader), - y=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + label=self.read_string(reader), + x=self.read_uint(reader), + y=self.read_uint(reader) ) if message_id == 101: return IOSInputEvent( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - value=MessageCodec.read_string(reader), - value_masked=MessageCodec.read_boolean(reader), - label=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + value=self.read_string(reader), + value_masked=self.read_boolean(reader), + label=self.read_string(reader) ) if message_id == 102: return IOSPerformanceEvent( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - name=MessageCodec.read_string(reader), - value=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + name=self.read_string(reader), + value=self.read_uint(reader) ) if message_id == 103: return IOSLog( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - severity=MessageCodec.read_string(reader), - content=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + severity=self.read_string(reader), + content=self.read_string(reader) ) if message_id == 104: return IOSInternalError( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - content=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + content=self.read_string(reader) ) if message_id == 105: return IOSNetworkCall( - timestamp=MessageCodec.read_uint(reader), - length=MessageCodec.read_uint(reader), - duration=MessageCodec.read_uint(reader), - headers=MessageCodec.read_string(reader), - body=MessageCodec.read_string(reader), - url=MessageCodec.read_string(reader), - success=MessageCodec.read_boolean(reader), - method=MessageCodec.read_string(reader), - status=MessageCodec.read_uint(reader) + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + type=self.read_string(reader), + method=self.read_string(reader), + url=self.read_string(reader), + request=self.read_string(reader), + response=self.read_string(reader), + status=self.read_uint(reader), + duration=self.read_uint(reader) + ) + + if message_id == 106: + return IOSSwipeEvent( + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + label=self.read_string(reader), + x=self.read_uint(reader), + y=self.read_uint(reader), + direction=self.read_string(reader) + ) + + if message_id == 107: + return IOSBatchMeta( + timestamp=self.read_uint(reader), + length=self.read_uint(reader), + first_index=self.read_uint(reader) ) if message_id == 110: return IOSPerformanceAggregated( - timestamp_start=MessageCodec.read_uint(reader), - timestamp_end=MessageCodec.read_uint(reader), - min_fps=MessageCodec.read_uint(reader), - avg_fps=MessageCodec.read_uint(reader), - max_fps=MessageCodec.read_uint(reader), - min_cpu=MessageCodec.read_uint(reader), - avg_cpu=MessageCodec.read_uint(reader), - max_cpu=MessageCodec.read_uint(reader), - min_memory=MessageCodec.read_uint(reader), - avg_memory=MessageCodec.read_uint(reader), - max_memory=MessageCodec.read_uint(reader), - min_battery=MessageCodec.read_uint(reader), - avg_battery=MessageCodec.read_uint(reader), - max_battery=MessageCodec.read_uint(reader) + timestamp_start=self.read_uint(reader), + timestamp_end=self.read_uint(reader), + min_fps=self.read_uint(reader), + avg_fps=self.read_uint(reader), + max_fps=self.read_uint(reader), + min_cpu=self.read_uint(reader), + avg_cpu=self.read_uint(reader), + max_cpu=self.read_uint(reader), + min_memory=self.read_uint(reader), + avg_memory=self.read_uint(reader), + max_memory=self.read_uint(reader), + min_battery=self.read_uint(reader), + avg_battery=self.read_uint(reader), + max_battery=self.read_uint(reader) ) if message_id == 111: return IOSIssueEvent( - timestamp=MessageCodec.read_uint(reader), - type=MessageCodec.read_string(reader), - context_string=MessageCodec.read_string(reader), - context=MessageCodec.read_string(reader), - payload=MessageCodec.read_string(reader) + timestamp=self.read_uint(reader), + type=self.read_string(reader), + context_string=self.read_string(reader), + context=self.read_string(reader), + payload=self.read_string(reader) ) diff --git a/frontend/app/player/web/messages/RawMessageReader.gen.ts b/frontend/app/player/web/messages/RawMessageReader.gen.ts index 78a9254dc..c18852feb 100644 --- a/frontend/app/player/web/messages/RawMessageReader.gen.ts +++ b/frontend/app/player/web/messages/RawMessageReader.gen.ts @@ -689,32 +689,6 @@ export default class RawMessageReader extends PrimitiveReader { }; } - case 90: { - const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } - const projectID = this.readUint(); if (projectID === null) { return resetPointer() } - const trackerVersion = this.readString(); if (trackerVersion === null) { return resetPointer() } - const revID = this.readString(); if (revID === null) { return resetPointer() } - const userUUID = this.readString(); if (userUUID === null) { return resetPointer() } - const userOS = this.readString(); if (userOS === null) { return resetPointer() } - const userOSVersion = this.readString(); if (userOSVersion === null) { return resetPointer() } - const userDevice = this.readString(); if (userDevice === null) { return resetPointer() } - const userDeviceType = this.readString(); if (userDeviceType === null) { return resetPointer() } - const userCountry = this.readString(); if (userCountry === null) { return resetPointer() } - return { - tp: MType.IosSessionStart, - timestamp, - projectID, - trackerVersion, - revID, - userUUID, - userOS, - userOSVersion, - userDevice, - userDeviceType, - userCountry, - }; - } - case 93: { const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } const length = this.readUint(); if (length === null) { return resetPointer() } @@ -763,6 +737,22 @@ export default class RawMessageReader extends PrimitiveReader { }; } + case 101: { + const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } + const length = this.readUint(); if (length === null) { return resetPointer() } + const value = this.readString(); if (value === null) { return resetPointer() } + const valueMasked = this.readBoolean(); if (valueMasked === null) { return resetPointer() } + const label = this.readString(); if (label === null) { return resetPointer() } + return { + tp: MType.IosInputEvent, + timestamp, + length, + value, + valueMasked, + label, + }; + } + case 102: { const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } const length = this.readUint(); if (length === null) { return resetPointer() } @@ -794,24 +784,42 @@ export default class RawMessageReader extends PrimitiveReader { case 105: { const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } const length = this.readUint(); if (length === null) { return resetPointer() } - const duration = this.readUint(); if (duration === null) { return resetPointer() } - const headers = this.readString(); if (headers === null) { return resetPointer() } - const body = this.readString(); if (body === null) { return resetPointer() } - const url = this.readString(); if (url === null) { return resetPointer() } - const success = this.readBoolean(); if (success === null) { return resetPointer() } + const type = this.readString(); if (type === null) { return resetPointer() } const method = this.readString(); if (method === null) { return resetPointer() } + const url = this.readString(); if (url === null) { return resetPointer() } + const request = this.readString(); if (request === null) { return resetPointer() } + const response = this.readString(); if (response === null) { return resetPointer() } const status = this.readUint(); if (status === null) { return resetPointer() } + const duration = this.readUint(); if (duration === null) { return resetPointer() } return { tp: MType.IosNetworkCall, timestamp, length, - duration, - headers, - body, - url, - success, + type, method, + url, + request, + response, status, + duration, + }; + } + + case 106: { + const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } + const length = this.readUint(); if (length === null) { return resetPointer() } + const label = this.readString(); if (label === null) { return resetPointer() } + const x = this.readUint(); if (x === null) { return resetPointer() } + const y = this.readUint(); if (y === null) { return resetPointer() } + const direction = this.readString(); if (direction === null) { return resetPointer() } + return { + tp: MType.IosSwipeEvent, + timestamp, + length, + label, + x, + y, + direction, }; } diff --git a/frontend/app/player/web/messages/filters.gen.ts b/frontend/app/player/web/messages/filters.gen.ts index 6eedfd1d2..12c16e6bd 100644 --- a/frontend/app/player/web/messages/filters.gen.ts +++ b/frontend/app/player/web/messages/filters.gen.ts @@ -3,7 +3,7 @@ import { MType } from './raw.gen' -const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,54,55,57,58,59,60,61,67,69,70,71,72,73,74,75,76,77,113,114,117,118,90,93,96,100,102,103,105] +const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,54,55,57,58,59,60,61,67,69,70,71,72,73,74,75,76,77,113,114,117,118,93,96,100,101,102,103,105,106] export function isDOMType(t: MType) { return DOM_TYPES.includes(t) } \ No newline at end of file diff --git a/frontend/app/player/web/messages/message.gen.ts b/frontend/app/player/web/messages/message.gen.ts index fd622f0c5..9263c0a8f 100644 --- a/frontend/app/player/web/messages/message.gen.ts +++ b/frontend/app/player/web/messages/message.gen.ts @@ -60,13 +60,14 @@ import type { RawResourceTiming, RawTabChange, RawTabData, - RawIosSessionStart, RawIosCustomEvent, RawIosScreenChanges, RawIosClickEvent, + RawIosInputEvent, RawIosPerformanceEvent, RawIosLog, RawIosNetworkCall, + RawIosSwipeEvent, } from './raw.gen' export type Message = RawMessage & Timed @@ -184,17 +185,19 @@ export type TabChange = RawTabChange & Timed export type TabData = RawTabData & Timed -export type IosSessionStart = RawIosSessionStart & Timed - export type IosCustomEvent = RawIosCustomEvent & Timed export type IosScreenChanges = RawIosScreenChanges & Timed export type IosClickEvent = RawIosClickEvent & Timed +export type IosInputEvent = RawIosInputEvent & Timed + export type IosPerformanceEvent = RawIosPerformanceEvent & Timed export type IosLog = RawIosLog & Timed export type IosNetworkCall = RawIosNetworkCall & Timed +export type IosSwipeEvent = RawIosSwipeEvent & Timed + diff --git a/frontend/app/player/web/messages/raw.gen.ts b/frontend/app/player/web/messages/raw.gen.ts index 8c37ebada..073512016 100644 --- a/frontend/app/player/web/messages/raw.gen.ts +++ b/frontend/app/player/web/messages/raw.gen.ts @@ -58,13 +58,14 @@ export const enum MType { ResourceTiming = 116, TabChange = 117, TabData = 118, - IosSessionStart = 90, IosCustomEvent = 93, IosScreenChanges = 96, IosClickEvent = 100, + IosInputEvent = 101, IosPerformanceEvent = 102, IosLog = 103, IosNetworkCall = 105, + IosSwipeEvent = 106, } @@ -459,20 +460,6 @@ export interface RawTabData { tabId: string, } -export interface RawIosSessionStart { - tp: MType.IosSessionStart, - timestamp: number, - projectID: number, - trackerVersion: string, - revID: string, - userUUID: string, - userOS: string, - userOSVersion: string, - userDevice: string, - userDeviceType: string, - userCountry: string, -} - export interface RawIosCustomEvent { tp: MType.IosCustomEvent, timestamp: number, @@ -500,6 +487,15 @@ export interface RawIosClickEvent { y: number, } +export interface RawIosInputEvent { + tp: MType.IosInputEvent, + timestamp: number, + length: number, + value: string, + valueMasked: boolean, + label: string, +} + export interface RawIosPerformanceEvent { tp: MType.IosPerformanceEvent, timestamp: number, @@ -520,14 +516,24 @@ export interface RawIosNetworkCall { tp: MType.IosNetworkCall, timestamp: number, length: number, - duration: number, - headers: string, - body: string, - url: string, - success: boolean, + type: string, method: string, + url: string, + request: string, + response: string, status: number, + duration: number, +} + +export interface RawIosSwipeEvent { + tp: MType.IosSwipeEvent, + timestamp: number, + length: number, + label: string, + x: number, + y: number, + direction: string, } -export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequest | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall; +export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequest | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosInputEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall | RawIosSwipeEvent; diff --git a/frontend/app/player/web/messages/tracker-legacy.gen.ts b/frontend/app/player/web/messages/tracker-legacy.gen.ts index 207b4ce2a..a0ddd3f17 100644 --- a/frontend/app/player/web/messages/tracker-legacy.gen.ts +++ b/frontend/app/player/web/messages/tracker-legacy.gen.ts @@ -59,11 +59,12 @@ export const TP_MAP = { 116: MType.ResourceTiming, 117: MType.TabChange, 118: MType.TabData, - 90: MType.IosSessionStart, 93: MType.IosCustomEvent, 96: MType.IosScreenChanges, 100: MType.IosClickEvent, + 101: MType.IosInputEvent, 102: MType.IosPerformanceEvent, 103: MType.IosLog, 105: MType.IosNetworkCall, + 106: MType.IosSwipeEvent, } as const diff --git a/mobs/ios_messages.rb b/mobs/ios_messages.rb index 342137877..0b9775d98 100644 --- a/mobs/ios_messages.rb +++ b/mobs/ios_messages.rb @@ -1,30 +1,17 @@ -message 107, 'IOSBatchMeta', :replayer => false do - uint 'Timestamp' - uint 'Length' - uint 'FirstIndex' -end - -message 90, 'IOSSessionStart', :replayer => true do +message 90, 'IOSSessionStart', :replayer => false do uint 'Timestamp' - # uint 'Length' - - uint 'ProjectID' - string 'TrackerVersion' - string 'RevID' - string 'UserUUID' - # string 'UserAgent' - string 'UserOS' - string 'UserOSVersion' - # string 'UserBrowser' - # string 'UserBrowserVersion' - string 'UserDevice' - string 'UserDeviceType' - # uint 'UserDeviceMemorySize' - # uint 'UserDeviceHeapSize' - string 'UserCountry' + uint 'ProjectID' + string 'TrackerVersion' + string 'RevID' + string 'UserUUID' + string 'UserOS' + string 'UserOSVersion' + string 'UserDevice' + string 'UserDeviceType' + string 'UserCountry' end -message 91, 'IOSSessionEnd' do +message 91, 'IOSSessionEnd' do uint 'Timestamp' end @@ -68,21 +55,15 @@ message 97, 'IOSCrash' do uint 'Length' string 'Name' string 'Reason' - string 'Stacktrace' + string 'Stacktrace' end -message 98, 'IOSScreenEnter' do +message 98, 'IOSViewComponentEvent' do uint 'Timestamp' uint 'Length' - string 'Title' - string 'ViewName' -end - -message 99, 'IOSScreenLeave' do - uint 'Timestamp' - uint 'Length' - string 'Title' + string 'ScreenName' string 'ViewName' + boolean 'Visible' end message 100, 'IOSClickEvent', :replayer => true do @@ -93,7 +74,7 @@ message 100, 'IOSClickEvent', :replayer => true do uint 'Y' end -message 101, 'IOSInputEvent' do +message 101, 'IOSInputEvent', :replayer => true do uint 'Timestamp' uint 'Length' string 'Value' @@ -136,17 +117,33 @@ message 104, 'IOSInternalError' do end message 105, 'IOSNetworkCall', :replayer => true do - uint 'Timestamp' - uint 'Length' - uint 'Duration' - string 'Headers' - string 'Body' - string 'URL' - boolean 'Success' - string 'Method' - uint 'Status' + uint 'Timestamp' + uint 'Length' + string 'Type' + string 'Method' + string 'URL' + string 'Request' + string 'Response' + uint 'Status' + uint 'Duration' end -message 110, 'IOSPerformanceAggregated', :swift => false do + +message 106, 'IOSSwipeEvent', :replayer => true do + uint 'Timestamp' + uint 'Length' + string 'Label' + uint 'X' + uint 'Y' + string 'Direction' +end + +message 107, 'IOSBatchMeta', :replayer => false do + uint 'Timestamp' + uint 'Length' + uint 'FirstIndex' +end + +message 110, 'IOSPerformanceAggregated', :swift => false do uint 'TimestampStart' uint 'TimestampEnd' uint 'MinFPS' diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/.helmignore b/scripts/helmcharts/openreplay/charts/imagestorage/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/Chart.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/Chart.yaml new file mode 100644 index 000000000..9aaaad5df --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: imagestorage +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +AppVersion: "v1.14.0" diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/imagestorage/templates/NOTES.txt new file mode 100644 index 000000000..f7917fcb8 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "imagestorage.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "imagestorage.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "imagestorage.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "imagestorage.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/imagestorage/templates/_helpers.tpl new file mode 100644 index 000000000..c57877578 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "imagestorage.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "imagestorage.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "imagestorage.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "imagestorage.labels" -}} +helm.sh/chart: {{ include "imagestorage.chart" . }} +{{ include "imagestorage.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "imagestorage.selectorLabels" -}} +app.kubernetes.io/name: {{ include "imagestorage.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "imagestorage.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "imagestorage.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/deployment.yaml new file mode 100644 index 000000000..747d8232f --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/deployment.yaml @@ -0,0 +1,131 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "imagestorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "imagestorage.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "imagestorage.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "imagestorage.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + shareProcessNamespace: true + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- if .Values.global.enterpriseEditionLicense }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}-ee" + {{- else }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.healthCheck}} + {{- .Values.healthCheck | toYaml | nindent 10}} + {{- end}} + env: + - name: AWS_ACCESS_KEY_ID + {{- if .Values.global.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.s3.existingSecret }} + key: access-key + {{- else }} + value: {{ .Values.global.s3.accessKey }} + {{- end }} + - name: AWS_SECRET_ACCESS_KEY + {{- if .Values.global.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.s3.existingSecret }} + key: secret-key + {{- else }} + value: {{ .Values.global.s3.secretKey }} + {{- end }} + - name: AWS_ENDPOINT + value: '{{ .Values.global.s3.endpoint }}' + - name: AWS_REGION + value: '{{ .Values.global.s3.region }}' + - name: BUCKET_NAME + value: {{ .Values.global.s3.recordingsBucket }} + - name: LICENSE_KEY + value: '{{ .Values.global.enterpriseEditionLicense }}' + - name: KAFKA_SERVERS + value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}' + - name: KAFKA_USE_SSL + value: '{{ .Values.global.kafka.kafkaUseSsl }}' + {{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }} + {{- range $key, $val := .Values.global.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end }} + {{- range $key, $val := .Values.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end}} + ports: + {{- range $key, $val := .Values.service.ports }} + - name: {{ $key }} + containerPort: {{ $val }} + protocol: TCP + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: datadir + mountPath: /mnt/efs + {{- include "openreplay.volume.redis_ca_certificate.mount" .Values.global.redis | nindent 10 }} + {{- with .Values.persistence.mounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if eq (tpl .Values.pvc.name . ) "hostPath" }} + volumes: + {{- with .Values.persistence.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} + - name: datadir + hostPath: + # Ensure the file directory is created. + path: {{ .Values.pvc.hostMountPath }} + type: DirectoryOrCreate + {{- else }} + volumes: + {{- with .Values.persistence.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + - name: datadir + persistentVolumeClaim: + claimName: "{{ tpl .Values.pvc.name . }}" + {{- end }} + {{- include "openreplay.volume.redis_ca_certificate" .Values.global.redis | nindent 6 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/hpa.yaml new file mode 100644 index 000000000..173118010 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/hpa.yaml @@ -0,0 +1,29 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "imagestorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "imagestorage.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/ingress.yaml new file mode 100644 index 000000000..dac53a91d --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "imagestorage.fullname" . -}} +{{- $svcPort := .Values.service.ports.http -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/service.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/service.yaml new file mode 100644 index 000000000..e5a381b9c --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "imagestorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + {{- range $key, $val := .Values.service.ports }} + - port: {{ $val }} + targetPort: {{ $key }} + protocol: TCP + name: {{ $key }} + {{- end}} + selector: + {{- include "imagestorage.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceMonitor.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceMonitor.yaml new file mode 100644 index 000000000..eea202001 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceMonitor.yaml @@ -0,0 +1,18 @@ +{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled ) }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "imagestorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} + {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + {{- .Values.serviceMonitor.scrapeConfigs | toYaml | nindent 4 }} + selector: + matchLabels: + {{- include "imagestorage.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceaccount.yaml new file mode 100644 index 000000000..83c85ee5d --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "imagestorage.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "imagestorage.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/templates/tests/test-connection.yaml new file mode 100644 index 000000000..66eab4707 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "imagestorage.fullname" . }}-test-connection" + labels: + {{- include "imagestorage.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "imagestorage.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/scripts/helmcharts/openreplay/charts/imagestorage/values.yaml b/scripts/helmcharts/openreplay/charts/imagestorage/values.yaml new file mode 100644 index 000000000..a247300fe --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/imagestorage/values.yaml @@ -0,0 +1,124 @@ +# Default values for openreplay. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: "{{ .Values.global.openReplayContainerRegistry }}/imagestorage" + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "imagestorage" +fullnameOverride: "imagestorage-openreplay" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +securityContext: + runAsUser: 1001 + runAsGroup: 1001 +podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + fsGroupChangePolicy: "OnRootMismatch" + +#securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + ports: + http: 9000 + metrics: 8888 + +serviceMonitor: + enabled: true + additionalLabels: + release: observability + scrapeConfigs: + - port: metrics + honorLabels: true + interval: 15s + path: /metrics + scheme: http + scrapeTimeout: 10s + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +env: + FS_CLEAN_HRS: 24 + +pvc: + # This can be either persistentVolumeClaim or hostPath. + # In case of pvc, you'll have to provide the pvc name. + # For example + # name: openreplay-efs + name: "{{ .Values.global.pvcRWXName }}" + hostMountPath: /openreplay/storage/nfs + +persistence: {} + # # Spec of spec.template.spec.containers[*].volumeMounts + # mounts: + # - name: kafka-ssl + # mountPath: /opt/kafka/ssl + # # Spec of spec.template.spec.volumes + # volumes: + # - name: kafka-ssl + # secret: + # secretName: kafka-ssl + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/.helmignore b/scripts/helmcharts/openreplay/charts/videostorage/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/scripts/helmcharts/openreplay/charts/videostorage/Chart.yaml b/scripts/helmcharts/openreplay/charts/videostorage/Chart.yaml new file mode 100644 index 000000000..fdd3eda10 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: videostorage +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +AppVersion: "v1.14.0" diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/videostorage/templates/NOTES.txt new file mode 100644 index 000000000..ca7428fc4 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "videostorage.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "videostorage.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "videostorage.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "videostorage.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/videostorage/templates/_helpers.tpl new file mode 100644 index 000000000..0856e8e31 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "videostorage.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "videostorage.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "videostorage.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "videostorage.labels" -}} +helm.sh/chart: {{ include "videostorage.chart" . }} +{{ include "videostorage.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "videostorage.selectorLabels" -}} +app.kubernetes.io/name: {{ include "videostorage.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "videostorage.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "videostorage.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/deployment.yaml new file mode 100644 index 000000000..971b6f7c4 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/deployment.yaml @@ -0,0 +1,131 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "videostorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "videostorage.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "videostorage.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "videostorage.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + shareProcessNamespace: true + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- if .Values.global.enterpriseEditionLicense }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}-ee" + {{- else }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.healthCheck}} + {{- .Values.healthCheck | toYaml | nindent 10}} + {{- end}} + env: + - name: AWS_ACCESS_KEY_ID + {{- if .Values.global.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.s3.existingSecret }} + key: access-key + {{- else }} + value: {{ .Values.global.s3.accessKey }} + {{- end }} + - name: AWS_SECRET_ACCESS_KEY + {{- if .Values.global.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.s3.existingSecret }} + key: secret-key + {{- else }} + value: {{ .Values.global.s3.secretKey }} + {{- end }} + - name: AWS_ENDPOINT + value: '{{ .Values.global.s3.endpoint }}' + - name: AWS_REGION + value: '{{ .Values.global.s3.region }}' + - name: BUCKET_NAME + value: {{ .Values.global.s3.recordingsBucket }} + - name: LICENSE_KEY + value: '{{ .Values.global.enterpriseEditionLicense }}' + - name: KAFKA_SERVERS + value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}' + - name: KAFKA_USE_SSL + value: '{{ .Values.global.kafka.kafkaUseSsl }}' + {{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }} + {{- range $key, $val := .Values.global.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end }} + {{- range $key, $val := .Values.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end}} + ports: + {{- range $key, $val := .Values.service.ports }} + - name: {{ $key }} + containerPort: {{ $val }} + protocol: TCP + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: datadir + mountPath: /mnt/efs + {{- include "openreplay.volume.redis_ca_certificate.mount" .Values.global.redis | nindent 10 }} + {{- with .Values.persistence.mounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if eq (tpl .Values.pvc.name . ) "hostPath" }} + volumes: + {{- with .Values.persistence.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} + - name: datadir + hostPath: + # Ensure the file directory is created. + path: {{ .Values.pvc.hostMountPath }} + type: DirectoryOrCreate + {{- else }} + volumes: + {{- with .Values.persistence.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + - name: datadir + persistentVolumeClaim: + claimName: "{{ tpl .Values.pvc.name . }}" + {{- end }} + {{- include "openreplay.volume.redis_ca_certificate" .Values.global.redis | nindent 6 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/hpa.yaml new file mode 100644 index 000000000..326b08152 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/hpa.yaml @@ -0,0 +1,29 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "videostorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "videostorage.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/ingress.yaml new file mode 100644 index 000000000..0568a9202 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "videostorage.fullname" . -}} +{{- $svcPort := .Values.service.ports.http -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/service.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/service.yaml new file mode 100644 index 000000000..d9c3328f0 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "videostorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + {{- range $key, $val := .Values.service.ports }} + - port: {{ $val }} + targetPort: {{ $key }} + protocol: TCP + name: {{ $key }} + {{- end}} + selector: + {{- include "videostorage.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceMonitor.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceMonitor.yaml new file mode 100644 index 000000000..fd6009e5b --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceMonitor.yaml @@ -0,0 +1,18 @@ +{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled ) }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "videostorage.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} + {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + {{- .Values.serviceMonitor.scrapeConfigs | toYaml | nindent 4 }} + selector: + matchLabels: + {{- include "videostorage.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceaccount.yaml new file mode 100644 index 000000000..eb5ad9988 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "videostorage.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "videostorage.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/videostorage/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/videostorage/templates/tests/test-connection.yaml new file mode 100644 index 000000000..460be7d4c --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "videostorage.fullname" . }}-test-connection" + labels: + {{- include "videostorage.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "videostorage.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/scripts/helmcharts/openreplay/charts/videostorage/values.yaml b/scripts/helmcharts/openreplay/charts/videostorage/values.yaml new file mode 100644 index 000000000..6e876b701 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/videostorage/values.yaml @@ -0,0 +1,124 @@ +# Default values for openreplay. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: "{{ .Values.global.openReplayContainerRegistry }}/videostorage" + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "videostorage" +fullnameOverride: "videostorage-openreplay" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +securityContext: + runAsUser: 1001 + runAsGroup: 1001 +podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + fsGroupChangePolicy: "OnRootMismatch" + +#securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + ports: + http: 9000 + metrics: 8888 + +serviceMonitor: + enabled: true + additionalLabels: + release: observability + scrapeConfigs: + - port: metrics + honorLabels: true + interval: 15s + path: /metrics + scheme: http + scrapeTimeout: 10s + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +env: + FS_CLEAN_HRS: 24 + +pvc: + # This can be either persistentVolumeClaim or hostPath. + # In case of pvc, you'll have to provide the pvc name. + # For example + # name: openreplay-efs + name: "{{ .Values.global.pvcRWXName }}" + hostMountPath: /openreplay/storage/nfs + +persistence: {} + # # Spec of spec.template.spec.containers[*].volumeMounts + # mounts: + # - name: kafka-ssl + # mountPath: /opt/kafka/ssl + # # Spec of spec.template.spec.volumes + # volumes: + # - name: kafka-ssl + # secret: + # secretName: kafka-ssl + +nodeSelector: {} + +tolerations: [] + +affinity: {}