Add the /joined_rooms API

This commit is contained in:
Travis Ralston 2018-07-20 23:33:40 -06:00
parent d2ae425752
commit 0b169f9a2f
7 changed files with 184 additions and 0 deletions

View file

@ -30,6 +30,10 @@ type response struct {
Chunk []gomatrixserverlib.ClientEvent `json:"chunk"`
}
type joinedResponse struct {
JoinedRooms []string `json:"joined_rooms,flow"`
}
// GetMemberships implements GET /rooms/{roomId}/members
func GetMemberships(
req *http.Request, device *authtypes.Device, roomID string, joinedOnly bool,
@ -58,3 +62,24 @@ func GetMemberships(
JSON: response{queryRes.JoinEvents},
}
}
// GetJoinedRooms implements GET /joined_rooms
func GetJoinedRooms(
req *http.Request, device *authtypes.Device,
_ config.Dendrite,
queryAPI api.RoomserverQueryAPI,
) util.JSONResponse {
queryReq := api.QueryRoomsForUserRequest{
UserID: device.UserID,
Membership:"join",
}
var queryRes api.QueryRoomsForUserResponse
if err := queryAPI.QueryRoomsForUser(req.Context(), &queryReq, &queryRes); err != nil {
return httputil.LogThenError(req, err)
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: joinedResponse{queryRes.RoomIDs},
}
}

View file

