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

mendersoftware / mender-server / 1812619803

12 May 2025 09:48AM UTC coverage: 65.361%. First build
1812619803

Pull #653

gitlab-ci

bahaa-ghazal
refactor(inventory): Migrate from ant0nie/go-json-rest to gin-gonic/gin

Ticket: MEN-8236
Changelog: Title
Signed-off-by: Bahaa Aldeen Ghazal <bahaa.ghazal@northern.tech>
Pull Request #653: refactor(inventory): Migrate from ant0nie/go-json-rest to gin-gonic/gin

419 of 483 new or added lines in 8 files covered. (86.75%)

31906 of 48815 relevant lines covered (65.36%)

1.37 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

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

NEW
44
func ErrQueryParmLimit(name string) error {
×
NEW
45
        return fmt.Errorf("invalid %s query: value must be a non-zero positive integer", name)
×
NEW
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) {
3✔
51
        q := r.URL.Query()
3✔
52
        var (
3✔
53
                err     error
3✔
54
                page    int64
3✔
55
                perPage int64
3✔
56
        )
3✔
57
        qPage := q.Get(pageQueryParam)
3✔
58
        if qPage == "" {
6✔
59
                page = 1
3✔
60
        } else {
4✔
61
                page, err = strconv.ParseInt(qPage, 10, 64)
1✔
62
                if err != nil {
1✔
NEW
63
                        return -1, -1, ErrQueryParmInvalid("page", qPage)
×
64
                } else if page < 1 {
1✔
NEW
65
                        return -1, -1, ErrQueryParmLimit("page")
×
66
                }
×
67
        }
68

69
        qPerPage := q.Get(perPageQueryParam)
3✔
70
        if qPerPage == "" {
6✔
71
                perPage = PerPageDefault
3✔
72
        } else {
4✔
73
                perPage, err = strconv.ParseInt(qPerPage, 10, 64)
1✔
74
                if err != nil {
1✔
NEW
75
                        return -1, -1, ErrQueryParmInvalid("per_page", qPerPage)
×
76
                } else if page < 1 {
1✔
NEW
77
                        return -1, -1, ErrQueryParmLimit("per_page")
×
78
                } else if perPage > PerPageMax {
1✔
79
                        return page, perPage, ErrPerPageLimit
×
80
                }
×
81
        }
82
        return page, perPage, nil
3✔
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 {
2✔
99
        return new(PagingHints)
2✔
100
}
2✔
101

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

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

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

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

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

171
        // TotalCount takes precedence over HasNext
172
        if hint.TotalCount != nil && *hint.TotalCount > 0 {
4✔
173
                lastPage := (*hint.TotalCount-1) / *hint.PerPage + 1
2✔
174
                if *hint.Page < lastPage {
4✔
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))
2✔
184
                locationURL.RawQuery = q.Encode()
2✔
185
                links = append(links, fmt.Sprintf(
2✔
186
                        "<%s>; rel=\"last\"", locationURL.String(),
2✔
187
                ))
2✔
188
        } else if hint.HasNext != nil && *hint.HasNext {
2✔
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
2✔
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