paging.go (4412B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package util 19 20 import ( 21 "fmt" 22 "net/url" 23 "strings" 24 25 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 26 "github.com/superseriousbusiness/gotosocial/internal/config" 27 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 28 ) 29 30 // PageableResponseParams models the parameters to pass to PackagePageableResponse. 31 // 32 // The given items will be provided in the paged response. 33 // 34 // The other values are all used to create the Link header so that callers know 35 // which endpoint to query next and previously in order to do paging. 36 type PageableResponseParams struct { 37 Items []interface{} // Sorted slice of items (statuses, notifications, etc) 38 Path string // path to use for next/prev queries in the link header 39 NextMaxIDKey string // key to use for the next max id query param in the link header, defaults to 'max_id' 40 NextMaxIDValue string // value to use for next max id 41 PrevMinIDKey string // key to use for the prev min id query param in the link header, defaults to 'min_id' 42 PrevMinIDValue string // value to use for prev min id 43 Limit int // limit number of entries to return 44 ExtraQueryParams []string // any extra query parameters to provide in the link header, should be in the format 'example=value' 45 } 46 47 // PackagePageableResponse is a convenience function for returning 48 // a bunch of pageable items (notifications, statuses, etc), as well 49 // as a Link header to inform callers of where to find next/prev items. 50 func PackagePageableResponse(params PageableResponseParams) (*apimodel.PageableResponse, gtserror.WithCode) { 51 if len(params.Items) == 0 { 52 // No items to page through. 53 return EmptyPageableResponse(), nil 54 } 55 56 // Set default paging values, if 57 // they weren't set by the caller. 58 if params.NextMaxIDKey == "" { 59 params.NextMaxIDKey = "max_id" 60 } 61 62 if params.PrevMinIDKey == "" { 63 params.PrevMinIDKey = "min_id" 64 } 65 66 var ( 67 protocol = config.GetProtocol() 68 host = config.GetHost() 69 nextLink string 70 prevLink string 71 linkHeaderParts = make([]string, 0, 2) 72 ) 73 74 // Parse next link. 75 if params.NextMaxIDValue != "" { 76 nextRaw := params.NextMaxIDKey + "=" + params.NextMaxIDValue 77 78 if params.Limit != 0 { 79 nextRaw = fmt.Sprintf("limit=%d&", params.Limit) + nextRaw 80 } 81 82 for _, p := range params.ExtraQueryParams { 83 nextRaw += "&" + p 84 } 85 86 nextLink = func() string { 87 u := &url.URL{ 88 Scheme: protocol, 89 Host: host, 90 Path: params.Path, 91 RawQuery: nextRaw, 92 } 93 return u.String() 94 }() 95 96 linkHeaderParts = append(linkHeaderParts, `<`+nextLink+`>; rel="next"`) 97 } 98 99 // Parse prev link. 100 if params.PrevMinIDValue != "" { 101 prevRaw := params.PrevMinIDKey + "=" + params.PrevMinIDValue 102 103 if params.Limit != 0 { 104 prevRaw = fmt.Sprintf("limit=%d&", params.Limit) + prevRaw 105 } 106 107 for _, p := range params.ExtraQueryParams { 108 prevRaw = prevRaw + "&" + p 109 } 110 111 prevLink = func() string { 112 u := &url.URL{ 113 Scheme: protocol, 114 Host: host, 115 Path: params.Path, 116 RawQuery: prevRaw, 117 } 118 return u.String() 119 }() 120 121 linkHeaderParts = append(linkHeaderParts, `<`+prevLink+`>; rel="prev"`) 122 } 123 124 return &apimodel.PageableResponse{ 125 Items: params.Items, 126 LinkHeader: strings.Join(linkHeaderParts, ", "), 127 NextLink: nextLink, 128 PrevLink: prevLink, 129 }, nil 130 } 131 132 // EmptyPageableResponse just returns an empty 133 // PageableResponse with no link header or items. 134 func EmptyPageableResponse() *apimodel.PageableResponse { 135 return &apimodel.PageableResponse{ 136 Items: []interface{}{}, 137 } 138 }