Make all fields optional; set values to nil if we're returning nothing

This commit is contained in:
Till Faelligen 2022-10-02 11:48:27 +02:00
parent d4710217f8
commit 283f909b2d
No known key found for this signature in database
GPG key ID: 3DF82D8AB9211D4E

View file

@ -327,29 +327,57 @@ type PrevEventRef struct {
PrevSender string `json:"prev_sender"`
}
type DeviceLists struct {
Changed []string `json:"changed,omitempty"`
Left []string `json:"left,omitempty"`
}
type RoomsResponse struct {
Join map[string]JoinResponse `json:"join,omitempty"`
Peek map[string]JoinResponse `json:"peek,omitempty"`
Invite map[string]InviteResponse `json:"invite,omitempty"`
Leave map[string]LeaveResponse `json:"leave,omitempty"`
}
type ToDeviceResponse struct {
Events []gomatrixserverlib.SendToDeviceEvent `json:"events,omitempty"`
}
// Response represents a /sync API response. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
type Response struct {
NextBatch StreamingToken `json:"next_batch"`
AccountData struct {
Events []gomatrixserverlib.ClientEvent `json:"events,omitempty"`
} `json:"account_data,omitempty"`
Presence struct {
Events []gomatrixserverlib.ClientEvent `json:"events,omitempty"`
} `json:"presence,omitempty"`
Rooms struct {
Join map[string]JoinResponse `json:"join,omitempty"`
Peek map[string]JoinResponse `json:"peek,omitempty"`
Invite map[string]InviteResponse `json:"invite,omitempty"`
Leave map[string]LeaveResponse `json:"leave,omitempty"`
} `json:"rooms,omitempty"`
ToDevice struct {
Events []gomatrixserverlib.SendToDeviceEvent `json:"events,omitempty"`
} `json:"to_device,omitempty"`
DeviceLists struct {
Changed []string `json:"changed,omitempty"`
Left []string `json:"left,omitempty"`
} `json:"device_lists,omitempty"`
DeviceListsOTKCount map[string]int `json:"device_one_time_keys_count,omitempty"`
NextBatch StreamingToken `json:"next_batch"`
AccountData *ClientEvents `json:"account_data,omitempty"`
Presence *ClientEvents `json:"presence,omitempty"`
Rooms *RoomsResponse `json:"rooms,omitempty"`
ToDevice *ToDeviceResponse `json:"to_device,omitempty"`
DeviceLists *DeviceLists `json:"device_lists,omitempty"`
DeviceListsOTKCount map[string]int `json:"device_one_time_keys_count,omitempty"`
}
func (r Response) MarshalJSON() ([]byte, error) {
type alias Response
a := alias(r)
if r.AccountData != nil && len(r.AccountData.Events) == 0 {
a.AccountData = nil
}
if r.Presence != nil && len(r.Presence.Events) == 0 {
a.Presence = nil
}
if r.DeviceLists != nil {
if len(r.DeviceLists.Left) == 0 && len(r.DeviceLists.Changed) == 0 {
a.DeviceLists = nil
}
}
if r.Rooms != nil {
if len(r.Rooms.Join) == 0 && len(r.Rooms.Peek) == 0 &&
len(r.Rooms.Invite) == 0 && len(r.Rooms.Leave) == 0 {
a.Rooms = nil
}
}
if r.ToDevice != nil && len(r.ToDevice.Events) == 0 {
a.ToDevice = nil
}
return json.Marshal(a)
}
func (r *Response) HasUpdates() bool {
@ -370,18 +398,21 @@ func NewResponse() *Response {
res := Response{}
// Pre-initialise the maps. Synapse will return {} even if there are no rooms under a specific section,
// so let's do the same thing. Bonus: this means we can't get dreaded 'assignment to entry in nil map' errors.
res.Rooms.Join = map[string]JoinResponse{}
res.Rooms.Peek = map[string]JoinResponse{}
res.Rooms.Invite = map[string]InviteResponse{}
res.Rooms.Leave = map[string]LeaveResponse{}
res.Rooms = &RoomsResponse{
Join: map[string]JoinResponse{},
Peek: map[string]JoinResponse{},
Invite: map[string]InviteResponse{},
Leave: map[string]LeaveResponse{},
}
// Also pre-intialise empty slices or else we'll insert 'null' instead of '[]' for the value.
// TODO: We really shouldn't have to do all this to coerce encoding/json to Do The Right Thing. We should
// really be using our own Marshal/Unmarshal implementations otherwise this may prove to be a CPU bottleneck.
// This also applies to NewJoinResponse, NewInviteResponse and NewLeaveResponse.
res.AccountData.Events = []gomatrixserverlib.ClientEvent{}
res.Presence.Events = []gomatrixserverlib.ClientEvent{}
res.ToDevice.Events = []gomatrixserverlib.SendToDeviceEvent{}
res.AccountData = &ClientEvents{}
res.Presence = &ClientEvents{}
res.DeviceLists = &DeviceLists{}
res.ToDevice = &ToDeviceResponse{}
res.DeviceListsOTKCount = map[string]int{}
return &res
@ -403,37 +434,67 @@ type UnreadNotifications struct {
NotificationCount int `json:"notification_count"`
}
type ClientEvents struct {
Events []gomatrixserverlib.ClientEvent `json:"events,omitempty"`
}
type Timeline struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
Limited bool `json:"limited"`
PrevBatch *TopologyToken `json:"prev_batch,omitempty"`
}
type Summary struct {
Heroes []string `json:"m.heroes,omitempty"`
JoinedMemberCount *int `json:"m.joined_member_count,omitempty"`
InvitedMemberCount *int `json:"m.invited_member_count,omitempty"`
}
// JoinResponse represents a /sync response for a room which is under the 'join' or 'peek' key.
type JoinResponse struct {
Summary struct {
Heroes []string `json:"m.heroes,omitempty"`
JoinedMemberCount *int `json:"m.joined_member_count,omitempty"`
InvitedMemberCount *int `json:"m.invited_member_count,omitempty"`
} `json:"summary"`
State struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
} `json:"state"`
Timeline struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
Limited bool `json:"limited"`
PrevBatch *TopologyToken `json:"prev_batch,omitempty"`
} `json:"timeline"`
Ephemeral struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
} `json:"ephemeral"`
AccountData struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
} `json:"account_data"`
Summary *Summary `json:"summary,omitempty"`
State *ClientEvents `json:"state,omitempty"`
Timeline *Timeline `json:"timeline,omitempty"`
Ephemeral *ClientEvents `json:"ephemeral,omitempty"`
AccountData *ClientEvents `json:"account_data,omitempty"`
*UnreadNotifications `json:"unread_notifications,omitempty"`
}
func (jr JoinResponse) MarshalJSON() ([]byte, error) {
type alias JoinResponse
a := alias(jr)
if jr.State != nil && len(jr.State.Events) == 0 {
a.State = nil
}
if jr.Ephemeral != nil && len(jr.Ephemeral.Events) == 0 {
a.Ephemeral = nil
}
if jr.AccountData != nil && len(jr.AccountData.Events) == 0 {
a.AccountData = nil
}
if jr.Timeline != nil && len(jr.Timeline.Events) == 0 {
a.Timeline = nil
}
if jr.Summary != nil && len(jr.Summary.Heroes) == 0 {
a.Summary = nil
}
if jr.UnreadNotifications != nil &&
jr.UnreadNotifications.NotificationCount == 0 && jr.UnreadNotifications.HighlightCount == 0 {
a.UnreadNotifications = nil
}
return json.Marshal(a)
}
// NewJoinResponse creates an empty response with initialised arrays.
func NewJoinResponse() *JoinResponse {
res := JoinResponse{}
res.State.Events = []gomatrixserverlib.ClientEvent{}
res.Timeline.Events = []gomatrixserverlib.ClientEvent{}
res.Ephemeral.Events = []gomatrixserverlib.ClientEvent{}
res.AccountData.Events = []gomatrixserverlib.ClientEvent{}
res := JoinResponse{
Summary: &Summary{},
State: &ClientEvents{},
Timeline: &Timeline{},
Ephemeral: &ClientEvents{},
AccountData: &ClientEvents{},
UnreadNotifications: &UnreadNotifications{},
}
return &res
}
@ -469,21 +530,16 @@ func NewInviteResponse(event *gomatrixserverlib.HeaderedEvent) *InviteResponse {
// LeaveResponse represents a /sync response for a room which is under the 'leave' key.
type LeaveResponse struct {
State struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
} `json:"state"`
Timeline struct {
Events []gomatrixserverlib.ClientEvent `json:"events"`
Limited bool `json:"limited"`
PrevBatch *TopologyToken `json:"prev_batch,omitempty"`
} `json:"timeline"`
State *ClientEvents `json:"state,omitempty"`
Timeline *Timeline `json:"timeline,omitempty"`
}
// NewLeaveResponse creates an empty response with initialised arrays.
func NewLeaveResponse() *LeaveResponse {
res := LeaveResponse{}
res.State.Events = []gomatrixserverlib.ClientEvent{}
res.Timeline.Events = []gomatrixserverlib.ClientEvent{}
res := LeaveResponse{
State: &ClientEvents{},
Timeline: &Timeline{},
}
return &res
}