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

mendersoftware / reporting / 992949688

05 Sep 2023 09:18AM UTC coverage: 89.001% (+4.0%) from 85.036%
992949688

Pull #138

gitlab-ci

web-flow
Merge pull request #163 from mendersoftware/combined-prs-branch

Combined PRs
Pull Request #138: Align staging with master

177 of 199 new or added lines in 8 files covered. (88.94%)

2 existing lines in 1 file now uncovered.

2476 of 2782 relevant lines covered (89.0%)

18.04 hits per line

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

97.96
/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 {
16✔
38
        return &Device{
16✔
39
                ID:       &id,
16✔
40
                TenantID: &tenantID,
16✔
41
        }
16✔
42
}
16✔
43

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

66
func (a *Device) GetID() string {
18✔
67
        if a.ID != nil {
35✔
68
                return *a.ID
17✔
69
        }
17✔
70
        return ""
1✔
71
}
72

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

78
func (a *Device) GetTenantID() string {
33✔
79
        if a.TenantID != nil {
65✔
80
                return *a.TenantID
32✔
81
        }
32✔
82
        return ""
1✔
83
}
84

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

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

97
func (a *Device) SetUpdatedAt(val time.Time) *Device {
16✔
98
        if !val.IsZero() {
32✔
99
                a.UpdatedAt = &val
16✔
100
        }
16✔
101
        return a
16✔
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 {
40✔
122
        return &InventoryAttribute{
40✔
123
                Scope: s,
40✔
124
        }
40✔
125
}
40✔
126

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

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

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

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

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

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

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

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

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

179
func (a *InventoryAttribute) SetBooleans(val []bool) *InventoryAttribute {
1✔
180
        a.Boolean = val
1✔
181
        a.Numeric = nil
1✔
182
        a.String = nil
1✔
183
        return a
1✔
184
}
1✔
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 {
39✔
189
        switch val := val.(type) {
39✔
190
        case bool:
1✔
191
                a.SetBoolean(val)
1✔
192
        case float64:
13✔
193
                a.SetNumeric(val)
13✔
194
        case string:
22✔
195
                a.SetString(val)
22✔
196
        case []interface{}:
3✔
197
                switch val[0].(type) {
3✔
198
                case bool:
1✔
199
                        bools := make([]bool, len(val))
1✔
200
                        for i, v := range val {
3✔
201
                                bools[i] = v.(bool)
2✔
202
                        }
2✔
203
                        a.SetBooleans(bools)
1✔
204
                case float64:
1✔
205
                        nums := make([]float64, len(val))
1✔
206
                        for i, v := range val {
3✔
207
                                nums[i] = v.(float64)
2✔
208
                        }
2✔
209
                        a.SetNumerics(nums)
1✔
210
                case string:
1✔
211
                        strs := make([]string, len(val))
1✔
212
                        for i, v := range val {
3✔
213
                                strs[i] = v.(string)
2✔
214
                        }
2✔
215
                        a.SetStrings(strs)
1✔
216
                }
217
        }
218

219
        return a
39✔
220
}
221

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

232
        attributes := append(d.IdentityAttributes, d.InventoryAttributes...)
16✔
233
        attributes = append(attributes, d.MonitorAttributes...)
16✔
234
        attributes = append(attributes, d.SystemAttributes...)
16✔
235
        attributes = append(attributes, d.TagsAttributes...)
16✔
236

16✔
237
        for _, a := range attributes {
73✔
238
                name, val := a.Map()
57✔
239
                m[name] = val
57✔
240
        }
57✔
241

242
        return json.Marshal(m)
16✔
243
}
244

245
func (a *InventoryAttribute) Map() (string, interface{}) {
57✔
246
        var val interface{}
57✔
247
        var typ Type
57✔
248

57✔
249
        if a.IsStr() {
98✔
250
                typ = TypeStr
41✔
251
                val = a.String
41✔
252
        } else if a.IsNum() {
71✔
253
                typ = TypeNum
14✔
254
                val = a.Numeric
14✔
255
        } else if a.IsBool() {
18✔
256
                typ = TypeBool
2✔
257
                val = a.Boolean
2✔
258
        }
2✔
259

260
        name := ToAttr(a.Scope, a.Name, typ)
57✔
261

57✔
262
        return name, val
57✔
263
}
264

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

208✔
271
        for _, s := range []string{ScopeIdentity, ScopeInventory, ScopeMonitor,
208✔
272
                ScopeSystem, ScopeTags} {
895✔
273
                if strings.HasPrefix(field, s+"_") {
802✔
274
                        scope = s
115✔
275
                        break
115✔
276
                }
277
        }
278

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

115✔
286
                                name = field[start+1 : end]
115✔
287
                        }
115✔
288
                }
289
        }
290

291
        return scope, name, nil
208✔
292
}
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