• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1863989307

11 Jun 2025 08:38AM UTC coverage: 66.403% (+0.7%) from 65.731%
1863989307

Pull #720

gitlab-ci

mzedel
test(gui): made e2e tests work with changed tenant token expansion

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #720: MEN-8411 - billing/ organization split

29581 of 44548 relevant lines covered (66.4%)

1.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.67
/backend/pkg/rest.utils/paging.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package rest
16

17
import (
18
        "fmt"
19
        "net/http"
20
        "net/url"
21
        "strconv"
22

23
        "github.com/pkg/errors"
24
)
25

26
const (
27
        PerPageDefault = 20
28
        PerPageMax     = 500
29

30
        pageQueryParam    = "page"
31
        perPageQueryParam = "per_page"
32
)
33

34
var (
35
        ErrPerPageLimit = errors.Errorf(
36
                `parameter "per_page" above limit (max: %d)`, PerPageMax,
37
        )
38
)
39

40
func ErrQueryParmInvalid(param, name string) error {
×
41
        return fmt.Errorf("invalid %s query: \"%s\"", param, name)
×
42
}
×
43

44
func ErrQueryParmLimit(name string) error {
×
45
        return fmt.Errorf("invalid %s query: value must be a non-zero positive integer", name)
×
46
}
×
47

48
// ParsePagingParameters parses the paging parameters from the URL query
49
// string and returns the parsed page, per_page or a parsing error respectively.
50
func ParsePagingParameters(r *http.Request) (int64, int64, error) {
4✔
51
        q := r.URL.Query()
4✔
52
        var (
4✔
53
                err     error
4✔
54
                page    int64
4✔
55
                perPage int64
4✔
56
        )
4✔
57
        qPage := q.Get(pageQueryParam)
4✔
58
        if qPage == "" {
8✔
59
                page = 1
4✔
60
        } else {
7✔
61
                page, err = strconv.ParseInt(qPage, 10, 64)
3✔
62
                if err != nil {
3✔
63
                        return -1, -1, ErrQueryParmInvalid("page", qPage)
×
64
                } else if page < 1 {
3✔
65
                        return -1, -1, ErrQueryParmLimit("page")
×
66
                }
×
67
        }
68

69
        qPerPage := q.Get(perPageQueryParam)
4✔
70
        if qPerPage == "" {
8✔
71
                perPage = PerPageDefault
4✔
72
        } else {
6✔
73
                perPage, err = strconv.ParseInt(qPerPage, 10, 64)
2✔
74
                if err != nil {
2✔
75
                        return -1, -1, ErrQueryParmInvalid("per_page", qPerPage)
×
76
                } else if perPage < 1 {
2✔
77
                        return -1, -1, ErrQueryParmLimit("per_page")
×
78
                } else if perPage > PerPageMax {
2✔
79
                        return page, perPage, ErrPerPageLimit
×
80
                }
×
81
        }
82
        return page, perPage, nil
4✔
83
}
84

85
type PagingHints struct {
86
        // TotalCount provides the total count of elements available,
87
        // if provided adds another link to the last page available.
88
        TotalCount *int64
89

90
        // HasNext instructs adding the "next" link header. This option
91
        // has no effect if TotalCount is given.
92
        HasNext *bool
93

94
        // Pagination parameters
95
        Page, PerPage *int64
96
}
97

98
func NewPagingHints() *PagingHints {
3✔
99
        return new(PagingHints)
3✔
100
}
3✔
101

102
func (h *PagingHints) SetTotalCount(totalCount int64) *PagingHints {
3✔
103
        h.TotalCount = &totalCount
3✔
104
        return h
3✔
105
}
3✔
106

107
func (h *PagingHints) SetHasNext(hasNext bool) *PagingHints {
3✔
108
        h.HasNext = &hasNext
3✔
109
        return h
3✔
110
}
3✔
111

112
func (h *PagingHints) SetPage(page int64) *PagingHints {
3✔
113
        h.Page = &page
3✔
114
        return h
3✔
115
}
3✔
116