@ -344,6 +344,12 @@ func Setup(
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/joined_rooms",
common.MakeAuthAPI("joined_rooms", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
return GetJoinedRooms(req, device, cfg, queryAPI)
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/read_markers",
common.MakeExternalAPI("rooms_read_markers", func(req *http.Request) util.JSONResponse {
// TODO: return the read_markers.

View file

@ -138,6 +138,20 @@ type QueryMembershipsForRoomResponse struct {
HasBeenInRoom bool `json:"has_been_in_room"`
}
// QueryRoomsForUserRequest is a request to QueryRoomsForUser
type QueryRoomsForUserRequest struct {
// The ID of the user sending the request
UserID string `json:"user_id"`
// The membership state to be queried for
Membership string `json:"membership"`
}
// QueryRoomsForUserResponse is a response to QueryRoomsForUser
type QueryRoomsForUserResponse struct {
// The room IDs that match the user ID and membership requested
RoomIDs []string `json:"room_ids,flow"`
}
// QueryInvitesForUserRequest is a request to QueryInvitesForUser
type QueryInvitesForUserRequest struct {
// The room ID to look up invites in.
@ -251,6 +265,13 @@ type RoomserverQueryAPI interface {
response *QueryMembershipsForRoomResponse,
) error
// Query a list of rooms the user has membership in
QueryRoomsForUser(
ctx context.Context,
request *QueryRoomsForUserRequest,
response *QueryRoomsForUserResponse,
) error
// Query a list of invite event senders for a user in a room.
QueryInvitesForUser(
ctx context.Context,
@ -297,6 +318,9 @@ const RoomserverQueryMembershipForUserPath = "/api/roomserver/queryMembershipFor
// RoomserverQueryMembershipsForRoomPath is the HTTP path for the QueryMembershipsForRoom API
const RoomserverQueryMembershipsForRoomPath = "/api/roomserver/queryMembershipsForRoom"
// RoomserverQueryRoomsForUserPath is the HTTP path for the QueryRoomsForUser API
const RoomserverQueryRoomsForUserPath = "/api/roomserver/queryRoomsForUser"
// RoomserverQueryInvitesForUserPath is the HTTP path for the QueryInvitesForUser API
const RoomserverQueryInvitesForUserPath = "/api/roomserver/queryInvitesForUser"
@ -388,6 +412,18 @@ func (h *httpRoomserverQueryAPI) QueryMembershipsForRoom(
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}
func (h *httpRoomserverQueryAPI) QueryRoomsForUser(
ctx context.Context,
request *QueryRoomsForUserRequest,
response *QueryRoomsForUserResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryRoomsForUser")
defer span.Finish()
apiURL := h.roomserverURL + RoomserverQueryRoomsForUserPath
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}
// QueryInvitesForUser implements RoomserverQueryAPI
func (h *httpRoomserverQueryAPI) QueryInvitesForUser(
ctx context.Context,

View file

@ -45,6 +45,9 @@ type RoomserverQueryAPIDatabase interface {
// Returns 0 if the room doesn't exists.
// Returns an error if there was a problem talking to the database.
RoomNID(ctx context.Context, roomID string) (types.RoomNID, error)
// Look up the room IDs corresponding to the room numeric IDs
// Returns an error if there was a problem talking to the database.
GetRoomIDs(ctx context.Context, roomNIDs []types.RoomNID) ([]string, error)
// Look up event references for the latest events in the room and the current state snapshot.
// Returns the latest events, the current state and the maximum depth of the latest events plus 1.
// Returns an error if there was a problem talking to the database.
@ -72,6 +75,11 @@ type RoomserverQueryAPIDatabase interface {
GetMembershipEventNIDsForRoom(
ctx context.Context, roomNID types.RoomNID, joinOnly bool,
) ([]types.EventNID, error)
// Lookup the rooms for which a user has a particular membership state.
// Returns an error if there was a problem talkign to the database.
GetRoomsForUserMembership(
ctx context.Context, userNID types.EventStateKeyNID, membership string,
) ([]types.RoomNID, error)
// Look up the active invites targeting a user in a room and return the
// numeric state key IDs for the user IDs who sent them.
// Returns an error if there was a problem talking to the database.
@ -367,6 +375,32 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
return events, nil
}
// QueryRoomsForUser implements api.RoomserverQueryAPI
func (r *RoomserverQueryAPI) QueryRoomsForUser(
ctx context.Context,
request *api.QueryRoomsForUserRequest,
response *api.QueryRoomsForUserResponse,
) error {
targetUserNIDs, err := r.DB.EventStateKeyNIDs(ctx, []string{request.UserID})
if err != nil {
return err
}
targetUserNID := targetUserNIDs[request.UserID]
roomNIDs, err := r.DB.GetRoomsForUserMembership(ctx, targetUserNID, request.Membership)
if err != nil {
return err
}
roomIDs, err := r.DB.GetRoomIDs(ctx, roomNIDs)
if err != nil {
return err
}
response.RoomIDs = roomIDs
return nil
}
// QueryInvitesForUser implements api.RoomserverQueryAPI
func (r *RoomserverQueryAPI) QueryInvitesForUser(
ctx context.Context,
@ -652,6 +686,20 @@ func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
servMux.Handle(
api.RoomserverQueryRoomsForUserPath,
common.MakeInternalAPI("queryRoomsForUser", func(req *http.Request) util.JSONResponse {
var request api.QueryRoomsForUserRequest
var response api.QueryRoomsForUserResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := r.QueryRoomsForUser(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
servMux.Handle(
api.RoomserverQueryInvitesForUserPath,
common.MakeInternalAPI("queryInvitesForUser", func(req *http.Request) util.JSONResponse {

View file

@ -89,12 +89,17 @@ const updateMembershipSQL = "" +
"UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4, event_nid = $5" +
" WHERE room_nid = $1 AND target_nid = $2"
const selectRoomsForUserMembershipSQL = "" +
"SELECT room_nid FROM roomserver_membership" +
" WHERE target_nid = $1 AND membership_nid = $2"
type membershipStatements struct {
insertMembershipStmt *sql.Stmt
selectMembershipForUpdateStmt *sql.Stmt
selectMembershipFromRoomAndTargetStmt *sql.Stmt
selectMembershipsFromRoomAndMembershipStmt *sql.Stmt
selectMembershipsFromRoomStmt *sql.Stmt
selectRoomsForUserMembershipStmt *sql.Stmt
updateMembershipStmt *sql.Stmt
}
@ -110,6 +115,7 @@ func (s *membershipStatements) prepare(db *sql.DB) (err error) {
{&s.selectMembershipFromRoomAndTargetStmt, selectMembershipFromRoomAndTargetSQL},
{&s.selectMembershipsFromRoomAndMembershipStmt, selectMembershipsFromRoomAndMembershipSQL},
{&s.selectMembershipsFromRoomStmt, selectMembershipsFromRoomSQL},
{&s.selectRoomsForUserMembershipStmt, selectRoomsForUserMembershipSQL},
{&s.updateMembershipStmt, updateMembershipSQL},
}.prepare(db)
}
@ -180,6 +186,26 @@ func (s *membershipStatements) selectMembershipsFromRoomAndMembership(
return
}
func (s *membershipStatements) selectRoomsForUserMembership(
ctx context.Context,
targetUserNID types.EventStateKeyNID, membership membershipState,
) (roomNIDs []types.RoomNID, err error) {
stmt := s.selectRoomsForUserMembershipStmt
rows, err := stmt.QueryContext(ctx, targetUserNID, membership)
if err != nil {
return
}
for rows.Next() {
var rNID types.RoomNID
if err = rows.Scan(&rNID); err != nil {
return
}
roomNIDs = append(roomNIDs, rNID)
}
return
}
func (s *membershipStatements) updateMembership(
ctx context.Context,
txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID,

View file

@ -51,6 +51,9 @@ const insertRoomNIDSQL = "" +
const selectRoomNIDSQL = "" +
"SELECT room_nid FROM roomserver_rooms WHERE room_id = $1"
const selectRoomIDSQL = "" +
"SELECT room_id FROM roomserver_rooms WHERE room_nid = $1"
const selectLatestEventNIDsSQL = "" +
"SELECT latest_event_nids, state_snapshot_nid FROM roomserver_rooms WHERE room_nid = $1"
@ -63,6 +66,7 @@ const updateLatestEventNIDsSQL = "" +
type roomStatements struct {
insertRoomNIDStmt *sql.Stmt
selectRoomNIDStmt *sql.Stmt
selectRoomIDStmt *sql.Stmt
selectLatestEventNIDsStmt *sql.Stmt
selectLatestEventNIDsForUpdateStmt *sql.Stmt
updateLatestEventNIDsStmt *sql.Stmt
@ -76,6 +80,7 @@ func (s *roomStatements) prepare(db *sql.DB) (err error) {
return statementList{
{&s.insertRoomNIDStmt, insertRoomNIDSQL},
{&s.selectRoomNIDStmt, selectRoomNIDSQL},
{&s.selectRoomIDStmt, selectRoomIDSQL},
{&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL},
{&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL},
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
@ -100,6 +105,15 @@ func (s *roomStatements) selectRoomNID(
return types.RoomNID(roomNID), err
}
func (s *roomStatements) selectRoomID(
ctx context.Context, txn *sql.Tx, roomNID types.RoomNID,
) (string, error) {
var roomID string
stmt := common.TxStmt(txn, s.selectRoomIDStmt)
err := stmt.QueryRowContext(ctx, roomNID).Scan(&roomID)
return roomID, err
}
func (s *roomStatements) selectLatestEventNIDs(
ctx context.Context, roomNID types.RoomNID,
) ([]types.EventNID, types.StateSnapshotNID, error) {

View file

@ -412,6 +412,21 @@ func (d *Database) RoomNID(ctx context.Context, roomID string) (types.RoomNID, e
return roomNID, err
}
func (d *Database) GetRoomIDs(ctx context.Context, roomNIDs []types.RoomNID) ([]string, error) {
roomIDs := make([]string, 0)
for _, nid := range roomNIDs {
roomID, err := d.statements.selectRoomID(ctx, nil, nid)
if err != nil {
return nil, err
}
roomIDs = append(roomIDs, roomID)
}
return roomIDs, nil
}
// LatestEventIDs implements query.RoomserverQueryAPIDatabase
func (d *Database) LatestEventIDs(
ctx context.Context, roomNID types.RoomNID,
@ -431,6 +446,20 @@ func (d *Database) LatestEventIDs(
return references, currentStateSnapshotNID, depth, nil
}
func (d *Database) GetRoomsForUserMembership(
ctx context.Context,
userNID types.EventStateKeyNID,
membership string,
) (roomNIDs []types.RoomNID, err error) {
membershipNID := membershipStateLeaveOrBan
if membership == "join" {
membershipNID = membershipStateJoin
} else if membership == "invite" {
membershipNID = membershipStateInvite
}
return d.statements.selectRoomsForUserMembership(ctx, userNID, membershipNID)
}
// GetInvitesForUser implements query.RoomserverQueryAPIDatabase
func (d *Database) GetInvitesForUser(
ctx context.Context,