-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make extension scoping work with only one field #149
Changes from all commits
6637283
b1e0684
214002b
59d2d27
a0d02ea
cf21e5b
cd62d3e
c483104
da544d9
a51230d
2c7739e
f5773bf
e859312
fe63069
13256d9
7e9815d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,10 @@ type GenericRequest interface { | |
OnlyLists() []string | ||
// Returns the value of the `rooms` JSON key. nil for "not specified". | ||
OnlyRooms() []string | ||
// InterpretAsInitial interprets this as an initial request rather than a delta, and | ||
// overwrites fields accordingly. This can be useful when fields have default | ||
// values, but is a little ugly. Use sparingly. | ||
InterpretAsInitial() | ||
// Overwrite fields in the request by side-effecting on this struct. | ||
ApplyDelta(next GenericRequest) | ||
// ProcessInitial provides a means for extensions to return data to clients immediately. | ||
|
@@ -77,6 +81,17 @@ func (r *Core) OnlyRooms() []string { | |
return r.Rooms | ||
} | ||
|
||
func (r *Core) InterpretAsInitial() { | ||
// An omitted/nil value for lists and rooms normally means "no change". | ||
// If this extension has never been specified before, nil means "all lists/rooms". | ||
if r.Lists == nil { | ||
r.Lists = []string{"*"} | ||
} | ||
if r.Rooms == nil { | ||
r.Rooms = []string{"*"} | ||
} | ||
} | ||
|
||
kegsay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func (r *Core) ApplyDelta(gnext GenericRequest) { | ||
if gnext == nil { | ||
return | ||
|
@@ -100,22 +115,28 @@ func (r *Core) ApplyDelta(gnext GenericRequest) { | |
// according to the "core" extension scoping logic. Extensions are free to suppress | ||
// updates for a room based on additional criteria. | ||
func (r *Core) RoomInScope(roomID string, extCtx Context) bool { | ||
// If the extension hasn't had its scope configured, process everything. | ||
if r.Lists == nil && r.Rooms == nil { | ||
return true | ||
// First determine which rooms the extension is monitoring outside of any sliding windows. | ||
roomsToMonitor := r.Rooms | ||
if len(roomsToMonitor) > 0 && roomsToMonitor[0] == "*" { | ||
roomsToMonitor = extCtx.AllSubscribedRooms | ||
} | ||
|
||
// If this extension has been explicitly subscribed to this room, process the update. | ||
for _, roomInScope := range r.Rooms { | ||
// Process the update if this room is one of those monitored rooms. | ||
for _, roomInScope := range roomsToMonitor { | ||
if roomInScope == roomID { | ||
return true | ||
} | ||
} | ||
|
||
// If the room belongs to one of the lists that this extension should process, process the update. | ||
// Next determine which lists the extension is monitoring. | ||
listsToMonitor := r.Lists | ||
if len(listsToMonitor) > 0 && listsToMonitor[0] == "*" { | ||
kegsay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
listsToMonitor = extCtx.AllLists | ||
} | ||
|
||
// Process the update if the room is visible in one of those lists. | ||
visibleInLists := extCtx.RoomIDsToLists[roomID] | ||
for _, visibleInList := range visibleInLists { | ||
for _, shouldProcessList := range r.Lists { | ||
for _, shouldProcessList := range listsToMonitor { | ||
if visibleInList == shouldProcessList { | ||
return true | ||
} | ||
|
@@ -175,6 +196,8 @@ func (r Request) EnabledExtensions() (exts []GenericRequest) { | |
return | ||
} | ||
|
||
// ApplyDelta applies the `next` request as a delta atop the previous Request r, and | ||
// returns the result as a new Request. | ||
func (r Request) ApplyDelta(next *Request) Request { | ||
currFields := r.fields() | ||
nextFields := next.fields() | ||
|
@@ -187,6 +210,7 @@ func (r Request) ApplyDelta(next *Request) Request { | |
} | ||
if isNil(curr) { | ||
// the next field is what we want to apply | ||
next.InterpretAsInitial() | ||
currFields[i] = next | ||
hasChanges = true | ||
} else { | ||
|
@@ -202,6 +226,24 @@ func (r Request) ApplyDelta(next *Request) Request { | |
return r | ||
} | ||
|
||
func (r *Request) InterpretAsInitial() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what this is supposed to do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the comment above the declaration of this in the interface. https://github.com/matrix-org/sliding-sync/pull/149/files#r1232127907 To illustrate: suppose I receive a request whose account data extension is configured with {
"enabled": "true"
} If this is a brand-new request, the proxy should process this in two steps:
If this is a follow-up to a previous request, the proxy should interpret this as follows
The way I wanted to handle this was something like:
The problem is that I couldn't figure out a way to do the first bullet. It felt like I was fighting the language. So instead, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FTAOD I'm not very happy with InterpretAsInitial. If there is some nice way to implement Default() or similar I'm all ears. |
||
if r.ToDevice != nil { | ||
r.ToDevice.InterpretAsInitial() | ||
} | ||
if r.E2EE != nil { | ||
r.E2EE.InterpretAsInitial() | ||
} | ||
if r.AccountData != nil { | ||
r.AccountData.InterpretAsInitial() | ||
} | ||
if r.Typing != nil { | ||
r.Typing.InterpretAsInitial() | ||
} | ||
if r.Receipts != nil { | ||
r.Receipts.InterpretAsInitial() | ||
} | ||
} | ||
|
||
// Response represents the top-level `extensions` key in the JSON response. | ||
// | ||
// To add a new extension, add a field here and in fields(). | ||
|
@@ -233,6 +275,8 @@ func (r Response) HasData(isInitial bool) bool { | |
return false | ||
} | ||
|
||
// Context is a summary of useful information about the sync3.Request and the state of | ||
// the requester's connection. | ||
type Context struct { | ||
*Handler | ||
// RoomIDToTimeline is a map from room IDs to slices of event IDs. The keys are the | ||
|
@@ -253,6 +297,10 @@ type Context struct { | |
// enclose those sliding windows. Values should be nonnil and nonempty, and may | ||
// contain multiple list names. | ||
RoomIDsToLists map[string][]string | ||
// AllLists is the slice of list names provided to the Sliding Window API. | ||
AllLists []string | ||
// AllSubscribedRooms is the slice of room IDs provided to the Room Subscription API. | ||
AllSubscribedRooms []string | ||
} | ||
|
||
type HandlerInterface interface { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some of this ugliness comes from the fact that we use the same struct type to represent a blob of data D and an optional change to that blob of data.