diff options
author | Neil Alexander <neilalexander@users.noreply.github.com> | 2020-04-14 18:36:08 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-14 18:36:08 +0100 |
commit | 895a72b6eecf7a7e71770bfaede53cd51f7c91e1 (patch) | |
tree | c9f9d0091c9392cb81d9bc8cac97193f4cee0147 /clientapi | |
parent | 73d2f59e303fa998f997c483bb6843bf77e069e5 (diff) |
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
Diffstat (limited to 'clientapi')
-rw-r--r-- | clientapi/routing/routing.go | 26 | ||||
-rw-r--r-- | clientapi/routing/state.go | 141 |
2 files changed, 167 insertions, 0 deletions
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/clientapi/routing/state.go b/clientapi/routing/state.go new file mode 100644 index 00000000..c243eec0 --- /dev/null +++ b/clientapi/routing/state.go @@ -0,0 +1,141 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package routing + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/syncapi/types" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + log "github.com/sirupsen/logrus" +) + +type stateEventInStateResp struct { + gomatrixserverlib.ClientEvent + PrevContent json.RawMessage `json:"prev_content,omitempty"` + ReplacesState string `json:"replaces_state,omitempty"` +} + +// OnIncomingStateRequest is called when a client makes a /rooms/{roomID}/state +// request. It will fetch all the state events from the specified room and will +// append the necessary keys to them if applicable before returning them. +// Returns an error if something went wrong in the process. +// 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(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{} + + 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 stateRes.StateEvents { + stateEvent := stateEventInStateResp{ + ClientEvent: gomatrixserverlib.HeaderedToClientEvents( + []gomatrixserverlib.HeaderedEvent{event}, gomatrixserverlib.FormatAll, + )[0], + } + var prevEventRef types.PrevEventRef + if len(event.Unsigned()) > 0 { + if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { + util.GetLogger(ctx).WithError(err).Error("json.Unmarshal failed") + return jsonerror.InternalServerError() + } + // Fills the previous state event ID if the state event replaces another + // state event + if len(prevEventRef.ReplacesState) > 0 { + stateEvent.ReplacesState = prevEventRef.ReplacesState + } + // Fill the previous event if the state event references a previous event + if prevEventRef.PrevContent != nil { + stateEvent.PrevContent = prevEventRef.PrevContent + } + } + + resp = append(resp, stateEvent) + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: resp, + } +} + +// OnIncomingStateTypeRequest is called when a client makes a +// /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(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) + util.GetLogger(ctx).WithFields(log.Fields{ + "roomID": roomID, + "evType": evType, + "stateKey": stateKey, + }).Info("Fetching state") + + 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 len(stateRes.StateEvents) == 0 { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("cannot find state"), + } + } + + stateEvent := stateEventInStateResp{ + ClientEvent: gomatrixserverlib.HeaderedToClientEvent(stateRes.StateEvents[0], gomatrixserverlib.FormatAll), + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: stateEvent.Content, + } +} |