Move GMSL client types to Dendrite (#3045)

GMSL is intended for Federation only. Sister PR to
https://github.com/matrix-org/gomatrixserverlib/pull/357
This commit is contained in:
kegsay 2023-04-04 18:16:53 +01:00 committed by GitHub
parent 985298cfc4
commit 3691423626
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 692 additions and 234 deletions

View file

@ -0,0 +1,88 @@
/* 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 synctypes
import "github.com/matrix-org/gomatrixserverlib"
type ClientEventFormat int
const (
// FormatAll will include all client event keys
FormatAll ClientEventFormat = iota
// FormatSync will include only the event keys required by the /sync API. Notably, this
// means the 'room_id' will be missing from the events.
FormatSync
)
// ClientEvent is an event which is fit for consumption by clients, in accordance with the specification.
type ClientEvent struct {
Content gomatrixserverlib.RawJSON `json:"content"`
EventID string `json:"event_id,omitempty"` // EventID is omitted on receipt events
OriginServerTS gomatrixserverlib.Timestamp `json:"origin_server_ts,omitempty"` // OriginServerTS is omitted on receipt events
RoomID string `json:"room_id,omitempty"` // RoomID is omitted on /sync responses
Sender string `json:"sender,omitempty"` // Sender is omitted on receipt events
StateKey *string `json:"state_key,omitempty"`
Type string `json:"type"`
Unsigned gomatrixserverlib.RawJSON `json:"unsigned,omitempty"`
Redacts string `json:"redacts,omitempty"`
}
// ToClientEvents converts server events to client events.
func ToClientEvents(serverEvs []*gomatrixserverlib.Event, format ClientEventFormat) []ClientEvent {
evs := make([]ClientEvent, 0, len(serverEvs))
for _, se := range serverEvs {
if se == nil {
continue // TODO: shouldn't happen?
}
evs = append(evs, ToClientEvent(se, format))
}
return evs
}
// HeaderedToClientEvents converts headered server events to client events.
func HeaderedToClientEvents(serverEvs []*gomatrixserverlib.HeaderedEvent, format ClientEventFormat) []ClientEvent {
evs := make([]ClientEvent, 0, len(serverEvs))
for _, se := range serverEvs {
if se == nil {
continue // TODO: shouldn't happen?
}
evs = append(evs, HeaderedToClientEvent(se, format))
}
return evs
}
// ToClientEvent converts a single server event to a client event.
func ToClientEvent(se *gomatrixserverlib.Event, format ClientEventFormat) ClientEvent {
ce := ClientEvent{
Content: gomatrixserverlib.RawJSON(se.Content()),
Sender: se.Sender(),
Type: se.Type(),
StateKey: se.StateKey(),
Unsigned: gomatrixserverlib.RawJSON(se.Unsigned()),
OriginServerTS: se.OriginServerTS(),
EventID: se.EventID(),
Redacts: se.Redacts(),
}
if format == FormatAll {
ce.RoomID = se.RoomID()
}
return ce
}
// HeaderedToClientEvent converts a single headered server event to a client event.
func HeaderedToClientEvent(se *gomatrixserverlib.HeaderedEvent, format ClientEventFormat) ClientEvent {
return ToClientEvent(se.Event, format)
}

View file

@ -0,0 +1,105 @@
/* 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 synctypes
import (
"bytes"
"encoding/json"
"testing"
"github.com/matrix-org/gomatrixserverlib"
)
func TestToClientEvent(t *testing.T) { // nolint: gocyclo
ev, err := gomatrixserverlib.NewEventFromTrustedJSON([]byte(`{
"type": "m.room.name",
"state_key": "",
"event_id": "$test:localhost",
"room_id": "!test:localhost",
"sender": "@test:localhost",
"content": {
"name": "Hello World"
},
"origin_server_ts": 123456,
"unsigned": {
"prev_content": {
"name": "Goodbye World"
}
}
}`), false, gomatrixserverlib.RoomVersionV1)
if err != nil {
t.Fatalf("failed to create Event: %s", err)
}
ce := ToClientEvent(ev, FormatAll)
if ce.EventID != ev.EventID() {
t.Errorf("ClientEvent.EventID: wanted %s, got %s", ev.EventID(), ce.EventID)
}
if ce.OriginServerTS != ev.OriginServerTS() {
t.Errorf("ClientEvent.OriginServerTS: wanted %d, got %d", ev.OriginServerTS(), ce.OriginServerTS)
}
if ce.StateKey == nil || *ce.StateKey != "" {
t.Errorf("ClientEvent.StateKey: wanted '', got %v", ce.StateKey)
}
if ce.Type != ev.Type() {
t.Errorf("ClientEvent.Type: wanted %s, got %s", ev.Type(), ce.Type)
}
if !bytes.Equal(ce.Content, ev.Content()) {
t.Errorf("ClientEvent.Content: wanted %s, got %s", string(ev.Content()), string(ce.Content))
}
if !bytes.Equal(ce.Unsigned, ev.Unsigned()) {
t.Errorf("ClientEvent.Unsigned: wanted %s, got %s", string(ev.Unsigned()), string(ce.Unsigned))
}
if ce.Sender != ev.Sender() {
t.Errorf("ClientEvent.Sender: wanted %s, got %s", ev.Sender(), ce.Sender)
}
j, err := json.Marshal(ce)
if err != nil {
t.Fatalf("failed to Marshal ClientEvent: %s", err)
}
// Marshal sorts keys in structs by the order they are defined in the struct, which is alphabetical
out := `{"content":{"name":"Hello World"},"event_id":"$test:localhost","origin_server_ts":123456,` +
`"room_id":"!test:localhost","sender":"@test:localhost","state_key":"","type":"m.room.name",` +
`"unsigned":{"prev_content":{"name":"Goodbye World"}}}`
if !bytes.Equal([]byte(out), j) {
t.Errorf("ClientEvent marshalled to wrong bytes: wanted %s, got %s", out, string(j))
}
}
func TestToClientFormatSync(t *testing.T) {
ev, err := gomatrixserverlib.NewEventFromTrustedJSON([]byte(`{
"type": "m.room.name",
"state_key": "",
"event_id": "$test:localhost",
"room_id": "!test:localhost",
"sender": "@test:localhost",
"content": {
"name": "Hello World"
},
"origin_server_ts": 123456,
"unsigned": {
"prev_content": {
"name": "Goodbye World"
}
}
}`), false, gomatrixserverlib.RoomVersionV1)
if err != nil {
t.Fatalf("failed to create Event: %s", err)
}
ce := ToClientEvent(ev, FormatSync)
if ce.RoomID != "" {
t.Errorf("ClientEvent.RoomID: wanted '', got %s", ce.RoomID)
}
}

152
syncapi/synctypes/filter.go Normal file
View file

@ -0,0 +1,152 @@
// Copyright 2017 Jan Christian Grünhage
//
// 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 synctypes
import (
"errors"
)
// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
// Specified by: https://spec.matrix.org/v1.6/client-server-api/#filtering
type Filter struct {
EventFields []string `json:"event_fields,omitempty"`
EventFormat string `json:"event_format,omitempty"`
Presence EventFilter `json:"presence,omitempty"`
AccountData EventFilter `json:"account_data,omitempty"`
Room RoomFilter `json:"room,omitempty"`
}
// EventFilter is used to define filtering rules for events
type EventFilter struct {
Limit int `json:"limit,omitempty"`
NotSenders *[]string `json:"not_senders,omitempty"`
NotTypes *[]string `json:"not_types,omitempty"`
Senders *[]string `json:"senders,omitempty"`
Types *[]string `json:"types,omitempty"`
}
// RoomFilter is used to define filtering rules for room-related events
type RoomFilter struct {
NotRooms *[]string `json:"not_rooms,omitempty"`
Rooms *[]string `json:"rooms,omitempty"`
Ephemeral RoomEventFilter `json:"ephemeral,omitempty"`
IncludeLeave bool `json:"include_leave,omitempty"`
State StateFilter `json:"state,omitempty"`
Timeline RoomEventFilter `json:"timeline,omitempty"`
AccountData RoomEventFilter `json:"account_data,omitempty"`
}
// StateFilter is used to define filtering rules for state events
type StateFilter struct {
NotSenders *[]string `json:"not_senders,omitempty"`
NotTypes *[]string `json:"not_types,omitempty"`
Senders *[]string `json:"senders,omitempty"`
Types *[]string `json:"types,omitempty"`
LazyLoadMembers bool `json:"lazy_load_members,omitempty"`
IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"`
NotRooms *[]string `json:"not_rooms,omitempty"`
Rooms *[]string `json:"rooms,omitempty"`
Limit int `json:"limit,omitempty"`
UnreadThreadNotifications bool `json:"unread_thread_notifications,omitempty"`
ContainsURL *bool `json:"contains_url,omitempty"`
}
// RoomEventFilter is used to define filtering rules for events in rooms
type RoomEventFilter struct {
Limit int `json:"limit,omitempty"`
NotSenders *[]string `json:"not_senders,omitempty"`
NotTypes *[]string `json:"not_types,omitempty"`
Senders *[]string `json:"senders,omitempty"`
Types *[]string `json:"types,omitempty"`
LazyLoadMembers bool `json:"lazy_load_members,omitempty"`
IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"`
NotRooms *[]string `json:"not_rooms,omitempty"`
Rooms *[]string `json:"rooms,omitempty"`
UnreadThreadNotifications bool `json:"unread_thread_notifications,omitempty"`
ContainsURL *bool `json:"contains_url,omitempty"`
}
// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" {
return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
}
return nil
}
// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in
// the request
func DefaultFilter() Filter {
return Filter{
AccountData: DefaultEventFilter(),
EventFields: nil,
EventFormat: "client",
Presence: DefaultEventFilter(),
Room: RoomFilter{
AccountData: DefaultRoomEventFilter(),
Ephemeral: DefaultRoomEventFilter(),
IncludeLeave: false,
NotRooms: nil,
Rooms: nil,
State: DefaultStateFilter(),
Timeline: DefaultRoomEventFilter(),
},
}
}
// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is
// provided in the request
func DefaultEventFilter() EventFilter {
return EventFilter{
// parity with synapse: https://github.com/matrix-org/synapse/blob/v1.80.0/synapse/api/filtering.py#L336
Limit: 10,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
}
}
// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter
// is provided in the request
func DefaultStateFilter() StateFilter {
return StateFilter{
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
LazyLoadMembers: false,
IncludeRedundantMembers: false,
NotRooms: nil,
Rooms: nil,
ContainsURL: nil,
}
}
// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no
// filter is provided in the request
func DefaultRoomEventFilter() RoomEventFilter {
return RoomEventFilter{
// parity with synapse: https://github.com/matrix-org/synapse/blob/v1.80.0/synapse/api/filtering.py#L336
Limit: 10,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
NotRooms: nil,
Rooms: nil,
ContainsURL: nil,
}
}

View file

@ -0,0 +1,60 @@
package synctypes
import (
"encoding/json"
"reflect"
"testing"
)
func Test_Filter(t *testing.T) {
tests := []struct {
name string
input []byte
want RoomEventFilter
}{
{
name: "empty types filter",
input: []byte(`{ "types": [] }`),
want: RoomEventFilter{
Limit: 0,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: &[]string{},
LazyLoadMembers: false,
IncludeRedundantMembers: false,
NotRooms: nil,
Rooms: nil,
ContainsURL: nil,
},
},
{
name: "absent types filter",
input: []byte(`{}`),
want: RoomEventFilter{
Limit: 0,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
LazyLoadMembers: false,
IncludeRedundantMembers: false,
NotRooms: nil,
Rooms: nil,
ContainsURL: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var f RoomEventFilter
if err := json.Unmarshal(tt.input, &f); err != nil {
t.Fatalf("unable to parse filter: %v", err)
}
if !reflect.DeepEqual(f, tt.want) {
t.Fatalf("Expected %+v\ngot %+v", tt.want, f)
}
})
}
}