From c4352fa70fc7362091555d4caae11abc98d89d98 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 10 Apr 2017 17:07:36 +0100 Subject: [PATCH] Implement /sync API response struct --- .../dendrite/clientapi/sync/requestpool.go | 24 +++- .../clientapi/sync/syncapi/syncapi.go | 108 ++++++++++++++++++ 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go b/src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go index 90b142b22..399f30dcc 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go +++ b/src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go @@ -138,12 +138,12 @@ func (rp *RequestPool) waitForEvents(req syncRequest) syncapi.StreamPosition { return currentPos } -func (rp *RequestPool) currentSyncForUser(req syncRequest) ([]gomatrixserverlib.Event, error) { +func (rp *RequestPool) currentSyncForUser(req syncRequest) (*syncapi.Response, error) { currentPos := rp.waitForEvents(req) - // TODO: ignored users + // TODO: handle ignored users - // Implement https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L821 + // TODO: Implement https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L821 // 1) Get the CURRENT joined room list for this user // 2) Get membership list changes for this user between the provided stream position and now. // 3) For each room which has membership list changes: @@ -153,7 +153,23 @@ func (rp *RequestPool) currentSyncForUser(req syncRequest) ([]gomatrixserverlib. // c) Check if the user is CURRENTLY left/banned. If so, add room to 'archived' block. // Synapse has a TODO: How do we handle ban -> leave in same batch? // 4) Add joined rooms (joined room list) - return rp.db.EventsInRange(int64(req.since), int64(currentPos)) + events, err := rp.db.EventsInRange(int64(req.since), int64(currentPos)) + if err != nil { + return nil, err + } + + res := syncapi.NewResponse() + // for now, dump everything as join timeline events + for _, ev := range events { + roomData := res.Rooms.Join[ev.RoomID()] + roomData.Timeline.Events = append(roomData.Timeline.Events, ev) + res.Rooms.Join[ev.RoomID()] = roomData + } + + // Make sure we send the next_batch as a string. We don't want to confuse clients by sending this + // as an integer even though (at the moment) it is. + res.NextBatch = currentPos.String() + return res, nil } func getTimeout(timeoutMS string) time.Duration { diff --git a/src/github.com/matrix-org/dendrite/clientapi/sync/syncapi/syncapi.go b/src/github.com/matrix-org/dendrite/clientapi/sync/syncapi/syncapi.go index 6dd0a9176..24603c039 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/sync/syncapi/syncapi.go +++ b/src/github.com/matrix-org/dendrite/clientapi/sync/syncapi/syncapi.go @@ -1,4 +1,112 @@ package syncapi +import ( + "strconv" + + "github.com/matrix-org/gomatrixserverlib" +) + // StreamPosition represents the offset in the sync stream a client is at. type StreamPosition int64 + +// String implements the Stringer interface. +func (sp StreamPosition) String() string { + return strconv.FormatInt(int64(sp), 10) +} + +// Response represents a /sync API response. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync +type Response struct { + NextBatch string `json:"next_batch"` + AccountData struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"account_data"` + Presence struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"presence"` + Rooms struct { + Join map[string]JoinResponse `json:"join"` + Invite map[string]InviteResponse `json:"invite"` + Leave map[string]LeaveResponse `json:"leave"` + } `json:"rooms"` +} + +// NewResponse creates an empty response with initialised maps. +func NewResponse() *Response { + res := Response{} + // Pre-initalise the maps. Synapse will return {} even if there are no rooms under a specific section, + // so let's do the same thing. Bonus: this means we can't get dreaded 'assignment to entry in nil map' errors. + res.Rooms.Join = make(map[string]JoinResponse) + res.Rooms.Invite = make(map[string]InviteResponse) + res.Rooms.Leave = make(map[string]LeaveResponse) + + // Also pre-intialise empty slices or else we'll insert 'null' instead of '[]' for the value. + // TODO: We really shouldn't have to do all this to coerce encoding/json to Do The Right Thing. We should + // really be using our own Marshal/Unmarshal implementations otherwise this may prove to be a CPU bottleneck. + // This also applies to NewJoinResponse, NewInviteResponse and NewLeaveResponse. + res.AccountData.Events = make([]gomatrixserverlib.Event, 0) + res.Presence.Events = make([]gomatrixserverlib.Event, 0) + + return &res +} + +// JoinResponse represents a /sync response for a room which is under the 'join' key. +type JoinResponse struct { + State struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"state"` + Timeline struct { + Events []gomatrixserverlib.Event `json:"events"` + Limited bool `json:"limited"` + PrevBatch string `json:"prev_batch"` + } `json:"timeline"` + Ephemeral struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"ephemeral"` + AccountData struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"account_data"` +} + +// NewJoinResponse creates an empty response with initialised arrays. +func NewJoinResponse() *JoinResponse { + res := JoinResponse{} + res.State.Events = make([]gomatrixserverlib.Event, 0) + res.Timeline.Events = make([]gomatrixserverlib.Event, 0) + res.Ephemeral.Events = make([]gomatrixserverlib.Event, 0) + res.AccountData.Events = make([]gomatrixserverlib.Event, 0) + return &res +} + +// InviteResponse represents a /sync response for a room which is under the 'invite' key. +type InviteResponse struct { + InviteState struct { + Events []gomatrixserverlib.Event + } `json:"invite_state"` +} + +// NewInviteResponse creates an empty response with initialised arrays. +func NewInviteResponse() *InviteResponse { + res := InviteResponse{} + res.InviteState.Events = make([]gomatrixserverlib.Event, 0) + return &res +} + +// LeaveResponse represents a /sync response for a room which is under the 'leave' key. +type LeaveResponse struct { + State struct { + Events []gomatrixserverlib.Event `json:"events"` + } `json:"state"` + Timeline struct { + Events []gomatrixserverlib.Event `json:"events"` + Limited bool `json:"limited"` + PrevBatch string `json:"prev_batch"` + } `json:"timeline"` +} + +// NewLeaveResponse creates an empty response with initialised arrays. +func NewLeaveResponse() *LeaveResponse { + res := LeaveResponse{} + res.State.Events = make([]gomatrixserverlib.Event, 0) + res.Timeline.Events = make([]gomatrixserverlib.Event, 0) + return &res +}