Merge branch 'dev' into player-refactoring-phase-1

This commit is contained in:
Alex Kaminskii 2022-11-29 15:51:20 +01:00
commit 1524733f65
37 changed files with 297 additions and 238 deletions

View file

@ -7,7 +7,7 @@ on:
paths:
- sourcemap-reader/**
name: Build and Deploy Chalice
name: Build and Deploy sourcemap-reader
jobs:
deploy:

View file

@ -242,7 +242,7 @@ class JiraManager:
def get_issue_types(self):
try:
types = self._jira.issue_types()
types = self._jira.project(self._config['JIRA_PROJECT_ID']).issueTypes
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:

View file

@ -73,6 +73,8 @@ func main() {
log.Printf("Error while caching: %v", err)
case <-tick:
cacher.UpdateTimeouts()
case msg := <-msgConsumer.Rebalanced():
log.Println(msg)
default:
if !cacher.CanCache() {
continue

View file

@ -163,6 +163,8 @@ func main() {
os.Exit(0)
case <-commitTick:
commitDBUpdates()
case msg := <-consumer.Rebalanced():
log.Println(msg)
default:
// Handle new message from queue
if err := consumer.ConsumeNext(); err != nil {

View file

@ -98,6 +98,8 @@ func main() {
if err := consumer.CommitBack(intervals.EVENTS_BACK_COMMIT_GAP); err != nil {
log.Printf("can't commit messages with offset: %s", err)
}
case msg := <-consumer.Rebalanced():
log.Println(msg)
default:
if err := consumer.ConsumeNext(); err != nil {
log.Fatalf("Error on consuming: %v", err)

View file

@ -82,6 +82,8 @@ func main() {
})
producer.Flush(cfg.ProducerTimeout)
consumer.Commit()
case msg := <-consumer.Rebalanced():
log.Println(msg)
default:
if err := consumer.ConsumeNext(); err != nil {
log.Fatalf("Error on consuming: %v", err)

View file

@ -14,14 +14,11 @@ import (
"openreplay/backend/internal/storage"
"openreplay/backend/pkg/messages"
"openreplay/backend/pkg/monitoring"
"openreplay/backend/pkg/pprof"
"openreplay/backend/pkg/queue"
"openreplay/backend/pkg/url/assets"
)
func main() {
pprof.StartProfilingServer()
metrics := monitoring.New("sink")
log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile)
@ -147,6 +144,15 @@ func main() {
case <-tickInfo:
counter.Print()
log.Printf("writer: %s", writer.Info())
case <-consumer.Rebalanced():
s := time.Now()
// Commit now to avoid duplicate reads
if err := consumer.Commit(); err != nil {
log.Printf("can't commit messages: %s", err)
}
// Sync all files
writer.Sync()
log.Printf("manual sync finished, dur: %d", time.Now().Sub(s).Milliseconds())
default:
err := consumer.ConsumeNext()
if err != nil {

View file

@ -73,6 +73,8 @@ func main() {
os.Exit(0)
case <-counterTick:
go counter.Print()
case msg := <-consumer.Rebalanced():
log.Println(msg)
default:
err := consumer.ConsumeNext()
if err != nil {

View file

@ -8,6 +8,7 @@ require (
github.com/Masterminds/semver v1.5.0
github.com/aws/aws-sdk-go v1.44.98
github.com/btcsuite/btcutil v1.0.2
github.com/confluentinc/confluent-kafka-go v1.8.2
github.com/elastic/go-elasticsearch/v7 v7.13.1
github.com/go-redis/redis v6.15.9+incompatible
github.com/google/uuid v1.3.0
@ -26,9 +27,8 @@ require (
go.opentelemetry.io/otel/exporters/prometheus v0.30.0
go.opentelemetry.io/otel/metric v0.30.0
go.opentelemetry.io/otel/sdk/metric v0.30.0
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c
google.golang.org/api v0.81.0
gopkg.in/confluentinc/confluent-kafka-go.v1 v1.8.2
)
require (
@ -38,7 +38,6 @@ require (
cloud.google.com/go/storage v1.14.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/confluentinc/confluent-kafka-go v1.9.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -53,7 +52,6 @@ require (
github.com/jackc/puddle v1.2.2-0.20220404125616-4e959849469a // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.15.7 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/paulmach/orb v0.7.1 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
@ -69,7 +67,7 @@ require (
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/appengine v1.6.7 // indirect

View file

@ -115,12 +115,11 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/confluentinc/confluent-kafka-go v1.9.0 h1:d1k62oAuQVxgdMdiDQnpkABbtIWTBwXHpDcyGQUw5QQ=
github.com/confluentinc/confluent-kafka-go v1.9.0/go.mod h1:WDFs+KlhHITEoCzEfHSNgj5aP7vjajyYbZpvTEGs1sE=
github.com/confluentinc/confluent-kafka-go v1.8.2 h1:PBdbvYpyOdFLehj8j+9ba7FL4c4Moxn79gy9cYKxG5E=
github.com/confluentinc/confluent-kafka-go v1.8.2/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -328,14 +327,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
@ -400,8 +397,6 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@ -566,8 +561,9 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -679,8 +675,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -941,8 +937,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/confluentinc/confluent-kafka-go.v1 v1.8.2 h1:QAgN6OC0o7dwvyz+HML6GYm+0Pk54O91+oxGqJ/5z8I=
gopkg.in/confluentinc/confluent-kafka-go.v1 v1.8.2/go.mod h1:ZdI3yfYmdNSLQPNCpO1y00EHyWaHG5EnQEyL/ntAegY=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

View file

@ -97,17 +97,21 @@ func (w *SessionWriter) Info() string {
return fmt.Sprintf("%d sessions", w.meta.Count())
}
func (w *SessionWriter) synchronizer() {
tick := time.Tick(w.syncTimeout)
for {
select {
case <-tick:
func (w *SessionWriter) Sync() {
w.sessions.Range(func(sid, lockObj any) bool {
if err := w.sync(sid.(uint64)); err != nil {
log.Printf("can't sync file descriptor: %s", err)
}
return true
})
}
func (w *SessionWriter) synchronizer() {
tick := time.Tick(w.syncTimeout)
for {
select {
case <-tick:
w.Sync()
case <-w.done:
w.sessions.Range(func(sid, lockObj any) bool {
if err := w.Close(sid.(uint64)); err != nil {

View file

@ -6,6 +6,7 @@ type Consumer interface {
CommitBack(gap int64) error
Commit() error
Close()
Rebalanced() <-chan interface{}
}
// Producer sends batches of session data to queue (redis or kafka)

View file

@ -27,6 +27,7 @@ type Consumer struct {
idsPending streamPendingIDsMap
lastTs int64
autoCommit bool
event chan interface{}
}
func NewConsumer(group string, streams []string, messageIterator messages.MessageIterator) *Consumer {
@ -57,11 +58,16 @@ func NewConsumer(group string, streams []string, messageIterator messages.Messag
group: group,
autoCommit: true,
idsPending: idsPending,
event: make(chan interface{}, 4),
}
}
const READ_COUNT = 10
func (c *Consumer) Rebalanced() <-chan interface{} {
return c.event
}
func (c *Consumer) ConsumeNext() error {
// MBTODO: read in go routine, send messages to channel
res, err := c.redis.XReadGroup(&_redis.XReadGroupArgs{

View file

@ -2,14 +2,14 @@ package kafka
import (
"log"
"openreplay/backend/pkg/messages"
"os"
"time"
"github.com/pkg/errors"
"gopkg.in/confluentinc/confluent-kafka-go.v1/kafka"
"openreplay/backend/pkg/env"
"openreplay/backend/pkg/messages"
"github.com/confluentinc/confluent-kafka-go/kafka"
"github.com/pkg/errors"
)
type Message = kafka.Message
@ -19,7 +19,7 @@ type Consumer struct {
messageIterator messages.MessageIterator
commitTicker *time.Ticker
pollTimeout uint
events chan interface{}
lastReceivedPrtTs map[int32]int64
}
@ -61,6 +61,21 @@ func NewConsumer(
if err != nil {
log.Fatalln(err)
}
var commitTicker *time.Ticker
if autoCommit {
commitTicker = time.NewTicker(2 * time.Minute)
}
consumer := &Consumer{
c: c,
messageIterator: messageIterator,
commitTicker: commitTicker,
pollTimeout: 200,
events: make(chan interface{}, 4),
lastReceivedPrtTs: make(map[int32]int64, 16),
}
subREx := "^("
for i, t := range topics {
if i != 0 {
@ -69,22 +84,27 @@ func NewConsumer(
subREx += t
}
subREx += ")$"
if err := c.Subscribe(subREx, nil); err != nil {
if err := c.Subscribe(subREx, consumer.reBalanceCallback); err != nil {
log.Fatalln(err)
}
var commitTicker *time.Ticker
if autoCommit {
commitTicker = time.NewTicker(2 * time.Minute)
}
return consumer
}
return &Consumer{
c: c,
messageIterator: messageIterator,
commitTicker: commitTicker,
pollTimeout: 200,
lastReceivedPrtTs: make(map[int32]int64),
func (consumer *Consumer) reBalanceCallback(_ *kafka.Consumer, e kafka.Event) error {
switch evt := e.(type) {
case kafka.RevokedPartitions:
// receive before re-balancing partitions; stop consuming messages and commit current state
consumer.events <- evt.String()
case kafka.AssignedPartitions:
// receive after re-balancing partitions; continue consuming messages
//consumer.events <- evt.String()
}
return nil
}
func (consumer *Consumer) Rebalanced() <-chan interface{} {
return consumer.events
}
func (consumer *Consumer) Commit() error {

View file

@ -1,13 +1,12 @@
package kafka
import (
"log"
"fmt"
"log"
"gopkg.in/confluentinc/confluent-kafka-go.v1/kafka"
"github.com/confluentinc/confluent-kafka-go/kafka"
)
func logPartitions(s string, prts []kafka.TopicPartition) {
for _, p := range prts {
s = fmt.Sprintf("%v | %v", s, p.Partition)

View file

@ -5,7 +5,7 @@ import (
"log"
"os"
"gopkg.in/confluentinc/confluent-kafka-go.v1/kafka"
"github.com/confluentinc/confluent-kafka-go/kafka"
"openreplay/backend/pkg/env"
)

View file

@ -23,4 +23,4 @@ MINIO_SECRET_KEY = ''
# APP and TRACKER VERSIONS
VERSION = '1.9.0'
TRACKER_VERSION = '4.1.7'
TRACKER_VERSION = '4.1.9'

View file

@ -46,7 +46,7 @@ function AuditView(props) {
]}
defaultValue={order}
plain
onChange={({ value }) => auditStore.updateKey('order', value)}
onChange={({ value }) => auditStore.updateKey('order', value.value)}
/>
</div>
<AuditSearchField onChange={(value) => auditStore.updateKey('searchQuery', value) }/>

View file

@ -2,11 +2,10 @@ import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { setAutoplayValues } from 'Duck/sessions';
import { session as sessionRoute } from 'App/routes';
import { Link, Icon, Toggler, Tooltip } from 'UI';
import { Link, Icon, Tooltip } from 'UI';;
import { withRouter, RouteComponentProps } from 'react-router-dom';
import cn from 'classnames';
import { fetchAutoplaySessions } from 'Duck/search';
import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite';
const PER_PAGE = 10;
@ -14,12 +13,10 @@ const PER_PAGE = 10;
interface Props extends RouteComponentProps {
previousId: string;
nextId: string;
autoplay: boolean;
defaultList: any;
currentPage: number;
total: number;
setAutoplayValues?: () => void;
toggleAutoplay?: () => void;
latestRequestTime: any;
sessionIds: any;
fetchAutoplaySessions?: (page: number) => Promise<void>;
@ -37,8 +34,6 @@ function Autoplay(props: Props) {
params: { sessionId },
},
} = props;
const { player, store } = React.useContext(PlayerContext)
const { autoplay } = store.get()
const disabled = sessionIds.length === 0;
@ -57,14 +52,6 @@ function Autoplay(props: Props) {
return (
<div className="flex items-center">
<div
onClick={() => player.toggleAutoplay()}
className="cursor-pointer flex items-center mr-2 hover:bg-gray-light-shade rounded-md p-2"
>
<Toggler name="sessionsLive" onChange={() => player.toggleAutoplay()} checked={autoplay} />
<span className="ml-2 whitespace-nowrap">Auto-Play</span>
</div>
<Tooltip
placement="bottom"
title={<div className="whitespace-nowrap">Play Previous Session</div>}
@ -114,4 +101,4 @@ export default connect(
latestRequestTime: state.getIn(['search', 'latestRequestTime']),
}),
{ setAutoplayValues, fetchAutoplaySessions }
)(withRouter(observer(Autoplay)))
)(withRouter(Autoplay))

View file

@ -18,7 +18,6 @@ const SelectedValue = ({ icon, text }) => {
class IssueForm extends React.PureComponent {
componentDidMount() {
const { projects, issueTypes } = this.props;
this.props.init({
projectId: projects[0] ? projects[0].id : '',
issueType: issueTypes[0] ? issueTypes[0].id : '',
@ -27,8 +26,8 @@ class IssueForm extends React.PureComponent {
componentWillReceiveProps(newProps) {
const { instance } = this.props;
if (instance.projectId && newProps.instance.projectId != instance.projectId) {
this.props.fetchMeta(instance.projectId).then(() => {
if (newProps.instance.projectId && newProps.instance.projectId != instance.projectId) {
this.props.fetchMeta(newProps.instance.projectId).then(() => {
this.props.edit({ issueType: '', assignee: '', projectId: newProps.instance.projectId });
});
}
@ -87,7 +86,7 @@ class IssueForm extends React.PureComponent {
<Select
name="projectId"
options={projectOptions}
// value={instance.projectId}
defaultValue={instance.projectId}
fluid
onChange={this.writeOption}
placeholder="Project"
@ -100,7 +99,7 @@ class IssueForm extends React.PureComponent {
name="issueType"
labeled
options={issueTypeOptions}
value={instance.issueType}
defaultValue={instance.issueType}
fluid
onChange={this.writeOption}
placeholder="Select issue type"

View file

@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { Icon, Popover } from 'UI';
import { Icon, Popover, Button } from 'UI';
import IssuesModal from './IssuesModal';
import { fetchProjects, fetchMeta } from 'Duck/assignments';
import stl from './issues.module.css';
@ -67,30 +67,27 @@ class Issues extends React.Component {
const provider = issuesIntegration.provider;
return (
<div className="relative h-full w-full p-3">
<div className={stl.buttonWrapper}>
<Popover
onOpen={this.handleOpen}
render={({ close }) => (
<div>
<IssuesModal
provider={provider}
sessionId={sessionId}
closeHandler={close}
/>
<IssuesModal provider={provider} sessionId={sessionId} closeHandler={close} />
</div>
)}
>
<div
className="flex items-center"
<div className="relative">
<Button icon={`integrations/${provider === 'jira' ? 'jira' : 'github'}`} variant="text">
Create Issue
</Button>
</div>
{/* <div
className="flex items-center cursor-pointer"
disabled={!isModalDisplayed && (metaLoading || fetchIssuesLoading || projectsLoading)}
>
<Icon name={`integrations/${provider === 'jira' ? 'jira' : 'github'}`} size="16" />
<span className="ml-2">Create Issue</span>
</div>
<span className="ml-2 whitespace-nowrap">Create Issue</span>
</div> */}
</Popover>
</div>
</div>
);
}
}

