Skip to content

Commit 60e85d9

Browse files
authored
[useragent] Added a useragent module to help generate User-Agent header (#589)
<!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description - Moved utilities to generate user agent header - Modified the `IsEmpty` behaviour to be more performant with regards to strings ### Test Coverage <!-- Please put an `x` in the correct box e.g. `[x]` to indicate the testing coverage of this change. --> - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update).
1 parent d27a7c2 commit 60e85d9

File tree

6 files changed

+112
-5
lines changed

6 files changed

+112
-5
lines changed

changes/20250328120125.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:recycle: [reflection] Modified IsEmpty behaviour with regards to strings to consider strings with only whitespaces as empty

changes/20250328120920.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: `[useragent]` Added a `useragent` module to help generate `User-Agent`

utils/http/useragent/useragent.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package useragent
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/ARM-software/golang-utils/utils/commonerrors"
8+
"github.com/ARM-software/golang-utils/utils/reflection"
9+
)
10+
11+
// AddValuesToUserAgent extends a user agent string with new elements. See https://en.wikipedia.org/wiki/User-Agent_header#Format_for_human-operated_web_browsers
12+
func AddValuesToUserAgent(userAgent string, elements ...string) (newUserAgent string) {
13+
if len(elements) == 0 {
14+
newUserAgent = userAgent
15+
return
16+
}
17+
newUserAgent = strings.Join(elements, " ")
18+
newUserAgent = strings.TrimSpace(newUserAgent)
19+
if newUserAgent == "" {
20+
newUserAgent = userAgent
21+
return
22+
}
23+
if !reflection.IsEmpty(userAgent) {
24+
newUserAgent = fmt.Sprintf("%v %v", userAgent, newUserAgent)
25+
}
26+
return
27+
}
28+
29+
// GenerateUserAgentValue generates a user agent value. See https://en.wikipedia.org/wiki/User-Agent_header#Format_for_human-operated_web_browsers
30+
func GenerateUserAgentValue(product string, productVersion string, comment string) (userAgent string, err error) {
31+
if reflection.IsEmpty(product) {
32+
err = commonerrors.UndefinedVariable("product")
33+
return
34+
}
35+
if reflection.IsEmpty(productVersion) {
36+
err = commonerrors.UndefinedVariable("product version")
37+
return
38+
}
39+
userAgent = fmt.Sprintf("%v/%v", product, productVersion)
40+
if !reflection.IsEmpty(comment) {
41+
userAgent = fmt.Sprintf("%v (%v)", userAgent, comment)
42+
}
43+
return
44+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package useragent
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-faker/faker/v4"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/ARM-software/golang-utils/utils/commonerrors"
11+
"github.com/ARM-software/golang-utils/utils/commonerrors/errortest"
12+
)
13+
14+
func TestAddValuesToUserAgent(t *testing.T) {
15+
userAgent, err := GenerateUserAgentValue(faker.Name(), faker.Word(), faker.Sentence())
16+
require.NoError(t, err)
17+
newAgent := AddValuesToUserAgent(userAgent, faker.Word())
18+
assert.Contains(t, newAgent, userAgent)
19+
elem := faker.Word()
20+
newAgent = AddValuesToUserAgent(" ", elem)
21+
assert.Equal(t, elem, newAgent)
22+
}
23+
24+
func TestGenerateUserAgentValue(t *testing.T) {
25+
userAgent, err := GenerateUserAgentValue("", "", "")
26+
errortest.AssertError(t, err, commonerrors.ErrUndefined, commonerrors.ErrInvalid)
27+
assert.Empty(t, userAgent)
28+
userAgent, err = GenerateUserAgentValue(" ", " ", faker.Sentence())
29+
errortest.AssertError(t, err, commonerrors.ErrUndefined, commonerrors.ErrInvalid)
30+
assert.Empty(t, userAgent)
31+
userAgent, err = GenerateUserAgentValue(faker.Name(), "", faker.Sentence())
32+
errortest.AssertError(t, err, commonerrors.ErrUndefined, commonerrors.ErrInvalid)
33+
assert.Empty(t, userAgent)
34+
userAgent, err = GenerateUserAgentValue(faker.Name(), faker.Word(), faker.Sentence())
35+
assert.NoError(t, err)
36+
assert.NotEmpty(t, userAgent)
37+
}

utils/reflection/reflection.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package reflection
77
import (
88
"fmt"
99
"reflect"
10+
"strings"
1011
"unsafe"
1112

1213
"github.com/ARM-software/golang-utils/utils/commonerrors"
@@ -195,10 +196,21 @@ func InheritsFrom(object interface{}, parentType reflect.Type) bool {
195196
}
196197

197198
// IsEmpty checks whether a value is empty i.e. "", nil, 0, [], {}, false, etc.
198-
func IsEmpty(value interface{}) bool {
199+
// For Strings, a string is considered empty if it is "" or if it only contains whitespaces
200+
func IsEmpty(value any) bool {
199201
if value == nil {
200202
return true
201203
}
204+
if valueStr, ok := value.(string); ok {
205+
return len(strings.TrimSpace(valueStr)) == 0
206+
}
207+
if valueStrPtr, ok := value.(*string); ok {
208+
return len(strings.TrimSpace(*valueStrPtr)) == 0
209+
}
210+
if valueBool, ok := value.(bool); ok {
211+
// if set to true, then value is not empty
212+
return !valueBool
213+
}
202214
objValue := reflect.ValueOf(value)
203215
switch objValue.Kind() {
204216
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:

utils/reflection/reflection_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/stretchr/testify/require"
1616

1717
"github.com/ARM-software/golang-utils/utils/commonerrors"
18+
"github.com/ARM-software/golang-utils/utils/field"
1819
)
1920

2021
type structtest struct {
@@ -398,8 +399,9 @@ func TestIsEmpty(t *testing.T) {
398399
aFilledChannel := make(chan struct{}, 1)
399400
aFilledChannel <- struct{}{}
400401
tests := []struct {
401-
value interface{}
402-
isEmpty bool
402+
value interface{}
403+
isEmpty bool
404+
differsFromAssertEmpty bool
403405
}{
404406
{
405407
value: nil,
@@ -421,6 +423,16 @@ func TestIsEmpty(t *testing.T) {
421423
value: "",
422424
isEmpty: true,
423425
},
426+
{
427+
value: " ",
428+
isEmpty: true,
429+
differsFromAssertEmpty: true,
430+
},
431+
{
432+
value: field.ToOptionalString(" "),
433+
isEmpty: true,
434+
differsFromAssertEmpty: true,
435+
},
424436
{
425437
value: false,
426438
isEmpty: true,
@@ -485,9 +497,9 @@ func TestIsEmpty(t *testing.T) {
485497

486498
for i := range tests {
487499
test := tests[i]
488-
t.Run(fmt.Sprintf("subtest #%v", i), func(t *testing.T) {
500+
t.Run(fmt.Sprintf("subtest #%v (%v)", i, test.value), func(t *testing.T) {
489501
assert.Equal(t, test.isEmpty, IsEmpty(test.value))
490-
if test.isEmpty {
502+
if test.isEmpty && !test.differsFromAssertEmpty {
491503
assert.Empty(t, test.value)
492504
} else {
493505
assert.NotEmpty(t, test.value)

0 commit comments

Comments
 (0)