feat(backend): handle click selector & url for heatmaps

This commit is contained in:
ShiKhu 2021-07-21 21:17:36 +08:00
parent 03919c23af
commit c0824d34d4
8 changed files with 91 additions and 22 deletions

View file

@ -105,11 +105,16 @@ func (conn *Conn) InsertWebClickEvent(sessionID uint64, e *ClickEvent) error {
defer tx.rollback()
if err = tx.exec(`
INSERT INTO events.clicks
(session_id, message_id, timestamp, label)
(session_id, message_id, timestamp, label, selector, url)
(SELECT
$1, $2, $3, NULLIF($4, ''), $5, host || base_path
FROM events.pages
WHERE session_id = $1 AND timestamp <= $3 ORDER BY timestamp DESC LIMIT 1
)
VALUES
($1, $2, $3, NULLIF($4, ''))
($1, $2, $3, NULLIF($4, ''), $5, SELECT)
`,
sessionID, e.MessageID, e.Timestamp, e.Label,
sessionID, e.MessageID, e.Timestamp, e.Label, e.Selector,
); err != nil {
return err
}

View file

@ -18,6 +18,8 @@ func ReadBatch(b []byte, callback func(Message)) error {
} else if err != nil {
return errors.Wrapf(err, "Batch Message decoding error on message with index %v", index)
}
msg = transformDepricated(msg)
isBatchMeta := false
switch m := msg.(type){
case *BatchMeta: // Is not required to be present in batch since IOS doesn't have it (though we might change it)

View file

@ -1,3 +1,4 @@
// Auto-generated, do not edit
package messages

View file

@ -0,0 +1,29 @@
package messages
func transformDepricated(msg Message) Message {
switch m := msg.(type) {
case *MouseClickDepricated:
return &MouseClick {
ID: m.ID,
HesitationTime: m.HesitationTime,
Label: m.Label,
// Selector: '',
}
// case *FetchDepricated:
// return &Fetch {
// Method: m.Method,
// URL: m.URL,
// Request: m.Request,
// Response: m.Response,
// Status: m.Status,
// Timestamp: m.Timestamp,
// Duration: m.Duration,
// // Headers: ''
// }
default:
return msg
}
}

View file

@ -350,13 +350,13 @@ p = WriteUint(msg.Y, buf, p)
return buf[:p]
}
type MouseClick struct {
type MouseClickDepricated struct {
*meta
ID uint64
HesitationTime uint64
Label string
}
func (msg *MouseClick) Encode() []byte{
func (msg *MouseClickDepricated) Encode() []byte{
buf := make([]byte, 31 + len(msg.Label))
buf[0] = 21
p := 1
@ -582,15 +582,17 @@ type ClickEvent struct {
Timestamp uint64
HesitationTime uint64
Label string
Selector string
}
func (msg *ClickEvent) Encode() []byte{
buf := make([]byte, 41 + len(msg.Label))
buf := make([]byte, 51 + len(msg.Label)+ len(msg.Selector))
buf[0] = 33
p := 1
p = WriteUint(msg.MessageID, buf, p)
p = WriteUint(msg.Timestamp, buf, p)
p = WriteUint(msg.HesitationTime, buf, p)
p = WriteString(msg.Label, buf, p)
p = WriteString(msg.Selector, buf, p)
return buf[:p]
}
@ -1146,6 +1148,24 @@ p = WriteString(msg.BaseURL, buf, p)
return buf[:p]
}
type MouseClick struct {
*meta
ID uint64
HesitationTime uint64
Label string
Selector string
}
func (msg *MouseClick) Encode() []byte{
buf := make([]byte, 41 + len(msg.Label)+ len(msg.Selector))
buf[0] = 69
p := 1
p = WriteUint(msg.ID, buf, p)
p = WriteUint(msg.HesitationTime, buf, p)
p = WriteString(msg.Label, buf, p)
p = WriteString(msg.Selector, buf, p)
return buf[:p]
}
type IOSSessionStart struct {
*meta
Timestamp uint64

View file

@ -159,7 +159,7 @@ if msg.Y, err = ReadUint(reader); err != nil { return nil, err }
return msg, nil
case 21:
msg := &MouseClick{ meta: &meta{ TypeID: 21} }
msg := &MouseClickDepricated{ meta: &meta{ TypeID: 21} }
if msg.ID, err = ReadUint(reader); err != nil { return nil, err }
if msg.HesitationTime, err = ReadUint(reader); err != nil { return nil, err }
if msg.Label, err = ReadString(reader); err != nil { return nil, err }
@ -265,6 +265,7 @@ if msg.Label, err = ReadString(reader); err != nil { return nil, err }
if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err }
if msg.HesitationTime, err = ReadUint(reader); err != nil { return nil, err }
if msg.Label, err = ReadString(reader); err != nil { return nil, err }
if msg.Selector, err = ReadString(reader); err != nil { return nil, err }
return msg, nil
case 34:
@ -512,6 +513,14 @@ if msg.Index, err = ReadUint(reader); err != nil { return nil, err }
if msg.BaseURL, err = ReadString(reader); err != nil { return nil, err }
return msg, nil
case 69:
msg := &MouseClick{ meta: &meta{ TypeID: 69} }
if msg.ID, err = ReadUint(reader); err != nil { return nil, err }
if msg.HesitationTime, err = ReadUint(reader); err != nil { return nil, err }
if msg.Label, err = ReadString(reader); err != nil { return nil, err }
if msg.Selector, err = ReadString(reader); err != nil { return nil, err }
return msg, nil
case 90:
msg := &IOSSessionStart{ meta: &meta{ TypeID: 90} }
if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err }

View file

@ -187,6 +187,7 @@ func (b *builder) handleMessage(message Message, messageID uint64) {
Label: msg.Label,
HesitationTime: msg.HesitationTime,
Timestamp: b.timestamp,
Selector: msg.Selector,
})
}
case *JSException:

View file

@ -61,62 +61,64 @@ func main() {
Addr: ":" + HTTP_PORT,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO: agree with specification
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
// TODO: agree with specification
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
if r.Method == http.MethodOptions {
w.Header().Set("Cache-Control", "max-age=86400")
w.WriteHeader(http.StatusOK)
return
}
switch r.URL.Path {
case "/":
w.WriteHeader(http.StatusOK)
case "/v1/web/not-started":
switch r.Method {
case "POST":
case http.MethodPost:
notStartedHandler(w, r)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
case "/v1/web/start":
switch r.Method {
case "POST":
case http.MethodPost:
startSessionHandlerWeb(w, r)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
case "/v1/web/i":
switch r.Method {
case "POST":
case http.MethodPost:
pushMessagesSeparatelyHandler(w, r)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
// case "/v1/ios/start":
// switch r.Method {
// case "POST":
// case http.MethodPost:
// startSessionHandlerIOS(w, r)
// default:
// w.WriteHeader(http.StatusMethodNotAllowed)
// }
// case "/v1/ios/append":
// switch r.Method {
// case "POST":
// case http.MethodPost:
// pushMessagesHandler(w, r)
// default:
// w.WriteHeader(http.StatusMethodNotAllowed)
// }
// case "/v1/ios/late":
// switch r.Method {
// case "POST":
// case http.MethodPost:
// pushLateMessagesHandler(w, r)
// default:
// w.WriteHeader(http.StatusMethodNotAllowed)
// }
// case "/v1/ios/images":
// switch r.Method {
// case "POST":
// case http.MethodPost:
// iosImagesUploadHandler(w, r)
// default:
// w.WriteHeader(http.StatusMethodNotAllowed)