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

mendersoftware / mender-server / 1807452801

08 May 2025 01:22PM UTC coverage: 65.386% (+0.1%) from 65.27%
1807452801

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

476 of 525 new or added lines in 6 files covered. (90.67%)

62 existing lines in 9 files now uncovered.

31949 of 48862 relevant lines covered (65.39%)

1.37 hits per line

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

78.4
/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 MsgQueryParmInvalid(param, name string) string {
×
NEW
41
        return fmt.Sprintf("invalid %s query: \"%s\"", param, name)
×
NEW
42
}
×
43

NEW
44
func MsgQueryParmLimit(name string) string {
×
NEW
45
        return fmt.Sprintf("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, errors.New(
×
NEW
64
                                MsgQueryParmInvalid("page", qPage))
×
65
                } else if page < 1 {
1✔
NEW
66
                        return -1, -1, errors.New(MsgQueryParmLimit("page"))
×
UNCOV
67
                }
×
68
        }
69

70
        qPerPage := q.Get(perPageQueryParam)
3✔
71
        if qPerPage == "" {
6✔
72
                perPage = PerPageDefault
3✔
73
        } else {
4✔
74
                perPage, err = strconv.ParseInt(qPerPage, 10, 64)
1✔
75
                if err != nil {
1✔
NEW
76
                        return -1, -1, errors.New(
×
NEW
77
                                MsgQueryParmInvalid("per_page", qPerPage))
×
78
                } else if page < 1 {
1✔
NEW
79
                        return -1, -1, errors.New(MsgQueryParmLimit("per_page"))
×
80
                } else if perPage > PerPageMax {
1✔
UNCOV
81
                        return page, perPage, ErrPerPageLimit
×
UNCOV
82
                }
×
83
        }
84
        return page, perPage, nil
3✔
85
}
86

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

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

96
        // Pagination parameters
97
        Page, PerPage *int64
98
}
99

100
func NewPagingHints() *PagingHints {
2✔
101
        return new(PagingHints)
2✔
102
}
2✔
103

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

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

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

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

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

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

198
        return links, nil
2✔
199
}
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