View file

@ -67,7 +67,7 @@ function ReadNote(props: Props) {
<Icon name="close" size={18} />
</div>
</div>
<div className="text-xl py-3 overflow-y-scroll capitalize-first" style={{ maxHeight: 400 }}>
<div className="text-xl py-3 overflow-y-auto capitalize-first" style={{ maxHeight: 400 }}>
{props.note.message}
</div>
<div className="w-full">

View file

@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState } from 'react';
import cn from 'classnames';
import { connect } from 'react-redux'
import { connect } from 'react-redux';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { Button, Link } from 'UI'
import { session as sessionRoute, withSiteId } from 'App/routes'
import { Button, Link, Icon } from 'UI';
import { session as sessionRoute, withSiteId } from 'App/routes';
import stl from './AutoplayTimer.module.css';
import clsOv from './overlay.module.css';
@ -13,49 +13,55 @@ interface IProps extends RouteComponentProps {
}
function AutoplayTimer({ nextId, siteId, history }: IProps) {
let timer: NodeJS.Timer
let timer: NodeJS.Timer;
const [cancelled, setCancelled] = useState(false);
const [counter, setCounter] = useState(5);
useEffect(() => {
if(counter > 0) {
if (counter > 0) {
timer = setTimeout(() => {
setCounter(counter - 1)
}, 1000)
setCounter(counter - 1);
}, 1000);
}
if (counter === 0) {
history.push(withSiteId(sessionRoute(nextId), siteId))
history.push(withSiteId(sessionRoute(nextId), siteId));
}
return () => clearTimeout(timer);
}, [counter])
}, [counter]);
const cancel = () => {
clearTimeout(timer)
setCancelled(true)
}
clearTimeout(timer);
setCancelled(true);
};
if (cancelled)
return null
if (cancelled) return null;
return (
<div className={ cn(clsOv.overlay, stl.overlayBg) } >
<div className={cn(clsOv.overlay, stl.overlayBg)}>
<div className="border p-6 shadow-lg bg-white rounded">
<div className="py-4">Next recording will be played in {counter}s</div>
<div className="flex items-center">
<Button primary="outline" onClick={cancel}>Cancel</Button>
<Button primary="outline" onClick={cancel}>
Cancel
</Button>
<div className="px-3" />
<Link to={ sessionRoute(nextId) } disabled={!nextId}>
<Link to={sessionRoute(nextId)} disabled={!nextId}>
<Button variant="primary">Play Now</Button>
</Link>
</div>
<div className="mt-2 flex items-center color-gray-dark">
Turn on/off auto-replay in <Icon name="ellipsis-v" className="mx-1" /> More options
</div>
</div>
)
</div>
);
}
export default withRouter(connect(state => ({
siteId: state.getIn([ 'site', 'siteId' ]),
nextId: parseInt(state.getIn([ 'sessions', 'nextId' ])),
}))(AutoplayTimer))
export default withRouter(
connect((state: any) => ({
siteId: state.getIn(['site', 'siteId']),
nextId: parseInt(state.getIn(['sessions', 'nextId'])),
}))(AutoplayTimer)
);

