2021-10-05 16:22:02 +01:00
|
|
|
package sync3
|
2021-09-20 16:53:41 +01:00
|
|
|
|
2021-10-25 18:03:32 +01:00
|
|
|
import (
|
|
|
|
"encoding/json"
|
2022-01-04 15:32:37 +00:00
|
|
|
"strconv"
|
2021-10-25 18:03:32 +01:00
|
|
|
|
2022-12-15 11:08:50 +00:00
|
|
|
"github.com/matrix-org/sliding-sync/sync3/extensions"
|
2021-10-25 18:03:32 +01:00
|
|
|
"github.com/tidwall/gjson"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
OpSync = "SYNC"
|
|
|
|
OpInvalidate = "INVALIDATE"
|
|
|
|
OpInsert = "INSERT"
|
|
|
|
OpDelete = "DELETE"
|
|
|
|
)
|
|
|
|
|
2021-09-20 16:53:41 +01:00
|
|
|
type Response struct {
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists map[string]ResponseList `json:"lists"`
|
2021-09-24 12:25:28 +01:00
|
|
|
|
2022-05-25 11:36:30 +01:00
|
|
|
Rooms map[string]Room `json:"rooms"`
|
2021-12-13 18:22:41 +00:00
|
|
|
Extensions extensions.Response `json:"extensions"`
|
2021-12-13 15:27:02 +00:00
|
|
|
|
2023-07-31 10:24:36 +01:00
|
|
|
Pos string `json:"pos"`
|
|
|
|
TxnID string `json:"txn_id,omitempty"`
|
2021-09-20 16:53:41 +01:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:36:30 +01:00
|
|
|
type ResponseList struct {
|
2022-05-27 15:23:31 +01:00
|
|
|
Ops []ResponseOp `json:"ops,omitempty"`
|
2022-05-25 11:36:30 +01:00
|
|
|
Count int `json:"count"`
|
|
|
|
}
|
|
|
|
|
2022-01-04 15:32:37 +00:00
|
|
|
func (r *Response) PosInt() int64 {
|
|
|
|
p, _ := strconv.ParseInt(r.Pos, 10, 64)
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2022-05-25 11:36:30 +01:00
|
|
|
func (r *Response) ListOps() int {
|
|
|
|
num := 0
|
|
|
|
for _, l := range r.Lists {
|
|
|
|
if len(l.Ops) > 0 {
|
|
|
|
num += len(l.Ops)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num
|
|
|
|
}
|
|
|
|
|
2023-02-08 16:33:52 +00:00
|
|
|
func (r *Response) RoomIDsToTimelineEventIDs() map[string][]string {
|
|
|
|
includedRoomIDs := make(map[string][]string)
|
|
|
|
for roomID := range r.Rooms {
|
|
|
|
eventIDs := make([]string, len(r.Rooms[roomID].Timeline))
|
|
|
|
for i := range eventIDs {
|
|
|
|
eventIDs[i] = gjson.ParseBytes(r.Rooms[roomID].Timeline[i]).Get("event_id").Str
|
|
|
|
}
|
|
|
|
includedRoomIDs[roomID] = eventIDs
|
|
|
|
}
|
|
|
|
return includedRoomIDs
|
|
|
|
}
|
|
|
|
|
2021-10-25 18:03:32 +01:00
|
|
|
// Custom unmarshal so we can dynamically create the right ResponseOp for Ops
|
|
|
|
func (r *Response) UnmarshalJSON(b []byte) error {
|
|
|
|
temporary := struct {
|
2022-05-25 11:36:30 +01:00
|
|
|
Rooms map[string]Room `json:"rooms"`
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists map[string]struct {
|
2022-05-25 11:36:30 +01:00
|
|
|
Ops []json.RawMessage `json:"ops"`
|
|
|
|
Count int `json:"count"`
|
|
|
|
} `json:"lists"`
|
2022-05-24 10:35:23 +01:00
|
|
|
Extensions extensions.Response `json:"extensions"`
|
2021-10-25 18:03:32 +01:00
|
|
|
|
2023-07-31 10:24:36 +01:00
|
|
|
Pos string `json:"pos"`
|
|
|
|
TxnID string `json:"txn_id,omitempty"`
|
2021-10-25 18:03:32 +01:00
|
|
|
}{}
|
|
|
|
if err := json.Unmarshal(b, &temporary); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-05-24 10:35:23 +01:00
|
|
|
r.Rooms = temporary.Rooms
|
2021-10-25 18:03:32 +01:00
|
|
|
r.Pos = temporary.Pos
|
2022-08-03 15:33:56 +01:00
|
|
|
r.TxnID = temporary.TxnID
|
2021-12-14 11:51:47 +00:00
|
|
|
r.Extensions = temporary.Extensions
|
2022-12-20 13:32:39 +00:00
|
|
|
r.Lists = make(map[string]ResponseList, len(temporary.Lists))
|
2022-05-25 11:36:30 +01:00
|
|
|
|
2022-12-20 13:32:39 +00:00
|
|
|
for listKey, l := range temporary.Lists {
|
2022-05-25 11:36:30 +01:00
|
|
|
var list ResponseList
|
|
|
|
list.Count = l.Count
|
|
|
|
for _, op := range l.Ops {
|
|
|
|
if gjson.GetBytes(op, "range").Exists() {
|
|
|
|
var oper ResponseOpRange
|
|
|
|
if err := json.Unmarshal(op, &oper); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
list.Ops = append(list.Ops, &oper)
|
|
|
|
} else {
|
|
|
|
var oper ResponseOpSingle
|
|
|
|
if err := json.Unmarshal(op, &oper); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
list.Ops = append(list.Ops, &oper)
|
2021-10-25 18:03:32 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-20 13:32:39 +00:00
|
|
|
r.Lists[listKey] = list
|
2021-10-25 18:03:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-20 16:53:41 +01:00
|
|
|
type ResponseOp interface {
|
|
|
|
Op() string
|
2022-03-24 19:38:55 +00:00
|
|
|
// which rooms are we giving data about
|
|
|
|
IncludedRoomIDs() []string
|
|
|
|
}
|
|
|
|
|
2021-09-20 16:53:41 +01:00
|
|
|
type ResponseOpRange struct {
|
2022-05-25 12:52:56 +01:00
|
|
|
Operation string `json:"op"`
|
2023-04-11 12:56:35 +01:00
|
|
|
Range [2]int64 `json:"range,omitempty"`
|
2022-05-25 12:52:56 +01:00
|
|
|
RoomIDs []string `json:"room_ids,omitempty"`
|
2021-09-20 16:53:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *ResponseOpRange) Op() string {
|
|
|
|
return r.Operation
|
|
|
|
}
|
2022-03-24 19:38:55 +00:00
|
|
|
func (r *ResponseOpRange) IncludedRoomIDs() []string {
|
|
|
|
if r.Op() == OpInvalidate {
|
|
|
|
return nil // the rooms are being excluded
|
|
|
|
}
|
2022-05-27 09:33:25 +01:00
|
|
|
return r.RoomIDs
|
2022-03-24 19:38:55 +00:00
|
|
|
}
|
2021-09-20 16:53:41 +01:00
|
|
|
|
|
|
|
type ResponseOpSingle struct {
|
|
|
|
Operation string `json:"op"`
|
|
|
|
Index *int `json:"index,omitempty"` // 0 is a valid value, hence *int
|
2022-05-25 12:52:56 +01:00
|
|
|
RoomID string `json:"room_id,omitempty"`
|
2021-09-20 16:53:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *ResponseOpSingle) Op() string {
|
|
|
|
return r.Operation
|
|
|
|
}
|
2022-03-24 19:38:55 +00:00
|
|
|
|
|
|
|
func (r *ResponseOpSingle) IncludedRoomIDs() []string {
|
2022-05-27 09:33:25 +01:00
|
|
|
if r.Op() == OpDelete || r.RoomID == "" {
|
2022-03-24 19:38:55 +00:00
|
|
|
return nil // the room is being excluded
|
|
|
|
}
|
2022-05-27 09:33:25 +01:00
|
|
|
return []string{r.RoomID}
|
2022-03-24 19:38:55 +00:00
|
|
|
}
|