diff --git a/internal/backend.go b/internal/backend.go index 3861e7f..ab1045f 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -2,9 +2,9 @@ package internal import ( "github.com/signaller-matrix/signaller/internal/models" - "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/devices" + "github.com/signaller-matrix/signaller/internal/models/filter" "github.com/signaller-matrix/signaller/internal/models/rooms" "github.com/signaller-matrix/signaller/internal/models/sync" ) @@ -51,6 +51,6 @@ type User interface { LogoutAll() JoinRoom(Room) models.ApiError Invite(Room, User) models.ApiError - AddFilter(filterID string, filter common.Filter) - GetFilterByID(filterID string) *common.Filter + AddFilter(filterID string, filterReq filter.Request) + GetFilterByID(filterID string) *filter.Request } diff --git a/internal/backends/memory/backend.go b/internal/backends/memory/backend.go index b480d95..1bf5b1d 100644 --- a/internal/backends/memory/backend.go +++ b/internal/backends/memory/backend.go @@ -7,11 +7,10 @@ import ( "strings" "sync" - "github.com/signaller-matrix/signaller/internal/models/common" - "github.com/signaller-matrix/signaller/internal" "github.com/signaller-matrix/signaller/internal/models" "github.com/signaller-matrix/signaller/internal/models/createroom" + "github.com/signaller-matrix/signaller/internal/models/filter" mSync "github.com/signaller-matrix/signaller/internal/models/sync" ) @@ -55,7 +54,7 @@ func (backend *Backend) Register(username, password, device string) (user intern password: password, Tokens: make(map[string]Token), backend: backend, - filters: make(map[string]common.Filter)} + filters: make(map[string]filter.Request)} backend.data[username] = user diff --git a/internal/backends/memory/tokens_test.go b/internal/backends/memory/tokens_test.go index d76ed68..686d954 100644 --- a/internal/backends/memory/tokens_test.go +++ b/internal/backends/memory/tokens_test.go @@ -3,10 +3,12 @@ package memory import ( "testing" + "github.com/signaller-matrix/signaller/internal" + "github.com/stretchr/testify/assert" ) func TestTokenGenerator(t *testing.T) { - token := newToken(defaultTokenSize) + token := internal.RandomString(defaultTokenSize) assert.Len(t, token, defaultTokenSize*2) } diff --git a/internal/backends/memory/user.go b/internal/backends/memory/user.go index 61860f8..5ceb76c 100644 --- a/internal/backends/memory/user.go +++ b/internal/backends/memory/user.go @@ -6,9 +6,9 @@ import ( "github.com/signaller-matrix/signaller/internal" "github.com/signaller-matrix/signaller/internal/models" - "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/devices" + "github.com/signaller-matrix/signaller/internal/models/filter" "github.com/signaller-matrix/signaller/internal/models/rooms" ) @@ -16,7 +16,7 @@ type User struct { name string password string Tokens map[string]Token - filters map[string]common.Filter + filters map[string]filter.Request backend *Backend @@ -278,13 +278,20 @@ func (user *User) JoinRoom(room internal.Room) models.ApiError { return nil } -func (user *User) AddFilter(filterID string, filter common.Filter) { - user.filters[filterID] = filter +func (user *User) AddFilter(filterID string, filterReq filter.Request) { + user.mutex.Lock() + defer user.mutex.Unlock() + + user.filters[filterID] = filterReq } -func (user *User) GetFilterByID(filterID string) *common.Filter { - if val, ok := user.filters[filterID]; ok { - return &val +func (user *User) GetFilterByID(filterID string) *filter.Request { + user.mutex.RLock() + defer user.mutex.RUnlock() + + if filterReq, ok := user.filters[filterID]; ok { + return &filterReq } + return nil } diff --git a/internal/handlers.go b/internal/handlers.go index 1075153..7f297d7 100644 --- a/internal/handlers.go +++ b/internal/handlers.go @@ -14,6 +14,7 @@ import ( "github.com/signaller-matrix/signaller/internal/models/capabilities" "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/devices" + "github.com/signaller-matrix/signaller/internal/models/filter" "github.com/signaller-matrix/signaller/internal/models/joinedrooms" "github.com/signaller-matrix/signaller/internal/models/listroom" "github.com/signaller-matrix/signaller/internal/models/login" @@ -323,6 +324,7 @@ func CapabilitiesHandler(w http.ResponseWriter, r *http.Request) { sendJsonResponse(w, http.StatusOK, response) } +// func AddFilterHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { errorResponse(w, models.M_UNKNOWN, http.StatusBadRequest, "wrong method: "+r.Method) @@ -341,15 +343,19 @@ func AddFilterHandler(w http.ResponseWriter, r *http.Request) { return } - var request common.Filter - getRequest(r, &request) + var request filter.Request + err := getRequest(r, &request) + if err != nil { + errorResponse(w, models.M_BAD_JSON, http.StatusBadRequest, err.Error()) + return + } filterID := RandomString(12) user.AddFilter(filterID, request) - sendJsonResponse(w, http.StatusOK, map[string]interface{}{ - "filter_id": filterID, - }) + response := filter.Response{FilterID: filterID} + + sendJsonResponse(w, http.StatusOK, response) } func GetFilterHandler(w http.ResponseWriter, r *http.Request) { @@ -373,6 +379,7 @@ func GetFilterHandler(w http.ResponseWriter, r *http.Request) { filter := user.GetFilterByID(mux.Vars(r)["filterID"]) if filter == nil { errorResponse(w, models.M_INVALID_PARAM, http.StatusNotFound, "") + return } sendJsonResponse(w, http.StatusOK, filter) diff --git a/internal/models/common/filter.go b/internal/models/common/filter.go deleted file mode 100644 index 029a292..0000000 --- a/internal/models/common/filter.go +++ /dev/null @@ -1,27 +0,0 @@ -package common - -type Filter struct { - Room struct { - State struct { - Types []string `json:"types"` - NotRooms []string `json:"not_rooms"` - } `json:"state"` - Timeline struct { - Limit int `json:"limit"` - Types []string `json:"types"` - NotRooms []string `json:"not_rooms"` - NotSenders []string `json:"not_senders"` - } `json:"timeline"` - Ephemeral struct { - Types []string `json:"types"` - NotRooms []string `json:"not_rooms"` - NotSenders []string `json:"not_senders"` - } `json:"ephemeral"` - } `json:"room"` - Presence struct { - Types []string `json:"types"` - NotSenders []string `json:"not_senders"` - } `json:"presence"` - EventFormat string `json:"event_format"` - EventFields []string `json:"event_fields"` -} diff --git a/internal/models/filter/filter.go b/internal/models/filter/filter.go new file mode 100644 index 0000000..b9c2324 --- /dev/null +++ b/internal/models/filter/filter.go @@ -0,0 +1,64 @@ +package filter + +type EventFormat string + +const ( + EventFormatClient = "client" + EventFormatFederation = "federation" +) + +type Request struct { + EventFields []string `json:"event_fields"` // List of event fields to include. If this list is absent then all fields are included. The entries may include '.' charaters to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' object. A literal '.' character in a field name may be escaped using a '\'. A server may include more fields than were requested. + EventFormat EventFormat `json:"event_format"` // The format to use for events. 'client' will return the events in a format suitable for clients. 'federation' will return the raw event as receieved over federation. The default is 'client'. One of: ["client", "federation"] + Presence EventFilter `json:"presence"` // The presence updates to include. + AccountData EventFilter `json:"account_data"` // The user account data that isn't associated with rooms to include. + Room RoomFilter `json:"room"` // Filters to be applied to room data. +} + +type EventFilter struct { + Limit int `json:"limit"` // The maximum number of events to return. + NotSenders []string `json:"not_senders"` // A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter. + NotTypes []string `json:"not_types"` // A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any sequence of characters. + Senders []string `json:"senders"` // A list of senders IDs to include. If this list is absent then all senders are included. + Types []string `json:"types"` // A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters. +} + +type RoomFilter struct { + NotRooms []string `json:"not_rooms"` // A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. This filter is applied before the filters in ephemeral, state, timeline or account_data + Rooms []string `json:"rooms"` // A list of room IDs to include. If this list is absent then all rooms are included. This filter is applied before the filters in ephemeral, state, timeline or account_data + Ephemeral RoomEventFilter `json:"ephemeral"` // The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms. + IncludeLeave bool `json:"include_leave"` // Include rooms that the user has left in the sync, default false + State StateFilter `json:"state"` // The state events to include for rooms. + Timeline RoomEventFilter `json:"timeline"` // The message and state update events to include for rooms. + AccountData RoomEventFilter `json:"account_data"` // The per user account data to include for rooms. +} + +type StateFilter struct { + Limit int `json:"limit"` // The maximum number of events to return. + NotSenders []string `json:"notSenders"` // A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter. + NotTypes []string `json:"notTypes"` // A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any sequence of characters. + Senders []string `json:"senders"` // A list of senders IDs to include. If this list is absent then all senders are included. + Types []string `json:"types"` // A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters. + LazyLoadMembers bool `json:"lazyLoadMembers"` // If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false. + IncludeRedundantMembers bool `json:"includeRedundantMembers"` // If true, sends all membership events for all events, even if they have already been sent to the client. Does not apply unless lazyLoadMembers is true. See Lazy- loading room members for more information. Defaults to false. + NotRooms []string `json:"notRooms"` // A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. + Rooms []string `json:"rooms"` // A list of room IDs to include. If this list is absent then all rooms are included. + Contains_url bool `json:"contains_url"` // If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url key is not considered for filtering. +} + +type RoomEventFilter struct { + Limit int `json:"limit"` // The maximum number of events to return. + NotSenders []string `json:"notSenders"` // A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter. + NotTypes []string `json:"notTypes"` // A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any sequence of characters. + Senders []string `json:"senders"` // A list of senders IDs to include. If this list is absent then all senders are included. + Types []string `json:"types"` // A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters. + LazyLoadMembers bool `json:"lazyLoadMembers"` // If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false. + IncludeRedundantMembers bool `json:"includeRedundantMembers"` // If true, sends all membership events for all events, even if they have already been sent to the client. Does not apply unless lazyLoadMembers is true. See Lazy- loading room members for more information. Defaults to false. + NotRooms []string `json:"notRooms"` // A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. + Rooms []string `json:"rooms"` // A list of room IDs to include. If this list is absent then all rooms are included. + Contains_url bool `json:"contains_url"` // If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url key is not considered for filtering. +} + +type Response struct { + FilterID string `json:"filter_id"` // Required. The ID of the filter that was created. Cannot start with a { as this character is used to determine if the filter provided is inline JSON or a previously declared filter by homeservers on some APIs. +}