117
func (h *PagingHints) SetPerPage(perPage int64) *PagingHints {
3✔
118
        h.PerPage = &perPage
3✔
119
        return h
3✔
120
}
3✔
121

122
func MakePagingHeaders(r *http.Request, hints ...*PagingHints) ([]string, error) {
3✔
123
        // Parse hints
3✔
124
        hint := new(PagingHints)
3✔
125
        for _, h := range hints {
6✔
126
                if h == nil {
3✔
127
                        continue
×
128
                }
129
                if h.HasNext != nil {
6✔
130
                        hint.HasNext = h.HasNext
3✔
131
                }
3✔
132
                if h.TotalCount != nil {
6✔
133
                        hint.TotalCount = h.TotalCount
3✔
134
                }
3✔
135
                if h.Page != nil {
6✔
136
                        hint.Page = h.Page
3✔
137
                }
3✔
138
                if h.PerPage != nil {
6✔
139
                        hint.PerPage = h.PerPage
3✔
140
                }
3✔
141
        }
142
        if hint.Page == nil || hint.PerPage == nil {
3✔
143
                page, perPage, err := ParsePagingParameters(r)
×
144
                if err != nil {
×
145
                        return nil, err
×
146
                }
×
147
                hint.Page, hint.PerPage = &page, &perPage
×
148
        }
149
        locationURL := url.URL{
3✔
150
                Path:     r.URL.Path,
3✔
151
                RawQuery: r.URL.RawQuery,
3✔
152
                Fragment: r.URL.Fragment,
3✔
153
        }
3✔
154
        q := locationURL.Query()
3✔
155
        // Ensure per_page is set
3✔
156
        q.Set(perPageQueryParam, strconv.FormatInt(*hint.PerPage, 10))
3✔
157
        links := make([]string, 0, 4)
3✔
158
        q.Set(pageQueryParam, "1")
3✔
159
        locationURL.RawQuery = q.Encode()
3✔
160
        links = append(links, fmt.Sprintf(
3✔
161
                "<%s>; rel=\"first\"", locationURL.String(),
3✔
162
        ))
3✔
163
        if (*hint.Page) > 1 {
5✔
164
                q.Set(pageQueryParam, strconv.FormatInt(*hint.Page-1, 10))
2✔
165
                locationURL.RawQuery = q.Encode()
2✔
166
                links = append(links, fmt.Sprintf(
2✔
167
                        "<%s>; rel=\"prev\"", locationURL.String(),
2✔
168
                ))
2✔
169
        }
2✔
170

171
        // TotalCount takes precedence over HasNext
172
        if hint.TotalCount != nil && *hint.TotalCount > 0 {
6✔
173
                lastPage := (*hint.TotalCount-1) / *hint.PerPage + 1
3✔
174
                if *hint.Page < lastPage {
5✔
175
                        // Add "next" link
2✔
176
                        q.Set(pageQueryParam, strconv.FormatUint(uint64(*hint.Page)+1, 10))
2✔
177
                        locationURL.RawQuery = q.Encode()
2✔
178
                        links = append(links, fmt.Sprintf(
2✔
179
                                "<%s>; rel=\"next\"", locationURL.String(),
2✔
180
                        ))
2✔
181
                }
2✔
182
                // Add "last" link
183
                q.Set(pageQueryParam, strconv.FormatInt(lastPage, 10))
3✔
184
                locationURL.RawQuery = q.Encode()
3✔
185
                links = append(links, fmt.Sprintf(
3✔
186
                        "<%s>; rel=\"last\"", locationURL.String(),
3✔
187
                ))
3✔
188
        } else if hint.HasNext != nil && *hint.HasNext {
3✔
189
                q.Set(pageQueryParam, strconv.FormatUint(uint64(*hint.Page)+1, 10))
×
190
                locationURL.RawQuery = q.Encode()
×
191
                links = append(links, fmt.Sprintf(
×
192
                        "<%s>; rel=\"next\"", locationURL.String(),
×
193
                ))
×
194
        }
×
195

196
        return links, nil
3✔
197
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc