From 895a72b6eecf7a7e71770bfaede53cd51f7c91e1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 14 Apr 2020 18:36:08 +0100 Subject: [PATCH] Move /room/{roomID}/state endpoints into client API (#606) (#962) * Move /room/{roomID}/state endpoints into client API (#606) * Update sytest-whitelist * Blacklist tests which rely on endpoints we don't implement --- clientapi/routing/routing.go | 26 ++++++++++++ {syncapi => clientapi}/routing/state.go | 54 ++++++++++++++++--------- common/events.go | 4 ++ federationapi/routing/threepid.go | 4 ++ roomserver/alias/alias.go | 4 ++ roomserver/query/query.go | 16 ++++++-- syncapi/routing/routing.go | 24 ----------- sytest-blacklist | 4 ++ sytest-whitelist | 6 ++- 9 files changed, 93 insertions(+), 49 deletions(-) rename {syncapi => clientapi}/routing/state.go (69%) diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 91a1588c..5dc6d7db 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -149,6 +149,31 @@ func Setup( return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, queryAPI, federation, keyRing) }), ).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateRequest(req.Context(), queryAPI, vars["roomID"]) + })).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state/{type}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateTypeRequest(req.Context(), queryAPI, vars["roomID"], vars["type"], "") + })).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateTypeRequest(req.Context(), queryAPI, vars["roomID"], vars["type"], vars["stateKey"]) + })).Methods(http.MethodGet, http.MethodOptions) + r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}", common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) @@ -164,6 +189,7 @@ func Setup( return SendEvent(req, device, vars["roomID"], eventType, nil, &emptyString, cfg, queryAPI, producer, nil) }), ).Methods(http.MethodPut, http.MethodOptions) + r0mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}", common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) diff --git a/syncapi/routing/state.go b/clientapi/routing/state.go similarity index 69% rename from syncapi/routing/state.go rename to clientapi/routing/state.go index 87b6396a..c243eec0 100644 --- a/syncapi/routing/state.go +++ b/clientapi/routing/state.go @@ -15,11 +15,12 @@ package routing import ( + "context" "encoding/json" "net/http" "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -39,22 +40,29 @@ type stateEventInStateResp struct { // TODO: Check if the user is in the room. If not, check if the room's history // is publicly visible. Current behaviour is returning an empty array if the // user cannot see the room's history. -func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID string) util.JSONResponse { +func OnIncomingStateRequest(ctx context.Context, queryAPI api.RoomserverQueryAPI, roomID string) util.JSONResponse { // TODO(#287): Auth request and handle the case where the user has left (where // we should return the state at the poin they left) + stateReq := api.QueryLatestEventsAndStateRequest{ + RoomID: roomID, + } + stateRes := api.QueryLatestEventsAndStateResponse{} - stateFilter := gomatrixserverlib.DefaultStateFilter() - // TODO: stateFilter should not limit the number of state events (or only limits abusive number of events) - - stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID, &stateFilter) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEventsForRoom failed") + if err := queryAPI.QueryLatestEventsAndState(ctx, &stateReq, &stateRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("queryAPI.QueryLatestEventsAndState failed") return jsonerror.InternalServerError() } + if len(stateRes.StateEvents) == 0 { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("cannot find state"), + } + } + resp := []stateEventInStateResp{} // Fill the prev_content and replaces_state keys if necessary - for _, event := range stateEvents { + for _, event := range stateRes.StateEvents { stateEvent := stateEventInStateResp{ ClientEvent: gomatrixserverlib.HeaderedToClientEvents( []gomatrixserverlib.HeaderedEvent{event}, gomatrixserverlib.FormatAll, @@ -63,7 +71,7 @@ func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID strin var prevEventRef types.PrevEventRef if len(event.Unsigned()) > 0 { if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { - util.GetLogger(req.Context()).WithError(err).Error("json.Unmarshal failed") + util.GetLogger(ctx).WithError(err).Error("json.Unmarshal failed") return jsonerror.InternalServerError() } // Fills the previous state event ID if the state event replaces another @@ -90,24 +98,32 @@ func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID strin // /rooms/{roomID}/state/{type}/{statekey} request. It will look in current // state to see if there is an event with that type and state key, if there // is then (by default) we return the content, otherwise a 404. -func OnIncomingStateTypeRequest(req *http.Request, db storage.Database, roomID string, evType, stateKey string) util.JSONResponse { +func OnIncomingStateTypeRequest(ctx context.Context, queryAPI api.RoomserverQueryAPI, roomID string, evType, stateKey string) util.JSONResponse { // TODO(#287): Auth request and handle the case where the user has left (where // we should return the state at the poin they left) - - logger := util.GetLogger(req.Context()) - logger.WithFields(log.Fields{ + util.GetLogger(ctx).WithFields(log.Fields{ "roomID": roomID, "evType": evType, "stateKey": stateKey, }).Info("Fetching state") - event, err := db.GetStateEvent(req.Context(), roomID, evType, stateKey) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEvent failed") + stateReq := api.QueryLatestEventsAndStateRequest{ + RoomID: roomID, + StateToFetch: []gomatrixserverlib.StateKeyTuple{ + gomatrixserverlib.StateKeyTuple{ + EventType: evType, + StateKey: stateKey, + }, + }, + } + stateRes := api.QueryLatestEventsAndStateResponse{} + + if err := queryAPI.QueryLatestEventsAndState(ctx, &stateReq, &stateRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("queryAPI.QueryLatestEventsAndState failed") return jsonerror.InternalServerError() } - if event == nil { + if len(stateRes.StateEvents) == 0 { return util.JSONResponse{ Code: http.StatusNotFound, JSON: jsonerror.NotFound("cannot find state"), @@ -115,7 +131,7 @@ func OnIncomingStateTypeRequest(req *http.Request, db storage.Database, roomID s } stateEvent := stateEventInStateResp{ - ClientEvent: gomatrixserverlib.HeaderedToClientEvent(*event, gomatrixserverlib.FormatAll), + ClientEvent: gomatrixserverlib.HeaderedToClientEvent(stateRes.StateEvents[0], gomatrixserverlib.FormatAll), } return util.JSONResponse{ diff --git a/common/events.go b/common/events.go index adbdf338..556b7b67 100644 --- a/common/events.go +++ b/common/events.go @@ -73,6 +73,10 @@ func AddPrevEventsToEvent( return fmt.Errorf("gomatrixserverlib.StateNeededForEventBuilder: %w", err) } + if len(eventsNeeded.Tuples()) == 0 { + return errors.New("expecting state tuples for event builder, got none") + } + // Ask the roomserver for information about this room queryReq := api.QueryLatestEventsAndStateRequest{ RoomID: builder.RoomID, diff --git a/federationapi/routing/threepid.go b/federationapi/routing/threepid.go index da717473..f93d934e 100644 --- a/federationapi/routing/threepid.go +++ b/federationapi/routing/threepid.go @@ -271,6 +271,10 @@ func buildMembershipEvent( return nil, err } + if len(eventsNeeded.Tuples()) == 0 { + return nil, errors.New("expecting state tuples for event builder, got none") + } + // Ask the roomserver for information about this room queryReq := roomserverAPI.QueryLatestEventsAndStateRequest{ RoomID: builder.RoomID, diff --git a/roomserver/alias/alias.go b/roomserver/alias/alias.go index 032b8da1..eb606e5c 100644 --- a/roomserver/alias/alias.go +++ b/roomserver/alias/alias.go @@ -17,6 +17,7 @@ package alias import ( "context" "encoding/json" + "errors" "net/http" "time" @@ -218,6 +219,9 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent( if err != nil { return err } + if len(eventsNeeded.Tuples()) == 0 { + return errors.New("expecting state tuples for event builder, got none") + } req := roomserverAPI.QueryLatestEventsAndStateRequest{ RoomID: roomID, StateToFetch: eventsNeeded.Tuples(), diff --git a/roomserver/query/query.go b/roomserver/query/query.go index 7e05fe36..12d8436e 100644 --- a/roomserver/query/query.go +++ b/roomserver/query/query.go @@ -132,10 +132,18 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState( return err } - // Look up the current state for the requested tuples. - stateEntries, err := roomState.LoadStateAtSnapshotForStringTuples( - ctx, currentStateSnapshotNID, request.StateToFetch, - ) + var stateEntries []types.StateEntry + if len(request.StateToFetch) == 0 { + // Look up all room state. + stateEntries, err = roomState.LoadStateAtSnapshot( + ctx, currentStateSnapshotNID, + ) + } else { + // Look up the current state for the requested tuples. + stateEntries, err = roomState.LoadStateAtSnapshotForStringTuples( + ctx, currentStateSnapshotNID, request.StateToFetch, + ) + } if err != nil { return err } diff --git a/syncapi/routing/routing.go b/syncapi/routing/routing.go index be90e0a0..9078b87f 100644 --- a/syncapi/routing/routing.go +++ b/syncapi/routing/routing.go @@ -56,30 +56,6 @@ func Setup( return srp.OnIncomingSyncRequest(req, device) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/state", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateRequest(req, syncDB, vars["roomID"]) - })).Methods(http.MethodGet, http.MethodOptions) - - r0mux.Handle("/rooms/{roomID}/state/{type}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], "") - })).Methods(http.MethodGet, http.MethodOptions) - - r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], vars["stateKey"]) - })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/messages", common.MakeAuthAPI("room_messages", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) if err != nil { diff --git a/sytest-blacklist b/sytest-blacklist index da0667a1..caad2545 100644 --- a/sytest-blacklist +++ b/sytest-blacklist @@ -35,3 +35,7 @@ Inbound federation rejects invites which are not signed by the sender # Blacklisted because we don't support ignores yet Ignore invite in incremental sync + +# Blacklisted because this test calls /r0/events which we don't implement +New room members see their own join event +Existing members see new members' join events diff --git a/sytest-whitelist b/sytest-whitelist index 97ded9bb..3277cc59 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -55,8 +55,9 @@ Request to logout with invalid an access token is rejected Request to logout without an access token is rejected Room creation reports m.room.create to myself Room creation reports m.room.member to myself -New room members see their own join event -Existing members see new members' join events +# Blacklisted because these tests call /r0/events which we don't implement +# New room members see their own join event +# Existing members see new members' join events setting 'm.room.power_levels' respects room powerlevel Unprivileged users can set m.room.topic if it only needs level 0 Users cannot set ban powerlevel higher than their own @@ -245,3 +246,4 @@ Remote user can backfill in a room with version 4 Outbound federation can send invites via v2 API User can invite local user to room with version 3 User can invite local user to room with version 4 +A pair of servers can establish a join in a v2 room