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
main
Neil Alexander 2020-04-14 18:36:08 +01:00 committed by GitHub
parent 73d2f59e30
commit 895a72b6ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 93 additions and 49 deletions

View File

@ -149,6 +149,31 @@ func Setup(
return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, queryAPI, federation, keyRing) return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, queryAPI, federation, keyRing)
}), }),
).Methods(http.MethodGet, http.MethodOptions) ).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:[^/]+/?}", r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}",
common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
vars, err := common.URLDecodeMapValues(mux.Vars(req)) 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) return SendEvent(req, device, vars["roomID"], eventType, nil, &emptyString, cfg, queryAPI, producer, nil)
}), }),
).Methods(http.MethodPut, http.MethodOptions) ).Methods(http.MethodPut, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}", r0mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}",
common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
vars, err := common.URLDecodeMapValues(mux.Vars(req)) vars, err := common.URLDecodeMapValues(mux.Vars(req))

View File

@ -15,11 +15,12 @@
package routing package routing
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "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/dendrite/syncapi/types"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "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 // 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 // is publicly visible. Current behaviour is returning an empty array if the
// user cannot see the room's history. // 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 // TODO(#287): Auth request and handle the case where the user has left (where
// we should return the state at the poin they left) // we should return the state at the poin they left)
stateReq := api.QueryLatestEventsAndStateRequest{
RoomID: roomID,
}
stateRes := api.QueryLatestEventsAndStateResponse{}
stateFilter := gomatrixserverlib.DefaultStateFilter() if err := queryAPI.QueryLatestEventsAndState(ctx, &stateReq, &stateRes); err != nil {
// TODO: stateFilter should not limit the number of state events (or only limits abusive number of events) util.GetLogger(ctx).WithError(err).Error("queryAPI.QueryLatestEventsAndState failed")
stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID, &stateFilter)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEventsForRoom failed")
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }
if len(stateRes.StateEvents) == 0 {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("cannot find state"),
}
}
resp := []stateEventInStateResp{} resp := []stateEventInStateResp{}
// Fill the prev_content and replaces_state keys if necessary // Fill the prev_content and replaces_state keys if necessary
for _, event := range stateEvents { for _, event := range stateRes.StateEvents {
stateEvent := stateEventInStateResp{ stateEvent := stateEventInStateResp{
ClientEvent: gomatrixserverlib.HeaderedToClientEvents( ClientEvent: gomatrixserverlib.HeaderedToClientEvents(
[]gomatrixserverlib.HeaderedEvent{event}, gomatrixserverlib.FormatAll, []gomatrixserverlib.HeaderedEvent{event}, gomatrixserverlib.FormatAll,
@ -63,7 +71,7 @@ func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID strin
var prevEventRef types.PrevEventRef var prevEventRef types.PrevEventRef
if len(event.Unsigned()) > 0 { if len(event.Unsigned()) > 0 {
if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { 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() return jsonerror.InternalServerError()
} }
// Fills the previous state event ID if the state event replaces another // 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 // /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 // 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. // 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 // TODO(#287): Auth request and handle the case where the user has left (where
// we should return the state at the poin they left) // we should return the state at the poin they left)
util.GetLogger(ctx).WithFields(log.Fields{
logger := util.GetLogger(req.Context())
logger.WithFields(log.Fields{
"roomID": roomID, "roomID": roomID,
"evType": evType, "evType": evType,
"stateKey": stateKey, "stateKey": stateKey,
}).Info("Fetching state") }).Info("Fetching state")
event, err := db.GetStateEvent(req.Context(), roomID, evType, stateKey) stateReq := api.QueryLatestEventsAndStateRequest{
if err != nil { RoomID: roomID,
util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEvent failed") 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() return jsonerror.InternalServerError()
} }
if event == nil { if len(stateRes.StateEvents) == 0 {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("cannot find state"), JSON: jsonerror.NotFound("cannot find state"),
@ -115,7 +131,7 @@ func OnIncomingStateTypeRequest(req *http.Request, db storage.Database, roomID s
} }
stateEvent := stateEventInStateResp{ stateEvent := stateEventInStateResp{
ClientEvent: gomatrixserverlib.HeaderedToClientEvent(*event, gomatrixserverlib.FormatAll), ClientEvent: gomatrixserverlib.HeaderedToClientEvent(stateRes.StateEvents[0], gomatrixserverlib.FormatAll),
} }
return util.JSONResponse{ return util.JSONResponse{

View File

@ -73,6 +73,10 @@ func AddPrevEventsToEvent(
return fmt.Errorf("gomatrixserverlib.StateNeededForEventBuilder: %w", err) 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 // Ask the roomserver for information about this room
queryReq := api.QueryLatestEventsAndStateRequest{ queryReq := api.QueryLatestEventsAndStateRequest{
RoomID: builder.RoomID, RoomID: builder.RoomID,

View File

@ -271,6 +271,10 @@ func buildMembershipEvent(
return nil, err 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 // Ask the roomserver for information about this room
queryReq := roomserverAPI.QueryLatestEventsAndStateRequest{ queryReq := roomserverAPI.QueryLatestEventsAndStateRequest{
RoomID: builder.RoomID, RoomID: builder.RoomID,

View File

@ -17,6 +17,7 @@ package alias
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"net/http" "net/http"
"time" "time"
@ -218,6 +219,9 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(
if err != nil { if err != nil {
return err return err
} }
if len(eventsNeeded.Tuples()) == 0 {
return errors.New("expecting state tuples for event builder, got none")
}
req := roomserverAPI.QueryLatestEventsAndStateRequest{ req := roomserverAPI.QueryLatestEventsAndStateRequest{
RoomID: roomID, RoomID: roomID,
StateToFetch: eventsNeeded.Tuples(), StateToFetch: eventsNeeded.Tuples(),

View File

@ -132,10 +132,18 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
return err return err
} }
// Look up the current state for the requested tuples. var stateEntries []types.StateEntry
stateEntries, err := roomState.LoadStateAtSnapshotForStringTuples( if len(request.StateToFetch) == 0 {
ctx, currentStateSnapshotNID, request.StateToFetch, // 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 { if err != nil {
return err return err
} }

View File

@ -56,30 +56,6 @@ func Setup(
return srp.OnIncomingSyncRequest(req, device) return srp.OnIncomingSyncRequest(req, device)
})).Methods(http.MethodGet, http.MethodOptions) })).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 { 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)) vars, err := common.URLDecodeMapValues(mux.Vars(req))
if err != nil { if err != nil {

View File

@ -35,3 +35,7 @@ Inbound federation rejects invites which are not signed by the sender
# Blacklisted because we don't support ignores yet # Blacklisted because we don't support ignores yet
Ignore invite in incremental sync 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

View File

@ -55,8 +55,9 @@ Request to logout with invalid an access token is rejected
Request to logout without 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.create to myself
Room creation reports m.room.member to myself Room creation reports m.room.member to myself
New room members see their own join event # Blacklisted because these tests call /r0/events which we don't implement
Existing members see new members' join events # New room members see their own join event
# Existing members see new members' join events
setting 'm.room.power_levels' respects room powerlevel setting 'm.room.power_levels' respects room powerlevel
Unprivileged users can set m.room.topic if it only needs level 0 Unprivileged users can set m.room.topic if it only needs level 0
Users cannot set ban powerlevel higher than their own 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 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 3
User can invite local user to room with version 4 User can invite local user to room with version 4
A pair of servers can establish a join in a v2 room