View file

@ -11,6 +11,7 @@ import { useModal } from 'App/components/Modal';
import BugReportModal from './BugReport/BugReportModal';
import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite';
import AutoplayToggle from 'Shared/AutoplayToggle';
function SubHeader(props) {
const { player, store } = React.useContext(PlayerContext)
@ -74,39 +75,31 @@ function SubHeader(props) {
className="ml-auto text-sm flex items-center color-gray-medium gap-2"
style={{ width: 'max-content' }}
>
<Button icon="file-pdf" variant="text" onClick={showReportModal}>Create Bug Report</Button>
<Button icon="file-pdf" variant="text" onClick={showReportModal}>
Create Bug Report
</Button>
<NotePopup />
<ItemMenu
items={[
{
key: 2,
component: props.jiraConfig && props.jiraConfig.token && (
<Issues sessionId={props.sessionId} />
),
},
{
key: 3,
component: (
<SharePopup
entity="sessions"
id={props.sessionId}
showCopyLink={true}
trigger={
<div className="flex items-center h-full w-full">
<Icon
className="mr-2"
disabled={props.disabled}
name="share-alt"
size="16"
/>
<span>Share</span>
<div className="relative">
<Button icon="share-alt" variant="text" className="relative">
Share
</Button>
</div>
}
/>
),
<ItemMenu
items={[
{
key: 1,
component: <AutoplayToggle />,
},
{
key: 4,
key: 2,
component: <Bookmark noMargin sessionId={props.sessionId} />,
},
]}

View file

@ -1,5 +1,5 @@
import React from 'react';
import { Icon } from 'UI';
import { Icon, Button } from 'UI';
import styles from './menu.module.css';
import cn from 'classnames';
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
@ -32,8 +32,8 @@ export default class ItemMenu extends React.PureComponent<Props> {
};
closeMenu = () => {
this.setState({ displayed: false })
}
this.setState({ displayed: false });
};
render() {
const { items } = this.props;
@ -42,23 +42,9 @@ export default class ItemMenu extends React.PureComponent<Props> {
return (
<div className={styles.wrapper}>
<OutsideClickDetectingDiv onClickOutside={this.closeMenu}>
<div
onClick={this.toggleMenu}
className={cn(
'flex items-center cursor-pointer select-none',
'rounded p-2', displayed ? 'bg-gray-light' : 'hover:bg-gray-light-shade'
)}
>
<div
className={cn('rounded-full flex items-center justify-center', {
'bg-gray-light': displayed,
})}
role="button"
>
<Icon name="ellipsis-v" size="16" />
</div>
<span className={'mr-1 text-disabled-text'}>More</span>
</div>
<Button variant="text" icon="ellipsis-v" onClick={this.toggleMenu}>
More
</Button>
<div className={cn(styles.menu, styles.menuDim)} data-displayed={displayed}>
{items.map((item) =>
item.component ? (

View file

@ -6,6 +6,7 @@ import stl from './signup.module.css'
import { signup } from 'Duck/user';
import { connect } from 'react-redux'
import Select from 'Shared/Select'
import { SITE_ID_STORAGE_KEY } from 'App/constants/storageKeys';
const LOGIN_ROUTE = login()
const recaptchaRef = React.createRef()
@ -41,9 +42,9 @@ export default class SignupForm extends React.Component {
return null;
}
handleSubmit = (token) => {
const { tenantId, fullname, password, email, projectName, organizationName, auth } = this.state;
localStorage.removeItem(SITE_ID_STORAGE_KEY)
this.props.signup({ tenantId, fullname, password, email, projectName, organizationName, auth, 'g-recaptcha-response': token })
this.setState({ reload: true })
}

View file

@ -0,0 +1,29 @@
import React from 'react';
import { Controls as PlayerControls, connectPlayer } from 'Player';
import { Toggler } from 'UI';
interface Props {
toggleAutoplay: () => void;
autoplay: boolean;
}
function AutoplayToggle(props: Props) {
const { autoplay } = props;
return (
<div
onClick={props.toggleAutoplay}
className="cursor-pointer flex items-center mr-2 hover:bg-gray-light-shade rounded-md p-2"
>
<Toggler name="sessionsLive" onChange={props.toggleAutoplay} checked={autoplay} />
<span className="ml-2 whitespace-nowrap">Auto-Play</span>
</div>
);
}
export default connectPlayer(
(state: any) => ({
autoplay: state.autoplay,
}),
{
toggleAutoplay: PlayerControls.toggleAutoplay,
}
)(AutoplayToggle);

View file

@ -0,0 +1 @@
export { default } from './AutoplayToggle';

View file

@ -149,7 +149,7 @@ function ConsolePanel() {
)}
</CellMeasurer>
</React.Fragment>
);
)
}
return (

View file

@ -180,7 +180,6 @@ function NetworkPanel() {
const onTabClick = (activeTab: typeof TAP_KEYS[number]) => devTools.update(INDEX_KEY, { activeTab })
const onFilterChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => devTools.update(INDEX_KEY, { filter: value })
// AutoScroll
const countNow = fetchListNow.length + resourceListNow.length - intersectedCount
const [

View file

@ -38,7 +38,6 @@ function StackEventPanel() {
const onTabClick = (activeTab: typeof TAB_KEYS[number]) => devTools.update(INDEX_KEY, { activeTab })
const onFilterChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => devTools.update(INDEX_KEY, { filter: value })
const tabs = useMemo(() =>
TABS.filter(({ key }) => key === ALL || list.some(({ source }) => key === source)),
[ list.length ],

View file

@ -128,7 +128,7 @@ export default class SharePopup extends React.PureComponent {
</div>
)}
>
<div className="p-3 w-full">{trigger}</div>
{trigger}
</Popover>
);
}

View file

@ -59,7 +59,7 @@ const Popover = ({ children, render, placement, onOpen = () => {} }: Props) => {
{open && (
<FloatingFocusManager
context={context}
modal={false}
modal={true}
order={['reference', 'content']}
returnFocus={false}
>

View file

@ -4,3 +4,4 @@ export const DURATION_FILTER = "__$session-durationFilter$__"
export const SESSION_FILTER = "__$session-filter$__"
export const GLOBAL_DESTINATION_PATH = "__$global-destinationPath$__"
export const GLOBAL_HAS_NO_RECORDINGS = "__$global-hasNoRecordings$__"
export const SITE_ID_STORAGE_KEY = "__$user-siteId$__"

View file

@ -15,14 +15,12 @@ import {
createEdit,
createRemove,
createUpdate,
createSave,
saveType,
} from './funcTools/crud';
import { createRequestReducer } from './funcTools/request';
import { Map, List, fromJS } from "immutable";
import { GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys';
import { GLOBAL_HAS_NO_RECORDINGS, SITE_ID_STORAGE_KEY } from 'App/constants/storageKeys';
const SITE_ID_STORAGE_KEY = "__$user-siteId$__";
const storedSiteId = localStorage.getItem(SITE_ID_STORAGE_KEY);
const name = 'project';

View file

@ -1,3 +1,3 @@
<svg viewBox="0 0 29 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 29 19" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.7" d="M11.6355 2.9045C10.8963 1.75328 9.80334 0.873101 8.521 0.396241C7.23867 -0.0806184 5.83621 -0.128393 4.5244 0.260101C3.21259 0.648595 2.06231 1.45237 1.24645 2.55061C0.430585 3.64885 -0.00678382 4.98223 7.95597e-05 6.35034C0.00076002 7.48328 0.305669 8.59524 0.882952 9.57006C1.46024 10.5449 2.28871 11.3468 3.28182 11.892C4.27493 12.4372 5.39624 12.7058 6.52859 12.6695C7.66095 12.6333 8.76279 12.2937 9.71903 11.6861C9.22188 13.1623 8.29591 14.7372 6.77033 16.316C6.47844 16.6179 6.31846 17.0234 6.32558 17.4433C6.3327 17.8632 6.50633 18.2631 6.80828 18.555C7.11022 18.8469 7.51575 19.0069 7.93566 18.9997C8.35556 18.9926 8.75543 18.819 9.04731 18.517C14.6867 12.6728 13.9542 6.31998 11.6355 2.91209V2.9045ZM26.8154 2.9045C26.0762 1.75328 24.9833 0.873101 23.7009 0.396241C22.4186 -0.0806184 21.0161 -0.128393 19.7043 0.260101C18.3925 0.648595 17.2422 1.45237 16.4264 2.55061C15.6105 3.64885 15.1731 4.98223 15.18 6.35034C15.1807 7.48328 15.4856 8.59524 16.0629 9.57006C16.6402 10.5449 17.4686 11.3468 18.4617 11.892C19.4549 12.4372 20.5762 12.7058 21.7085 12.6695C22.8409 12.6333 23.9427 12.2937 24.899 11.6861C24.4018 13.1623 23.4758 14.7372 21.9503 16.316C21.6584 16.6179 21.4984 17.0234 21.5055 17.4433C21.5126 17.8632 21.6863 18.2631 21.9882 18.555C22.2901 18.8469 22.6957 19.0069 23.1156 18.9997C23.5355 18.9926 23.9354 18.819 24.2272 18.517C29.8666 12.6728 29.1342 6.31998 26.8154 2.91209V2.9045Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -19,9 +19,7 @@ version="v1.9.0"
usr=`whoami`
# Installing k3s
[[ x$SKIP_K8S_INSTALL == "xtrue" ]] && {
info "Skipping Kuberntes installation"
} || {
function install_k8s{
curl -sL https://get.k3s.io | sudo K3S_KUBECONFIG_MODE="644" INSTALL_K3S_VERSION='v1.22.8+k3s1' INSTALL_K3S_EXEC="--no-deploy=traefik" sh -
[[ -d ~/.kube ]] || mkdir ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
@ -29,42 +27,42 @@ usr=`whoami`
sudo chown -R $usr ~/.kube/config
}
[[ x$SKIP_K8S_TOOLS == "xtrue" ]] && {
info "Skipping Kuberntes installation"
} || {
function install_tools(){
## installing kubectl
which kubectl &> /dev/null || {
info "kubectl not installed. Installing it..."
sudo curl -SsL https://dl.k8s.io/release/v1.20.0/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl ; sudo chmod +x /usr/local/bin/kubectl
}
## Installing GH package manager
which eget &> /dev/null || {
local version="1.3.0"
info "eget not installed. Installing it..."
curl -SsL https://github.com/zyedidia/eget/releases/download/v$version/eget-1.3.0-linux_amd64.tar.gz -o /tmp/eget.tar.gz
cd /tmp
tar -xf eget.tar.gz
sudo mv eget-$version-linux_amd64/eget /usr/local/bin/eget
sudo chmod +x /usr/local/bin/eget
cd -
}
## installing stern
which stern &> /dev/null || {
info "stern not installed. installing..."
sudo curl -SsL https://github.com/derdanne/stern/releases/download/2.1.16/stern_linux_amd64 -o /usr/local/bin/stern ; sudo chmod +x /usr/local/bin/stern
sudo eget --to /usr/local/bin stern/stern
}
## installing k9s
which k9s &> /dev/null || {
info "k9s not installed. Installing it..."
sudo curl -SsL https://github.com/derailed/k9s/releases/download/v0.24.2/k9s_Linux_x86_64.tar.gz -o /tmp/k9s.tar.gz
cd /tmp
tar -xf k9s.tar.gz
sudo mv k9s /usr/local/bin/k9s
sudo chmod +x /usr/local/bin/k9s
cd -
sudo eget --to /usr/local/bin derailed/k9s
}
## installing helm
which helm &> /dev/null
if [[ $? -ne 0 ]]; then
which helm &> /dev/null || {
info "helm not installed. Installing it..."
curl -ssl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz -o /tmp/helm.tar.gz
tar -xf /tmp/helm.tar.gz
chmod +x linux-amd64/helm
sudo cp linux-amd64/helm /usr/local/bin/helm
rm -rf linux-amd64/helm /tmp/helm.tar.gz
fi
sudo eget --to /usr/local/bin https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz -f helm
}
}
sleep 10
@ -79,10 +77,6 @@ randomPass() {
## Prepping the infra
[[ -z $DOMAIN_NAME ]] && {
fatal 'DOMAIN_NAME variable is empty. Rerun the script `DOMAIN_NAME=openreplay.mycomp.org bash init.sh `'
}
# Mac os doesn't have gnu sed, which will cause compatibility issues.
# This wrapper will help to check the sed, and use the correct version="v1.9.0"
# Ref: https://stackoverflow.com/questions/37639496/how-can-i-check-the-version="v1.9.0"
@ -102,21 +96,52 @@ function sed_i_wrapper(){
fi
}
info "Creating dynamic passwords"
sed_i_wrapper -i "s/postgresqlPassword: \"changeMePassword\"/postgresqlPassword: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/accessKey: \"changeMeMinioAccessKey\"/accessKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/secretKey: \"changeMeMinioPassword\"/secretKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/jwt_secret: \"SetARandomStringHere\"/jwt_secret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistKey: \"SetARandomStringHere\"/assistKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistJWTSecret: \"SetARandomStringHere\"/assistJWTSecret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/domainName: \"\"/domainName: \"${DOMAIN_NAME}\"/g" vars.yaml
function create_passwords() {
[[ -z $DOMAIN_NAME ]] && {
fatal 'DOMAIN_NAME variable is empty. Rerun the script `DOMAIN_NAME=openreplay.mycomp.org bash init.sh `'
}
info "Setting proper permission for shared folder"
sudo mkdir -p /openreplay/storage/nfs
sudo chown -R 1001:1001 /openreplay/storage/nfs
info "Creating dynamic passwords"
sed_i_wrapper -i "s/postgresqlPassword: \"changeMePassword\"/postgresqlPassword: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/accessKey: \"changeMeMinioAccessKey\"/accessKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/secretKey: \"changeMeMinioPassword\"/secretKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/jwt_secret: \"SetARandomStringHere\"/jwt_secret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistKey: \"SetARandomStringHere\"/assistKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistJWTSecret: \"SetARandomStringHere\"/assistJWTSecret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/domainName: \"\"/domainName: \"${DOMAIN_NAME}\"/g" vars.yaml
}
function set_permissions() {
info "Setting proper permission for shared folder"
sudo mkdir -p /openreplay/storage/nfs
sudo chown -R 1001:1001 /openreplay/storage/nfs
}
## Installing OpenReplay
info "installing databases"
helm upgrade --install databases ./databases -n db --create-namespace --wait -f ./vars.yaml --atomic
info "installing application"
helm upgrade --install openreplay ./openreplay -n app --create-namespace --wait -f ./vars.yaml --atomic
function install_openreplay() {
info "installing databases"
helm upgrade --install databases ./databases -n db --create-namespace --wait -f ./vars.yaml --atomic
info "installing application"
helm upgrade --install openreplay ./openreplay -n app --create-namespace --wait -f ./vars.yaml --atomic
}
function main(){
[[ x$SKIP_K8S_INSTALL == "x1" ]] && {
info "Skipping Kuberntes installation"
} || {
install_k8s
}
[[ x$SKIP_K8S_TOOLS == "x1" ]] && {
info "Skipping Kuberntes tools installation"
} || {
install_tools
}
create_passwords
set_permissions
[[ x$SKIP_OR_INSTALL == "x1" ]] && {
info "Skipping OpenReplay installation"
} || {
install_openreplay
}
}