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

mendersoftware / reporting / 858111268

pending completion
858111268

Pull #139

gitlab-ci

Peter Grzybowski
feat: extend with device checkin data from device-auth
Pull Request #139: feat: extend with device checkin

28 of 40 new or added lines in 6 files covered. (70.0%)

12 existing lines in 1 file now uncovered.

2988 of 3507 relevant lines covered (85.2%)

18.08 hits per line

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

98.97
/model/device.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 model
16

17
import (
18
        "encoding/json"
19
        "errors"
20
        "strings"
21
        "time"
22
)
23

24
type Device struct {
25
        ID                  *string             `json:"id"`
26
        TenantID            *string             `json:"tenant_id,omitempty"`
27
        Location            *string             `json:"location,omitempty"`
28
        IdentityAttributes  InventoryAttributes `json:"identity_attributes,omitempty"`
29
        InventoryAttributes InventoryAttributes `json:"inventory_attributes,omitempty"`
30
        MonitorAttributes   InventoryAttributes `json:"monitor_attributes,omitempty"`
31
        SystemAttributes    InventoryAttributes `json:"system_attributes,omitempty"`
32
        TagsAttributes      InventoryAttributes `json:"tags_attributes,omitempty"`
33
        UpdatedAt           *time.Time          `json:"updated_at,omitempty"`
34
        LastCheckInDate     *time.Time          `json:"check_in_time,omitempty"`
35
}
36

37
func NewDevice(tenantID, id string) *Device {
17✔
38
        return &Device{
17✔
39
                ID:       &id,
17✔
40
                TenantID: &tenantID,
17✔
41
        }
17✔
42
}
17✔
43

44
func (a *Device) AppendAttr(attr *InventoryAttribute) error {
65✔
45
        switch attr.Scope {
65✔
46
        case ScopeIdentity:
17✔
47
                a.IdentityAttributes = append(a.IdentityAttributes, attr)
17✔
48
                return nil
17✔
49
        case ScopeInventory:
35✔
50
                a.InventoryAttributes = append(a.InventoryAttributes, attr)
35✔
51
                return nil
35✔
52
        case ScopeMonitor:
2✔
53
                a.MonitorAttributes = append(a.MonitorAttributes, attr)
2✔
54
                return nil
2✔
55
        case ScopeSystem:
7✔
56
                a.SystemAttributes = append(a.SystemAttributes, attr)
7✔
57
                return nil
7✔
58
        case ScopeTags:
2✔
59
                a.TagsAttributes = append(a.TagsAttributes, attr)
2✔
60
                return nil
2✔
61
        default:
2✔
62
                return errors.New("unknown attribute scope " + attr.Scope)
2✔
63
        }
64
}
65

66
func (a *Device) GetID() string {
21✔
67
        if a.ID != nil {
40✔
68
                return *a.ID
19✔
69
        }
19✔
70
        return ""
2✔
71
}
72

73
func (a *Device) SetID(val string) *Device {
2✔
74
        a.ID = &val
2✔
75
        return a
2✔
76
}
2✔
77

78
func (a *Device) GetTenantID() string {
36✔
79
        if a.TenantID != nil {
70✔
80
                return *a.TenantID
34✔
81
        }
34✔
82
        return ""
2✔
83
}
84

85
func (a *Device) SetTenantID(val string) *Device {
2✔
86
        a.TenantID = &val
2✔
87
        return a
2✔
88
}
2✔
89

90
func (a *Device) GetUpdatedAt() time.Time {
4✔
91
        if a.UpdatedAt != nil {
6✔
92
                return *a.UpdatedAt
2✔
93
        }
2✔
94
        return time.Time{}
2✔
95
}
96

97
func (a *Device) SetUpdatedAt(val time.Time) *Device {
17✔
98
        if !val.IsZero() {
34✔
99
                a.UpdatedAt = &val
17✔
100
        }
17✔
101
        return a
17✔
102
}
103

104
func (a *Device) SetLastCheckIn(val time.Time) *Device {
15✔
105
        if !val.IsZero() {
15✔
NEW
106
                a.LastCheckInDate = &val
×
NEW
107
        }
×
108
        return a
15✔
109
}
110

111
type InventoryAttributes []*InventoryAttribute
112

113
type InventoryAttribute struct {
114
        Scope   string
115
        Name    string
116
        String  []string
117
        Numeric []float64
118
        Boolean []bool
119
}
120

121
func NewInventoryAttribute(s string) *InventoryAttribute {
47✔
122
        return &InventoryAttribute{
47✔
123
                Scope: s,
47✔
124
        }
47✔
125
}
47✔
126

127
func (a *InventoryAttribute) IsStr() bool {
65✔
128
        return a.String != nil
65✔
129
}
65✔
130

131
func (a *InventoryAttribute) IsNum() bool {
22✔
132
        return a.Numeric != nil
22✔
133
}
22✔
134

135
func (a *InventoryAttribute) IsBool() bool {
6✔
136
        return a.Boolean != nil
6✔
137
}
6✔
138

139
func (a *InventoryAttribute) SetName(val string) *InventoryAttribute {
45✔
140
        a.Name = val
45✔
141
        return a
45✔
142
}
45✔
143

144
func (a *InventoryAttribute) SetString(val string) *InventoryAttribute {
23✔
145
        a.String = []string{val}
23✔
146
        a.Boolean = nil
23✔
147
        a.Numeric = nil
23✔
148
        return a
23✔
149
}
23✔
150

