Move /room/{roomID}/state endpoints into client API (#606)

This commit is contained in:
Neil Alexander 2020-04-14 16:58:34 +01:00
parent 73d2f59e30
commit dcaab10a92
7 changed files with 85 additions and 47 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 {