From d66ec1deef691c7dac9f044b7a676810cc223cf0 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 23 Nov 2016 17:16:54 +0100 Subject: [PATCH] Add pagination to connection listed by state One of the last endpoints that was missing pagination, which is inconsistent with the rest of the list endpoints. As connections are ordered by their update time we can chunk the pages by that dimension even when the representation returned is split by direction of the connection. --- core/connection.go | 36 ++++++++++++++++++++++++++++++++++-- handler/http/connection.go | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/core/connection.go b/core/connection.go index aee8f6f..41f0339 100644 --- a/core/connection.go +++ b/core/connection.go @@ -21,6 +21,7 @@ type ConnectionByStateFunc func( currentApp *app.App, originID uint64, state connection.State, + opts connection.QueryOptions, ) (*ConnectionFeed, error) // ConnectionByState returns all connections for the given origin and state. @@ -32,6 +33,7 @@ func ConnectionByState( currentApp *app.App, originID uint64, state connection.State, + opts connection.QueryOptions, ) (*ConnectionFeed, error) { switch state { case connection.StatePending, connection.StateConfirmed, connection.StateRejected: @@ -41,8 +43,10 @@ func ConnectionByState( } ics, err := connections.Query(currentApp.Namespace(), connection.QueryOptions{ + Before: opts.Before, Enabled: &defaultEnabled, FromIDs: []uint64{originID}, + Limit: opts.Limit, States: []connection.State{state}, }) if err != nil { @@ -50,7 +54,9 @@ func ConnectionByState( } ocs, err := connections.Query(currentApp.Namespace(), connection.QueryOptions{ + Before: opts.Before, Enabled: &defaultEnabled, + Limit: opts.Limit, States: []connection.State{state}, ToIDs: []uint64{originID}, }) @@ -58,17 +64,40 @@ func ConnectionByState( return nil, err } + cons := append(ics, ocs...) + + if len(cons) == 0 { + return &ConnectionFeed{ + Connections: connection.List{}, + UserMap: user.Map{}, + }, nil + } + + if len(cons) > opts.Limit { + cons = cons[:opts.Limit-1] + } + + ids := []uint64{} + + for _, c := range cons { + if c.FromID == originID { + ids = append(ids, c.ToID) + } else { + ids = append(ids, c.FromID) + } + } + um, err := user.MapFromIDs( users, currentApp.Namespace(), - append(ics.ToIDs(), ocs.FromIDs()...)..., + ids..., ) if err != nil { return nil, err } return &ConnectionFeed{ - Connections: append(ics, ocs...), + Connections: cons, UserMap: um, }, nil } @@ -185,6 +214,7 @@ type ConnectionFollowerIDsFunc func( origin uint64, ) ([]uint64, error) +// ConnectionFollowerIDs returns the list of ids of users who follow origin. func ConnectionFollowerIDs( connections connection.Service, ) ConnectionFollowerIDsFunc { @@ -326,6 +356,8 @@ type ConnectionFriendIDsFunc func( origin uint64, ) ([]uint64, error) +// ConnectionFriendIDs returns the list of ids of users who are friends with +// origin. func ConnectionFriendIDs(connections connection.Service) ConnectionFriendIDsFunc { return func(currentApp *app.App, origin uint64) ([]uint64, error) { fs, err := connections.Query(currentApp.Namespace(), connection.QueryOptions{ diff --git a/handler/http/connection.go b/handler/http/connection.go index fdc4310..4f5eee6 100644 --- a/handler/http/connection.go +++ b/handler/http/connection.go @@ -24,7 +24,25 @@ func ConnectionByState(fn core.ConnectionByStateFunc) Handler { state = extractState(r) ) - feed, err := fn(app, currentUser.ID, state) + opts, err := extractConnectionOpts(r) + if err != nil { + respondError(w, 0, wrapError(ErrBadRequest, err.Error())) + return + } + + opts.Before, err = extractTimeCursorBefore(r) + if err != nil { + respondError(w, 0, wrapError(ErrBadRequest, err.Error())) + return + } + + opts.Limit, err = extractLimit(r) + if err != nil { + respondError(w, 0, wrapError(ErrBadRequest, err.Error())) + return + } + + feed, err := fn(app, currentUser.ID, state, opts) if err != nil { respondError(w, 0, err) return @@ -36,6 +54,12 @@ func ConnectionByState(fn core.ConnectionByStateFunc) Handler { } respondJSON(w, http.StatusOK, &payloadConnections{ + pagination: pagination( + r, + opts.Limit, + connectionCursorAfter(feed.Connections, opts.Limit), + connectionCursorBefore(feed.Connections, opts.Limit), + ), cons: feed.Connections, origin: currentUser.ID, userMap: feed.UserMap, @@ -571,9 +595,10 @@ func (p *payloadConnection) UnmarshalJSON(raw []byte) error { } type payloadConnections struct { - cons connection.List - origin uint64 - userMap user.Map + cons connection.List + origin uint64 + pagination *payloadPagination + userMap user.Map } func (p *payloadConnections) MarshalJSON() ([]byte, error) { @@ -582,11 +607,13 @@ func (p *payloadConnections) MarshalJSON() ([]byte, error) { IncomingCount int `json:"incoming_connections_count"` Outgoing []*payloadConnection `json:"outgoing"` OutgoingCount int `json:"outgoing_connections_count"` + Pagination *payloadPagination `json:"paging"` Users []*payloadUser `json:"users"` UsersCount int `json:"users_count"` }{ Incoming: []*payloadConnection{}, Outgoing: []*payloadConnection{}, + Pagination: p.pagination, Users: []*payloadUser{}, UsersCount: len(p.userMap), }