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

mendersoftware / mender-server / 1961830404

01 Aug 2025 06:29PM UTC coverage: 65.815% (+0.3%) from 65.555%
1961830404

Pull #849

gitlab-ci

web-flow
chore: bump the backend-docker-dependencies group across 10 directories with 2 updates

Bumps the backend-docker-dependencies group with 2 updates in the /backend/services/create-artifact-worker directory: golang and alpine.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/deployments directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/deviceauth directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/deviceconfig directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/deviceconnect directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/inventory directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/iot-manager directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/reporting directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/useradm directory: golang.
Bumps the backend-docker-dependencies group with 1 update in the /backend/services/workflows directory: golang.


Updates `golang` from 1.24.4 to 1.24.5

Updates `alpine` from 3.22.0 to 3.22.1

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

Updates `golang` from 1.24.4 to 1.24.5

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: backend-docker-dependencies
- dependency-name: alpine
  dependency-version: 3.22.1
  dependency-type: direct:production... (continued)
Pull Request #849: chore: bump the backend-docker-dependencies group across 10 directories with 2 updates

29335 of 44572 relevant lines covered (65.81%)

1.44 hits per line

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

26.63
/backend/pkg/mongo/doc/bson.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 doc
16

17
import (
18
        "reflect"
19
        "strings"
20

21
        "github.com/pkg/errors"
22
        "go.mongodb.org/mongo-driver/bson"
23
)
24

25
// MarshallBSONOrDocumentFromStruct marshals a structure to BSON if it implements
26
// the bson.Marshaler interface, otherwise invokes DocumentFromStruct on it.
27
func MarshallBSONOrDocumentFromStruct(
28
        sct interface{},
29
        appendElements ...bson.E,
30
) (doc bson.D) {
×
31
        if marshaller, ok := sct.(bson.Marshaler); ok {
×
32
                var doc bson.D
×
33
                if data, err := marshaller.MarshalBSON(); err == nil {
×
34
                        _ = bson.Unmarshal(data, &doc)
×
35
                }
×
36
                return doc
×
37
        }
38
        return DocumentFromStruct(sct, appendElements...)
×
39
}
40

41
// DocumentFromStruct creates a bson document from a struct in the order of the
42
// underlying data structure. Additional fields can be appended to the struct
43
// with the appendElements, these fields will be added at the end of the
44
// document.
45
func DocumentFromStruct(
46
        sct interface{},
47
        appendElements ...bson.E,
48
) (doc bson.D) {
6✔
49
        s := reflect.ValueOf(sct)
6✔
50
        defer func() {
12✔
51
                if r := recover(); r != nil {
6✔
52
                        doc = nil
×
53
                }
×
54
        }()
55

56
        s = dereferenceValue(s)
6✔
57
        if s.Kind() != reflect.Struct {
6✔
58
                return nil
×
59
        }
×
60

61
        numAppends := len(appendElements)
6✔
62
        numFields := s.NumField()
6✔
63
        doc = make(bson.D, 0, numFields+numAppends)
6✔
64
        fields := s.Type()
6✔
65
        for i := 0; i < numFields; i++ {
12✔
66
                field := fields.Field(i)
6✔
67
                value := s.Field(i)
6✔
68
                key, valFace, set := valueFromStructField(field, value)
6✔
69
                if key == "inline" {
9✔
70
                        doc = append(doc, DocumentFromStruct(valFace)...)
3✔
71
                        continue
3✔
72
                }
73
                if set {
12✔
74
                        doc = append(doc, bson.E{Key: key, Value: valFace})
6✔
75
                }
6✔
76
        }
77
        for i := 0; i < numAppends; i++ {
12✔
78
                doc = append(doc, appendElements[i])
6✔
79
        }
6✔
80
        return doc
6✔
81
}
82

83
func dereferenceValue(val reflect.Value) reflect.Value {
6✔
84
        const maxDereference = 4
6✔
85
        for i := 0; i < maxDereference; i++ {
12✔
86
                switch val.Kind() {
6✔
87
                case reflect.Ptr:
3✔
88
                        val = val.Elem()
3✔
89
                case reflect.Interface:
×
90
                        val = val.Elem()
×
91
                }
92
        }
93
        return val
6✔
94
}
95

96
type FlattenOptions struct {
97
        // Transform provides an option for transforming key/value pairs in
98
        // the flattened array. The input contains the values that would
99
        // otherwise be added to the document. This can be useful for
100
        // transforming query containing arrays to add an $in operator.
101
        Transform func(key string, elem interface{}) (string, interface{})
102
}
103

104
func NewFlattenOptions() *FlattenOptions {
×
105
        return &FlattenOptions{}
×
106
}
×
107

108
func (opts *FlattenOptions) SetTransform(
109
        transform func(key string, elem interface{}) (string, interface{}),
110
) *FlattenOptions {
×
111
        opts.Transform = transform
×
112
        return opts
×
113
}
×
114

115
func mergeFlattenOptions(opts []*FlattenOptions) *FlattenOptions {
×
116
        var ret = &FlattenOptions{}
×
117
        for _, opt := range opts {
×
118
                if opt == nil {
×
119
                        continue
×
120
                }
121
                if opt.Transform != nil {
×
122
                        ret.Transform = opt.Transform
×
123
                }
×
124
        }
125
        return ret
×
126
}
127

128
// FlattenDocument consumes a struct or map and returns a flattened BSON
129
// document (bson.D) where embedded structs and maps are flattened into the
130
// root level of the map and field-names are concatenated by dots ('.').
131
// This function respects the "omitempty" bson tags such that it's possible
132
// to create queries from structs; the resulting document respects the struct
133
// declaration order with a depth first expansion of embedded fields. Please
134
// note, however, that there are no ordering guarantees on map-types.
135
//
136
//        type Foo struct {
137
//            Bar: map[string]string{
138
//                "baz": "foo"
139
//            },  `bson:"bar"`
140
//        }
141
//
142
// Becomes:
143
//
144
//        bson.D{
145
//          {Key: "bar.baz", Value: "foo"}
146
//        }
147
func FlattenDocument(
148
        mapping interface{}, options ...*FlattenOptions,
149
) (doc bson.D, err error) {
×
150
        defer func() {
×
151
                if r := recover(); r != nil {
×
152
                        switch v := r.(type) {
×
153
                        case error:
×
154
                                err = v
×
155
                        case string:
×
156
                                err = errors.New(v)
×
157

158
                        default:
×
159
                                panic(v)
×
160
                        }
161
                }
162
        }()
163
        opts := mergeFlattenOptions(options)
×
164

×
165
        s := reflect.ValueOf(mapping)
×
166
        s = dereferenceValue(s)
×
167

×
168
        switch s.Kind() {
×
169
        case reflect.Struct:
×
170
                return flattenStruct(s, "", opts), nil
×
171
        case reflect.Map:
×
172
                return flattenMap(s, "", opts), nil
×
173
        }
174
        return nil, errors.Errorf(
×
175
                "[programming error] invalid argument type %s, "+
×
176
                        "expected struct or map-like type",
×
177
                s.Kind(),
×
178
        )
×
179
}
180

181
func valueFromStructField(
182
        key reflect.StructField,
183
        value reflect.Value,
184
) (string, interface{}, bool) {
6✔
185
        if rune(key.Name[0]) >= 'a' && rune(key.Name[0]) <= 'z' {
6✔
186
                // unexported field
×
187
                return "", nil, false
×
188
        }
×
189
        tag := key.Tag.Get("bson")
6✔
190
        if tag == "" {
8✔
191
                tag = strings.ToLower(key.Name)
2✔
192
        }
2✔
193
        tags := strings.Split(tag, ",")
6✔
194
        name := tags[0]
6✔
195
        if name == "" {
6✔
196
                name = strings.ToLower(key.Name)
×
197
        }
×
198
        for _, t := range tags {
12✔
199
                if t == "omitempty" && value.IsZero() {
11✔
200
                        return "", nil, false
5✔
201
                }
5✔
202
        }
203
        return name, value.Interface(), true
6✔
204
}
205

206
func flattenStruct(
207
        sct reflect.Value,
208
        prefix string,
209
        options *FlattenOptions,
210
) (doc bson.D) {
×
211
        doc = bson.D{}
×
212
        sType := sct.Type()
×
213
        numSFields := sct.NumField()
×
214
        for i := 0; i < numSFields; i++ {
×
215
                sVal := sct.Field(i)
×
216
                sKey := sType.Field(i)
×
217

×
218
                sVal = dereferenceValue(sVal)
×
219
                fieldName, val, set := valueFromStructField(sKey, sVal)
×
220
                if !set {
×
221
                        continue
×
222
                }
223
                if len(prefix) > 0 {
×
224
                        fieldName = prefix + "." + fieldName
×
225
                }
×
226
                switch sVal.Kind() {
×
227
                case reflect.Struct:
×
228
                        ret := flattenStruct(sVal, fieldName, options)
×
229
                        if ret != nil {
×
230
                                doc = append(doc, ret...)
×
231
                        }
×
232
                case reflect.Map:
×
233
                        ret := flattenMap(sVal, fieldName, options)
×
234
                        if ret != nil {
×
235
                                doc = append(doc, ret...)
×
236
                        }
×
237
                default:
×
238
                        if options.Transform != nil {
×
239
                                fieldName, val = options.Transform(fieldName, val)
×
240
                        }
×
241
                        doc = append(doc, bson.E{
×
242
                                Key:   fieldName,
×
243
                                Value: val,
×
244
                        })
×
245
                }
246
        }
247
        return doc
×
248
}
249

250
func flattenMap(
251
        m reflect.Value, prefix string, options *FlattenOptions,
252
) (doc bson.D) {
×
253
        rKeys := m.MapKeys()
×
254
        for _, rKey := range rKeys {
×
255
                // NOTE: Will panic if map keys are not string!
×
256
                key := rKey.String()
×
257
                fieldName := key
×
258
                if prefix != "" {
×
259
                        fieldName = prefix + "." + fieldName
×
260
                }
×
261
                rVal := m.MapIndex(rKey)
×
262
                rVal = dereferenceValue(rVal)
×
263
                switch rVal.Kind() {
×
264
                case reflect.Struct:
×
265
                        ret := flattenStruct(rVal, fieldName, options)
×
266
                        if ret != nil {
×
267
                                doc = append(ret, doc...)
×
268
                        }
×
269
                case reflect.Map:
×
270
                        ret := flattenMap(rVal, fieldName, options)
×
271
                        if ret != nil {
×
272
                                doc = append(ret, doc...)
×
273
                        }
×
274
                default:
×
275
                        val := rVal.Interface()
×
276
                        if options.Transform != nil {
×
277
                                fieldName, val = options.Transform(fieldName, val)
×
278
                        }
×
279
                        doc = append(doc, bson.E{Key: fieldName, Value: val})
×
280
                }
281
        }
282
        return doc
×
283
}
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