151
func (a *InventoryAttribute) SetStrings(val []string) *InventoryAttribute {
2✔
152
        a.String = val
2✔
153
        a.Boolean = nil
2✔
154
        a.Numeric = nil
2✔
155
        return a
2✔
156
}
2✔
157

158
func (a *InventoryAttribute) SetNumeric(val float64) *InventoryAttribute {
14✔
159
        a.Numeric = []float64{val}
14✔
160
        a.Boolean = nil
14✔
161
        a.String = nil
14✔
162
        return a
14✔
163
}
14✔
164

165
func (a *InventoryAttribute) SetNumerics(val []float64) *InventoryAttribute {
2✔
166
        a.Numeric = val
2✔
167
        a.String = nil
2✔
168
        a.Boolean = nil
2✔
169
        return a
2✔
170
}
2✔
171

172
func (a *InventoryAttribute) SetBoolean(val bool) *InventoryAttribute {
2✔
173
        a.Boolean = []bool{val}
2✔
174
        a.Numeric = nil
2✔
175
        a.String = nil
2✔
176
        return a
2✔
177
}
2✔
178

179
func (a *InventoryAttribute) SetBooleans(val []bool) *InventoryAttribute {
2✔
180
        a.Boolean = val
2✔
181
        a.Numeric = nil
2✔
182
        a.String = nil
2✔
183
        return a
2✔
184
}
2✔
185

186
// SetVal inspects the 'val' type and sets the correct subtype field
187
// useful for translating from inventory attributes (interface{})
188
func (a *InventoryAttribute) SetVal(val interface{}) *InventoryAttribute {
45✔
189
        switch val := val.(type) {
45✔
190
        case bool:
2✔
191
                a.SetBoolean(val)
2✔
192
        case float64:
14✔
193
                a.SetNumeric(val)
14✔
194
        case string:
23✔
195
                a.SetString(val)
23✔
196
        case []interface{}:
6✔
197
                switch val[0].(type) {
6✔
198
                case bool:
2✔
199
                        bools := make([]bool, len(val))
2✔
200
                        for i, v := range val {
6✔
201
                                bools[i] = v.(bool)
4✔
202
                        }
4✔
203
                        a.SetBooleans(bools)
2✔
204
                case float64:
2✔
205
                        nums := make([]float64, len(val))
2✔
206
                        for i, v := range val {
6✔
207
                                nums[i] = v.(float64)
4✔
208
                        }
4✔
209
                        a.SetNumerics(nums)
2✔
210
                case string:
2✔
211
                        strs := make([]string, len(val))
2✔
212
                        for i, v := range val {
6✔
213
                                strs[i] = v.(string)
4✔
214
                        }
4✔
215
                        a.SetStrings(strs)
2✔
216
                }
217
        }
218

219
        return a
45✔
220
}
221

222
func (d *Device) MarshalJSON() ([]byte, error) {
17✔
223
        // TODO: smarter encoding, without explicit rewrites?
17✔
224
        m := make(map[string]interface{})
17✔
225
        m[FieldNameID] = d.ID
17✔
226
        m[FieldNameTenantID] = d.TenantID
17✔
227
        m[FieldNameLocation] = d.Location
17✔
228
        m[FieldNameCheckIn] = d.LastCheckInDate
17✔
229

17✔
230
        attributes := append(d.IdentityAttributes, d.InventoryAttributes...)
17✔
231
        attributes = append(attributes, d.MonitorAttributes...)
17✔
232
        attributes = append(attributes, d.SystemAttributes...)
17✔
233
        attributes = append(attributes, d.TagsAttributes...)
17✔
234

17✔
235
        for _, a := range attributes {
80✔
236
                name, val := a.Map()
63✔
237
                m[name] = val
63✔
238
        }
63✔
239

240
        return json.Marshal(m)
17✔
241
}
242

243
func (a *InventoryAttribute) Map() (string, interface{}) {
63✔
244
        var val interface{}
63✔
245
        var typ Type
63✔
246

63✔
247
        if a.IsStr() {
106✔
248
                typ = TypeStr
43✔
249
                val = a.String
43✔
250
        } else if a.IsNum() {
79✔
251
                typ = TypeNum
16✔
252
                val = a.Numeric
16✔
253
        } else if a.IsBool() {
24✔
254
                typ = TypeBool
4✔
255
                val = a.Boolean
4✔
256
        }
4✔
257

258
        name := ToAttr(a.Scope, a.Name, typ)
63✔
259

63✔
260
        return name, val
63✔
261
}
262

263
// maybeParseAttr decides if a given field is an attribute and parses
264
// it's name + scope
265
func MaybeParseAttr(field string) (string, string, error) {
240✔
266
        scope := ""
240✔
267
        name := ""
240✔
268

240✔
269
        for _, s := range []string{ScopeIdentity, ScopeInventory, ScopeMonitor,
240✔
270
                ScopeSystem, ScopeTags} {
1,085✔
271
                if strings.HasPrefix(field, s+"_") {
961✔
272
                        scope = s
116✔
273
                        break
116✔
274
                }
275
        }
276

277
        if scope != "" {
356✔
278
                for _, s := range []string{typeStr, typeNum} {
348✔
279
                        if strings.HasSuffix(field, "_"+s) {
348✔
280
                                // strip the prefix/suffix
116✔
281
                                start := strings.Index(field, "_")
116✔
282
                                end := strings.LastIndex(field, "_")
116✔
283

116✔
284
                                name = field[start+1 : end]
116✔
285
                        }
116✔
286
                }
287
        }
288

289
        return scope, name, nil
240✔
290
}
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