Implement fully read markers (#1475)

See #653

Signed-off-by: Loïck Bonniot <git@lesterpig.com>

Co-authored-by: Kegsay <kegan@matrix.org>
This commit is contained in:
Loïck Bonniot 2020-10-09 10:15:35 +02:00 committed by GitHub
parent 009401ad4d
commit f3e8ae01ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 4 deletions

View File

@ -20,8 +20,10 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/util" "github.com/matrix-org/util"
@ -91,6 +93,13 @@ func SaveAccountData(
} }
} }
if dataType == "m.fully_read" {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Unable to set read marker"),
}
}
body, err := ioutil.ReadAll(req.Body) body, err := ioutil.ReadAll(req.Body)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed") util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed")
@ -112,7 +121,7 @@ func SaveAccountData(
} }
dataRes := api.InputAccountDataResponse{} dataRes := api.InputAccountDataResponse{}
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil { if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryAccountData failed") util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
return util.ErrorResponse(err) return util.ErrorResponse(err)
} }
@ -127,3 +136,67 @@ func SaveAccountData(
JSON: struct{}{}, JSON: struct{}{},
} }
} }
type readMarkerJSON struct {
FullyRead string `json:"m.fully_read"`
Read string `json:"m.read"`
}
type fullyReadEvent struct {
EventID string `json:"event_id"`
}
// SaveReadMarker implements POST /rooms/{roomId}/read_markers
func SaveReadMarker(
req *http.Request, userAPI api.UserInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI,
syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string,
) util.JSONResponse {
// Verify that the user is a member of this room
resErr := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID)
if resErr != nil {
return *resErr
}
var r readMarkerJSON
resErr = httputil.UnmarshalJSONRequest(req, &r)
if resErr != nil {
return *resErr
}
if r.FullyRead == "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Missing m.fully_read mandatory field"),
}
}
data, err := json.Marshal(fullyReadEvent{EventID: r.FullyRead})
if err != nil {
return jsonerror.InternalServerError()
}
dataReq := api.InputAccountDataRequest{
UserID: device.UserID,
DataType: "m.fully_read",
RoomID: roomID,
AccountData: data,
}
dataRes := api.InputAccountDataResponse{}
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
return util.ErrorResponse(err)
}
if err := syncProducer.SendData(device.UserID, roomID, "m.fully_read"); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("syncProducer.SendData failed")
return jsonerror.InternalServerError()
}
// TODO handle the read receipt that may be included in the read marker
// See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-rooms-roomid-read-markers
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}

View File

@ -695,12 +695,15 @@ func Setup(
).Methods(http.MethodGet, http.MethodOptions) ).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/read_markers", r0mux.Handle("/rooms/{roomID}/read_markers",
httputil.MakeExternalAPI("rooms_read_markers", func(req *http.Request) util.JSONResponse { httputil.MakeAuthAPI("rooms_read_markers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.rateLimit(req); r != nil {
return *r return *r
} }
// TODO: return the read_markers. vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} if err != nil {
return util.ErrorResponse(err)
}
return SaveReadMarker(req, userAPI, rsAPI, syncProducer, device, vars["roomID"])
}), }),
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)

View File

@ -456,6 +456,9 @@ After changing password, can log in with new password
After changing password, existing session still works After changing password, existing session still works
After changing password, different sessions can optionally be kept After changing password, different sessions can optionally be kept
After changing password, a different session no longer works by default After changing password, a different session no longer works by default
Read markers appear in incremental v2 /sync
Read markers appear in initial v2 /sync
Read markers can be updated
Local users can peek into world_readable rooms by room ID Local users can peek into world_readable rooms by room ID
We can't peek into rooms with shared history_visibility We can't peek into rooms with shared history_visibility
We can't peek into rooms with invited history_visibility We can't peek into rooms with invited history_visibility