diff --git a/src/github.com/matrix-org/dendrite/roomserver/query/query.go b/src/github.com/matrix-org/dendrite/roomserver/query/query.go index 84f5d44c3..1d0a20f6e 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/query/query.go +++ b/src/github.com/matrix-org/dendrite/roomserver/query/query.go @@ -40,6 +40,9 @@ type RoomserverQueryAPIDatabase interface { // Lookup the numeric IDs for a list of events. // Returns an error if there was a problem talking to the database. EventNIDs(eventIDs []string) (map[string]types.EventNID, error) + // Lookup the event IDs for a batch of event numeric IDs. + // Returns an error if the retrieval went wrong. + EventIDs(eventNIDs []types.EventNID) (map[types.EventNID]string, error) // Save a given room alias with the room ID it refers to. // Returns an error if there was a problem talking to the database. SetRoomAlias(alias string, roomID string) error @@ -52,13 +55,16 @@ type RoomserverQueryAPIDatabase interface { // Remove a given room alias. // Returns an error if there was a problem talking to the database. RemoveRoomAlias(alias string) error - // Lookup the join events for all members in a room as requested by a given - // user. If the user is currently in the room, returns the room's current - // members, if not returns an empty array (TODO: Fix it) - // If the user requesting the list of members has never been in the room, - // returns nil. - // If there was an issue retrieving the events, returns an error. - GetMembershipEvents(roomNID types.RoomNID, requestSenderUserID string) (events []types.Event, err error) + // Lookup the membership of a given user in a given room. + // Returns the numeric ID of the latest membership event sent from this user + // in this room, along a boolean set to true if the user is still in this room, + // false if not. + // Returns an error if there was a problem talking to the database. + GetMembership(roomNID types.RoomNID, requestSenderUserID string) (membershipEventNID types.EventNID, stillInRoom bool, err error) + // Lookup the "join" membership event numeric IDs for all user that are + // currently members of a given room. + // Returns an error if there was a problem talking to the database. + GetJoinMembershipEventNIDsForRoom(roomNID types.RoomNID) ([]types.EventNID, error) } // RoomserverQueryAPI is an implementation of api.RoomserverQueryAPI @@ -199,12 +205,12 @@ func (r *RoomserverQueryAPI) QueryMembershipsForRoom( return err } - events, err := r.DB.GetMembershipEvents(roomNID, request.Sender) + membershipEventNID, stillInRoom, err := r.DB.GetMembership(roomNID, request.Sender) if err != nil { return nil } - if events == nil { + if membershipEventNID == 0 { response.HasBeenInRoom = false response.JoinEvents = nil return nil @@ -212,6 +218,24 @@ func (r *RoomserverQueryAPI) QueryMembershipsForRoom( response.HasBeenInRoom = true response.JoinEvents = []gomatrixserverlib.ClientEvent{} + + var events []types.Event + if stillInRoom { + var eventNIDs []types.EventNID + eventNIDs, err = r.DB.GetJoinMembershipEventNIDsForRoom(roomNID) + if err != nil { + return err + } + + events, err = r.DB.Events(eventNIDs) + } else { + events, err = r.getMembershipsBeforeEventNID(membershipEventNID) + } + + if err != nil { + return err + } + for _, event := range events { clientEvent := gomatrixserverlib.ToClientEvent(event.Event, gomatrixserverlib.FormatAll) response.JoinEvents = append(response.JoinEvents, clientEvent) @@ -220,6 +244,59 @@ func (r *RoomserverQueryAPI) QueryMembershipsForRoom( return nil } +// getMembershipsBeforeEventNID takes the numeric ID of an event and fetches the state +// of the event's room as it was when this event was fired, then filters the state events to +// only keep the "m.room.member" events with a "join" membership. These events are returned. +// Returns an error if there was an issue fetching the events. +func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(eventNID types.EventNID) ([]types.Event, error) { + events := []types.Event{} + // Lookup the event NID + eIDs, err := r.DB.EventIDs([]types.EventNID{eventNID}) + if err != nil { + return nil, err + } + eventIDs := []string{eIDs[eventNID]} + + prevState, err := r.DB.StateAtEventIDs(eventIDs) + if err != nil { + return nil, err + } + + // Fetch the state as it was when this event was fired + stateEntries, err := state.LoadCombinedStateAfterEvents(r.DB, prevState) + if err != nil { + return nil, err + } + + var eventNIDs []types.EventNID + for _, entry := range stateEntries { + // Filter the events to retrieve to only keep the membership events + if entry.EventStateKeyNID == types.MRoomMemberNID { + eventNIDs = append(eventNIDs, entry.EventNID) + } + } + + // Get all of the events in this state + stateEvents, err := r.DB.Events(eventNIDs) + if err != nil { + return nil, err + } + + // Filter the events to only keep the "join" membership events + for _, event := range stateEvents { + membership, err := event.Membership() + if err != nil { + return nil, err + } + + if membership == "join" { + events = append(events, event) + } + } + + return events, nil +} + // SetupHTTP adds the RoomserverQueryAPI handlers to the http.ServeMux. func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) { servMux.Handle( diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go index 1e696c36a..935b54440 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go @@ -19,7 +19,6 @@ import ( // Import the postgres database driver. _ "github.com/lib/pq" - "github.com/matrix-org/dendrite/roomserver/state" "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -509,8 +508,8 @@ func (u *membershipUpdater) SetToLeave(senderUserID string, eventID string) ([]s return inviteEventIDs, nil } -// GetMembershipEvents implements query.RoomserverQueryAPIDB -func (d *Database) GetMembershipEvents(roomNID types.RoomNID, requestSenderUserID string) (events []types.Event, err error) { +// GetMembership implements query.RoomserverQueryAPIDB +func (d *Database) GetMembership(roomNID types.RoomNID, requestSenderUserID string) (membershipEventNID types.EventNID, stillInRoom bool, err error) { txn, err := d.db.Begin() if err != nil { return @@ -525,79 +524,17 @@ func (d *Database) GetMembershipEvents(roomNID types.RoomNID, requestSenderUserI senderMembershipEventNID, senderMembership, err := d.statements.selectMembershipFromRoomAndTarget(roomNID, requestSenderUserNID) if err == sql.ErrNoRows { // The user has never been a member of that room - return nil, nil + return 0, false, nil } else if err != nil { return } - if senderMembership == membershipStateJoin { - // The user is still in the room: Send the current list of joined members - var joinEventNIDs []types.EventNID - joinEventNIDs, err = d.statements.selectMembershipsFromRoomAndMembership(roomNID, membershipStateJoin) - if err != nil { - return nil, err - } - - events, err = d.Events(joinEventNIDs) - } else { - // The user isn't in the room anymore: Send the list of joined user from - // when the user left - events, err = d.getMembershipsBeforeEventNID(senderMembershipEventNID) - } - - return + return senderMembershipEventNID, senderMembership == membershipStateJoin, nil } -// getMembershipsBeforeEventNID takes the numeric ID of an event and fetches the state -// of the event's room as it was when this event was fired, then filters the state events to -// only keep the "m.room.member" events with a "join" membership. These events are returned. -// Returns an error if there was an issue fetching the events. -func (d *Database) getMembershipsBeforeEventNID(eventNID types.EventNID) ([]types.Event, error) { - events := []types.Event{} - // Lookup the event NID - eIDs, err := d.EventIDs([]types.EventNID{eventNID}) - if err != nil { - return nil, err - } - eventIDs := []string{eIDs[eventNID]} - - prevState, err := d.StateAtEventIDs(eventIDs) - if err != nil { - return nil, err - } - - // Fetch the state as it was when this event was fired - stateEntries, err := state.LoadCombinedStateAfterEvents(d, prevState) - if err != nil { - return nil, err - } - - var eventNIDs []types.EventNID - for _, entry := range stateEntries { - if entry.EventStateKeyNID == types.MRoomMemberNID { - eventNIDs = append(eventNIDs, entry.EventNID) - } - } - - // Get all of the events in this state - stateEvents, err := d.Events(eventNIDs) - if err != nil { - return nil, err - } - - // Filter the events to only keep the "join" membership events - for _, event := range stateEvents { - membership, err := event.Membership() - if err != nil { - return nil, err - } - - if membership == "join" { - events = append(events, event) - } - } - - return events, nil +// GetJoinMembershipEventNIDsForRoom implements query.RoomserverQueryAPIDB +func (d *Database) GetJoinMembershipEventNIDsForRoom(roomNID types.RoomNID) ([]types.EventNID, error) { + return d.statements.selectMembershipsFromRoomAndMembership(roomNID, membershipStateJoin) } type transaction struct {