mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-10 16:33:11 -06:00
Membership events retrieval + update on leave
This commit is contained in:
parent
68aa5bce29
commit
f03bb7cf39
|
|
@ -197,7 +197,7 @@ func updateToLeaveMembership(
|
|||
// are active for that user. We notify the consumers that the invites have
|
||||
// been retired using a special event, even though they could infer this
|
||||
// by studying the state changes in the room event stream.
|
||||
retired, err := mu.SetToLeave(add.Sender())
|
||||
retired, err := mu.SetToLeave(add.Sender(), add.EventID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,10 +58,22 @@ const bulkSelectEventStateKeyNIDSQL = "" +
|
|||
"SELECT event_state_key, event_state_key_nid FROM roomserver_event_state_keys" +
|
||||
" WHERE event_state_key = ANY($1)"
|
||||
|
||||
const selectEventStateKeySQL = "" +
|
||||
"SELECT event_state_key FROM roomserver_event_state_keys" +
|
||||
" WHERE event_state_key_nid = $1"
|
||||
|
||||
// Bulk lookup from numeric ID to string state key for that state key.
|
||||
// Takes an array of strings as the query parameter.
|
||||
const bulkSelectEventStateKeySQL = "" +
|
||||
"SELECT event_state_key, event_state_key_nid FROM roomserver_event_state_keys" +
|
||||
" WHERE event_state_key_nid = ANY($1)"
|
||||
|
||||
type eventStateKeyStatements struct {
|
||||
insertEventStateKeyNIDStmt *sql.Stmt
|
||||
selectEventStateKeyNIDStmt *sql.Stmt
|
||||
selectEventStateKeyStmt *sql.Stmt
|
||||
bulkSelectEventStateKeyNIDStmt *sql.Stmt
|
||||
bulkSelectEventStateKeyStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (s *eventStateKeyStatements) prepare(db *sql.DB) (err error) {
|
||||
|
|
@ -72,7 +84,9 @@ func (s *eventStateKeyStatements) prepare(db *sql.DB) (err error) {
|
|||
return statementList{
|
||||
{&s.insertEventStateKeyNIDStmt, insertEventStateKeyNIDSQL},
|
||||
{&s.selectEventStateKeyNIDStmt, selectEventStateKeyNIDSQL},
|
||||
{&s.selectEventStateKeyStmt, selectEventStateKeySQL},
|
||||
{&s.bulkSelectEventStateKeyNIDStmt, bulkSelectEventStateKeyNIDSQL},
|
||||
{&s.bulkSelectEventStateKeyStmt, bulkSelectEventStateKeySQL},
|
||||
}.prepare(db)
|
||||
}
|
||||
|
||||
|
|
@ -114,3 +128,36 @@ func (s *eventStateKeyStatements) bulkSelectEventStateKeyNID(eventStateKeys []st
|
|||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *eventStateKeyStatements) selectEventStateKey(txn *sql.Tx, eventStateKeyNID types.EventStateKeyNID) (string, error) {
|
||||
var eventStateKey string
|
||||
stmt := s.selectEventStateKeyStmt
|
||||
if txn != nil {
|
||||
stmt = txn.Stmt(stmt)
|
||||
}
|
||||
err := stmt.QueryRow(eventStateKeyNID).Scan(&eventStateKey)
|
||||
return eventStateKey, err
|
||||
}
|
||||
|
||||
func (s *eventStateKeyStatements) bulkSelectEventStateKey(eventStateKeyNIDs []types.EventStateKeyNID) (map[types.EventStateKeyNID]string, error) {
|
||||
var nIDs pq.Int64Array
|
||||
for i := range eventStateKeyNIDs {
|
||||
nIDs[i] = int64(eventStateKeyNIDs[i])
|
||||
}
|
||||
rows, err := s.bulkSelectEventStateKeyStmt.Query(nIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make(map[types.EventStateKeyNID]string, len(eventStateKeyNIDs))
|
||||
for rows.Next() {
|
||||
var stateKey string
|
||||
var stateKeyNID int64
|
||||
if err := rows.Scan(&stateKey, &stateKeyNID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[types.EventStateKeyNID(stateKeyNID)] = stateKey
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,11 +46,16 @@ CREATE TABLE IF NOT EXISTS roomserver_membership (
|
|||
-- The state the user is in within this room.
|
||||
-- Default value is "membershipStateLeaveOrBan"
|
||||
membership_nid BIGINT NOT NULL DEFAULT 1,
|
||||
-- The ID of the "join" membership event.
|
||||
-- This ID is updated if the join event gets updated (e.g. profile update).
|
||||
-- This column is set to NULL if the user hasn't joined the room yet, e.g.
|
||||
-- if the user was invited but hasn't joined yet.
|
||||
event_id TEXT,
|
||||
-- The numeric ID of the membership event.
|
||||
-- It refers to the join membership event if the membership_nid is join (3),
|
||||
-- and to the leave/ban membership event if the membership_nid is leave or
|
||||
-- ban (1).
|
||||
-- If the membership_nid is invite (2) and the user has been in the room
|
||||
-- before, it will refer to the previous leave/ban membership event, and will
|
||||
-- be equals to 0 (its default) if the user never joined the room before.
|
||||
-- This NID is updated if the join event gets updated (e.g. profile update),
|
||||
-- or if the user leaves/joins the room.
|
||||
event_nid BIGINT NOT NULL DEFAULT 0,
|
||||
UNIQUE (room_nid, target_nid)
|
||||
);
|
||||
`
|
||||
|
|
@ -62,18 +67,33 @@ const insertMembershipSQL = "" +
|
|||
" VALUES ($1, $2)" +
|
||||
" ON CONFLICT DO NOTHING"
|
||||
|
||||
const selectMembershipFromRoomAndTargetSQL = "" +
|
||||
"SELECT membership_nid, event_nid FROM roomserver_membership" +
|
||||
" WHERE room_nid = $1 AND target_nid = $2"
|
||||
|
||||
const selectMembershipsFromRoomAndMembershipSQL = "" +
|
||||
"SELECT event_nid FROM roomserver_membership" +
|
||||
" WHERE room_nid = $1 AND membership_nid = $2"
|
||||
|
||||
const selectMembershipsFromRoomSQL = "" +
|
||||
"SELECT membership_nid, event_nid FROM roomserver_membership" +
|
||||
" WHERE room_nid = $1"
|
||||
|
||||
const selectMembershipForUpdateSQL = "" +
|
||||
"SELECT membership_nid FROM roomserver_membership" +
|
||||
" WHERE room_nid = $1 AND target_nid = $2 FOR UPDATE"
|
||||
|
||||
const updateMembershipSQL = "" +
|
||||
"UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4, event_id = $5" +
|
||||
"UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4, event_nid = $5" +
|
||||
" WHERE room_nid = $1 AND target_nid = $2"
|
||||
|
||||
type membershipStatements struct {
|
||||
insertMembershipStmt *sql.Stmt
|
||||
selectMembershipForUpdateStmt *sql.Stmt
|
||||
updateMembershipStmt *sql.Stmt
|
||||
insertMembershipStmt *sql.Stmt
|
||||
selectMembershipForUpdateStmt *sql.Stmt
|
||||
selectMembershipFromRoomAndTargetStmt *sql.Stmt
|
||||
selectMembershipsFromRoomAndMembershipStmt *sql.Stmt
|
||||
selectMembershipsFromRoomStmt *sql.Stmt
|
||||
updateMembershipStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (s *membershipStatements) prepare(db *sql.DB) (err error) {
|
||||
|
|
@ -85,6 +105,9 @@ func (s *membershipStatements) prepare(db *sql.DB) (err error) {
|
|||
return statementList{
|
||||
{&s.insertMembershipStmt, insertMembershipSQL},
|
||||
{&s.selectMembershipForUpdateStmt, selectMembershipForUpdateSQL},
|
||||
{&s.selectMembershipFromRoomAndTargetStmt, selectMembershipFromRoomAndTargetSQL},
|
||||
{&s.selectMembershipsFromRoomAndMembershipStmt, selectMembershipsFromRoomAndMembershipSQL},
|
||||
{&s.selectMembershipsFromRoomStmt, selectMembershipsFromRoomSQL},
|
||||
{&s.updateMembershipStmt, updateMembershipSQL},
|
||||
}.prepare(db)
|
||||
}
|
||||
|
|
@ -105,17 +128,59 @@ func (s *membershipStatements) selectMembershipForUpdate(
|
|||
return
|
||||
}
|
||||
|
||||
func (s *membershipStatements) selectMembershipFromRoomAndTarget(
|
||||
roomNID types.RoomNID, targetUserNID types.EventStateKeyNID,
|
||||
) (eventNID types.EventNID, membership membershipState, err error) {
|
||||
err = s.selectMembershipFromRoomAndTargetStmt.QueryRow(
|
||||
roomNID, targetUserNID,
|
||||
).Scan(&membership, &eventNID)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *membershipStatements) selectMembershipsFromRoom(
|
||||
roomNID types.RoomNID,
|
||||
) (eventNIDs map[types.EventNID]membershipState, err error) {
|
||||
rows, err := s.selectMembershipsFromRoomStmt.Query(roomNID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
eventNIDs = make(map[types.EventNID]membershipState)
|
||||
for rows.Next() {
|
||||
var eNID types.EventNID
|
||||
var membership membershipState
|
||||
if err = rows.Scan(&membership, &eNID); err != nil {
|
||||
return
|
||||
}
|
||||
eventNIDs[eNID] = membership
|
||||
}
|
||||
return
|
||||
}
|
||||
func (s *membershipStatements) selectMembershipsFromRoomAndMembership(
|
||||
roomNID types.RoomNID, membership membershipState,
|
||||
) (eventNIDs []types.EventNID, err error) {
|
||||
rows, err := s.selectMembershipsFromRoomAndMembershipStmt.Query(roomNID, membership)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var eNID types.EventNID
|
||||
if err = rows.Scan(&eNID); err != nil {
|
||||
return
|
||||
}
|
||||
eventNIDs = append(eventNIDs, eNID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *membershipStatements) updateMembership(
|
||||
txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID,
|
||||
senderUserNID types.EventStateKeyNID, membership membershipState,
|
||||
eventID string,
|
||||
eventNID types.EventNID,
|
||||
) error {
|
||||
eID := sql.NullString{
|
||||
String: eventID,
|
||||
Valid: len(eventID) > 0,
|
||||
}
|
||||
_, err := txn.Stmt(s.updateMembershipStmt).Exec(
|
||||
roomNID, targetUserNID, senderUserNID, membership, eID,
|
||||
roomNID, targetUserNID, senderUserNID, membership, eventNID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ func (u *membershipUpdater) SetToInvite(event gomatrixserverlib.Event) (bool, er
|
|||
}
|
||||
if u.membership != membershipStateInvite {
|
||||
if err = u.d.statements.updateMembership(
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateInvite, "",
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateInvite, 0,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -462,9 +462,15 @@ func (u *membershipUpdater) SetToJoin(senderUserID string, eventID string, isUpd
|
|||
}
|
||||
}
|
||||
|
||||
// Lookup the NID of the new join event
|
||||
nIDs, err := u.d.EventNIDs([]string{eventID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.membership != membershipStateJoin || isUpdate {
|
||||
if err = u.d.statements.updateMembership(
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateJoin, eventID,
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateJoin, nIDs[eventID],
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -474,7 +480,7 @@ func (u *membershipUpdater) SetToJoin(senderUserID string, eventID string, isUpd
|
|||
}
|
||||
|
||||
// SetToLeave implements types.MembershipUpdater
|
||||
func (u *membershipUpdater) SetToLeave(senderUserID string) ([]string, error) {
|
||||
func (u *membershipUpdater) SetToLeave(senderUserID string, eventID string) ([]string, error) {
|
||||
senderUserNID, err := u.d.assignStateKeyNID(u.txn, senderUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -485,9 +491,16 @@ func (u *membershipUpdater) SetToLeave(senderUserID string) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Lookup the NID of the new leave event
|
||||
nIDs, err := u.d.EventNIDs([]string{eventID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.membership != membershipStateLeaveOrBan {
|
||||
if err = u.d.statements.updateMembership(
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateLeaveOrBan, "",
|
||||
u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateLeaveOrBan, nIDs[eventID],
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -495,6 +508,53 @@ func (u *membershipUpdater) SetToLeave(senderUserID string) ([]string, error) {
|
|||
return inviteEventIDs, nil
|
||||
}
|
||||
|
||||
// GetMembershipEvents returns an array containing 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: in this case, send the list of members as it was when the user left
|
||||
// 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.
|
||||
func (d *Database) GetMembershipEvents(roomNID types.RoomNID, requestSenderUserID string) (events []types.Event, err error) {
|
||||
txn, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
requestSenderUserNID, err := d.assignStateKeyNID(txn, requestSenderUserID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, senderMembership, err := d.statements.selectMembershipFromRoomAndTarget(roomNID, requestSenderUserNID)
|
||||
if err == sql.ErrNoRows {
|
||||
// The user has never been a member of that room
|
||||
return nil, 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
|
||||
// TODO: Send the list of joined member as it was when the user left
|
||||
// We cannot do this using only the memberships database, as it
|
||||
// only stores the latest join event NID for a given target user.
|
||||
// The solution would be to build the state of a room after before
|
||||
// the leave event and extract a members list from it.
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type transaction struct {
|
||||
txn *sql.Tx
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ type MembershipUpdater interface {
|
|||
SetToJoin(senderUserID string, eventID string, isUpdate bool) (inviteEventIDs []string, err error)
|
||||
// Set the state to leave.
|
||||
// Returns a list of invite event IDs that this state change retired.
|
||||
SetToLeave(senderUserID string) (inviteEventIDs []string, err error)
|
||||
SetToLeave(senderUserID string, eventID string) (inviteEventIDs []string, err error)
|
||||
// Implements Transaction so it can be committed or rolledback.
|
||||
Transaction
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue