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

mendersoftware / mender-cli / 1779499080

22 Apr 2025 10:11AM UTC coverage: 1.737% (-30.1%) from 31.802%
1779499080

push

gitlab-ci

web-flow
Merge pull request #277 from alfrunes/MEN-7794

MEN-7794: Add support for pagination when listing devices

28 of 82 new or added lines in 4 files covered. (34.15%)

770 existing lines in 17 files now uncovered.

45 of 2590 relevant lines covered (1.74%)

0.04 hits per line

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

48.91
/client/devices/client.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
package devices
15

16
import (
17
        "encoding/json"
18
        "fmt"
19
        "io"
20
        "net/http"
21
        "net/http/httputil"
22
        "net/url"
23
        "os"
24
        "strconv"
25

26
        "github.com/mendersoftware/mender-cli/client"
27
        "github.com/mendersoftware/mender-cli/log"
28
)
29

30
type deviceData struct {
31
        ID           string `json:"id"`
32
        IdentityData struct {
33
                Mac string `json:"mac"`
34
                Sku string `json:"sku"`
35
                Sn  string `json:"sn"`
36
        } `json:"identity_data"`
37
        Status    string `json:"status"`
38
        CreatedTs string `json:"created_ts"`
39
        UpdatedTs string `json:"updated_ts"`
40
        AuthSets  []struct {
41
                ID           string `json:"id"`
42
                PubKey       string `json:"pubkey"`
43
                IdentityData struct {
44
                        Mac string `json:"mac"`
45
                        Sku string `json:"sku"`
46
                        Sn  string `json:"sn"`
47
                } `json:"identity_data"`
48
                Status string `json:"status"`
49
                Ts     string `json:"ts"`
50
        } `json:"auth_sets"`
51
        Decommissioning bool `json:"decommissioning"`
52
}
53

54
const (
55
        devicesListURL = "/api/management/v2/devauth/devices"
56
)
57

58
type Client struct {
59
        url            string
60
        devicesListURL string
61
        client         *http.Client
62
        output         io.Writer
63
}
64

65
func NewClient(url string, skipVerify bool) *Client {
2✔
66
        return &Client{
2✔
67
                url:            url,
2✔
68
                devicesListURL: client.JoinURL(url, devicesListURL),
2✔
69
                client:         client.NewHttpClient(skipVerify),
2✔
70
                output:         os.Stdout,
2✔
71
        }
2✔
72
}
2✔
73

74
func (c *Client) ListDevices(token string, detailLevel, perPage, page int, raw bool) error {
2✔
75
        if detailLevel > 3 || detailLevel < 0 {
2✔
76
                return fmt.Errorf("FAILURE: invalid devices detail")
×
77
        }
×
78

79
        req, err := http.NewRequest(http.MethodGet, c.devicesListURL, nil)
2✔
80
        if err != nil {
2✔
NEW
81
                return fmt.Errorf("failed to prepare request: %w", err)
×
NEW
82
        }
×
83
        req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
2✔
84
        q := url.Values{
2✔
85
                "per_page": []string{strconv.Itoa(perPage)},
2✔
86
                "page":     []string{strconv.Itoa(page)},
2✔
87
        }
2✔
88
        req.URL.RawQuery = q.Encode()
2✔
89

2✔
90
        reqDump, err := httputil.DumpRequest(req, false)
2✔
91
        if err != nil {
2✔
NEW
92
                return err
×
NEW
93
        }
×
94
        log.Verbf("sending request: \n%s", string(reqDump))
2✔
95

2✔
96
        rsp, err := c.client.Do(req)
2✔
97
        if err != nil {
2✔
98
                return err
×
99
        }
×
100
        defer rsp.Body.Close()
2✔
101
        if rsp.StatusCode != 200 {
2✔
NEW
102
                return fmt.Errorf("GET %s request failed with status %d",
×
NEW
103
                        req.URL.RequestURI(), rsp.StatusCode)
×
NEW
104
        }
×
105

106
        if raw {
2✔
NEW
107
                _, err := io.Copy(os.Stdout, rsp.Body)
×
NEW
108
                if err != nil {
×
NEW
109
                        return fmt.Errorf("error reading response body: %w", err)
×
NEW
110
                }
×
111
        } else {
2✔
112
                var list []deviceData
2✔
113
                err = json.NewDecoder(rsp.Body).Decode(&list)
2✔
114
                if err != nil {
2✔
115
                        return err
×
116
                }
×
117
                for _, v := range list {
4✔
118
                        listDevice(c.output, v, detailLevel)
2✔
119
                }
2✔
120
        }
121
        return nil
2✔
122
}
123

124
func listDevice(out io.Writer, a deviceData, detailLevel int) {
2✔
125
        fmt.Fprintf(out, "ID: %s\n", a.ID)
2✔
126
        fmt.Fprintf(out, "Status: %s\n", a.Status)
2✔
127
        if detailLevel >= 1 {
2✔
128
                fmt.Println("IdentityData:")
×
129
                if a.IdentityData.Mac != "" {
×
NEW
130
                        fmt.Fprintf(out, "  MAC address: %s\n", a.IdentityData.Mac)
×
131
                }
×
132
                if a.IdentityData.Sku != "" {
×
NEW
133
                        fmt.Fprintf(out, "  Stock keeping unit: %s\n", a.IdentityData.Sku)
×
134
                }
×
135
                if a.IdentityData.Sn != "" {
×
NEW
136
                        fmt.Fprintf(out, "  Serial number: %s\n", a.IdentityData.Sn)
×
137
                }
×
138
        }
139
        if detailLevel >= 1 {
2✔
NEW
140
                fmt.Fprintf(out, "CreatedTs: %s\n", a.CreatedTs)
×
NEW
141
                fmt.Fprintf(out, "UpdatedTs: %s\n", a.UpdatedTs)
×
NEW
142
                fmt.Fprintf(out, "Decommissioning: %t\n", a.Decommissioning)
×
UNCOV
143
        }
×
144
        if detailLevel >= 2 {
2✔
145
                for i, v := range a.AuthSets {
×
NEW
146
                        fmt.Fprintf(out, "AuthSet[%d]:\n", i)
×
NEW
147
                        fmt.Fprintf(out, "  ID: %s\n", v.ID)
×
NEW
148
                        fmt.Fprintf(out, "  PubKey:\n%s", v.PubKey)
×
149
                        fmt.Println("  IdentityData:")
×
150
                        if v.IdentityData.Mac != "" {
×
NEW
151
                                fmt.Fprintf(out, "    MAC address: %s\n", v.IdentityData.Mac)
×
152
                        }
×
153
                        if v.IdentityData.Sku != "" {
×
NEW
154
                                fmt.Fprintf(out, "    Stock keeping unit: %s\n", v.IdentityData.Sku)
×
155
                        }
×
156
                        if v.IdentityData.Sn != "" {
×
NEW
157
                                fmt.Fprintf(out, "    Serial number: %s\n", v.IdentityData.Sn)
×
158
                        }
×
NEW
159
                        fmt.Fprintf(out, "  Status: %s\n", v.Status)
×
NEW
160
                        fmt.Fprintf(out, "  Ts: %s\n", v.Ts)
×
161
                }
162
        }
163

164
        fmt.Fprintf(
2✔
165
                out, "--------------------------------------------------------------------------------",
2✔
166
        )
2✔
167
}
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