Skip to content

Commit 0bf1a75

Browse files
authored
[pkg/ottl]: Add ByteSliceLikeGetter interface (#33536)
**Description:** <Describe what has changed.> Adds ByteSliceLikeGetter interface due to future support of Hex() converter **Link to tracking Issue:** #31929 **Testing:** <Describe what testing was performed and which tests were added.> **Documentation:** <Describe the documentation added.> --------- Signed-off-by: odubajDT <[email protected]>
1 parent 86278d9 commit 0bf1a75

File tree

6 files changed

+339
-0
lines changed

6 files changed

+339
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add ByteSliceLikeGetter interface
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [31929]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [api]

pkg/ottl/LANGUAGE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ The following types are supported for single-value parameters in OTTL functions:
6969
- `IntLikeGetter`
7070
- `BoolGetter`
7171
- `BoolLikeGetter`
72+
- `ByteSliceLikeGetter`
7273
- `Enum`
7374
- `string`
7475
- `float64`

pkg/ottl/expression.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package ottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
55

66
import (
7+
"bytes"
78
"context"
9+
"encoding/binary"
810
"encoding/hex"
911
"fmt"
1012
"reflect"
@@ -549,6 +551,81 @@ func (g StandardIntLikeGetter[K]) Get(ctx context.Context, tCtx K) (*int64, erro
549551
return &result, nil
550552
}
551553

554+
// ByteSliceLikeGetter is a Getter that returns []byte by converting the underlying value to an []byte if necessary
555+
type ByteSliceLikeGetter[K any] interface {
556+
// Get retrieves []byte value.
557+
// The expectation is that the underlying value is converted to []byte if possible.
558+
// If the value cannot be converted to []byte, nil and an error are returned.
559+
// If the value is nil, nil is returned without an error.
560+
Get(ctx context.Context, tCtx K) ([]byte, error)
561+
}
562+
563+
type StandardByteSliceLikeGetter[K any] struct {
564+
Getter func(ctx context.Context, tCtx K) (any, error)
565+
}
566+
567+
func (g StandardByteSliceLikeGetter[K]) Get(ctx context.Context, tCtx K) ([]byte, error) {
568+
val, err := g.Getter(ctx, tCtx)
569+
if err != nil {
570+
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
571+
}
572+
if val == nil {
573+
return nil, nil
574+
}
575+
var result []byte
576+
switch v := val.(type) {
577+
case []byte:
578+
result = v
579+
case string:
580+
result = []byte(v)
581+
case float64, int64, bool:
582+
result, err = valueToBytes(v)
583+
if err != nil {
584+
return nil, fmt.Errorf("error converting value %f of %T: %w", v, g, err)
585+
}
586+
case pcommon.Value:
587+
switch v.Type() {
588+
case pcommon.ValueTypeBytes:
589+
result = v.Bytes().AsRaw()
590+
case pcommon.ValueTypeInt:
591+
result, err = valueToBytes(v.Int())
592+
if err != nil {
593+
return nil, fmt.Errorf("error converting value %d of int64: %w", v.Int(), err)
594+
}
595+
case pcommon.ValueTypeDouble:
596+
result, err = valueToBytes(v.Double())
597+
if err != nil {
598+
return nil, fmt.Errorf("error converting value %f of float64: %w", v.Double(), err)
599+
}
600+
case pcommon.ValueTypeStr:
601+
result = []byte(v.Str())
602+
case pcommon.ValueTypeBool:
603+
result, err = valueToBytes(v.Bool())
604+
if err != nil {
605+
return nil, fmt.Errorf("error converting value %s of bool: %w", v.Str(), err)
606+
}
607+
default:
608+
return nil, TypeError(fmt.Sprintf("unsupported value type: %v", v.Type()))
609+
}
610+
default:
611+
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
612+
}
613+
return result, nil
614+
}
615+
616+
// valueToBytes converts a value to a byte slice of length 8.
617+
func valueToBytes(n any) ([]byte, error) {
618+
// Create a buffer to hold the bytes
619+
buf := new(bytes.Buffer)
620+
// Write the value to the buffer using binary.Write
621+
err := binary.Write(buf, binary.BigEndian, n)
622+
if err != nil {
623+
return nil, err
624+
}
625+
626+
return buf.Bytes(), nil
627+
}
628+
552629
// BoolLikeGetter is a Getter that returns a bool by converting the underlying value to a bool if necessary.
553630
type BoolLikeGetter[K any] interface {
554631
// Get retrieves a bool value.

pkg/ottl/expression_test.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,205 @@ func Test_StandardIntLikeGetter_WrappedError(t *testing.T) {
14491449
assert.False(t, ok)
14501450
}
14511451

1452+
func Test_StandardByteSliceLikeGetter(t *testing.T) {
1453+
tests := []struct {
1454+
name string
1455+
getter ByteSliceLikeGetter[any]
1456+
want any
1457+
valid bool
1458+
expectedErrorMsg string
1459+
}{
1460+
{
1461+
name: "string type",
1462+
getter: StandardByteSliceLikeGetter[any]{
1463+
Getter: func(_ context.Context, _ any) (any, error) {
1464+
return "1", nil
1465+
},
1466+
},
1467+
want: []byte{49},
1468+
valid: true,
1469+
},
1470+
{
1471+
name: "byte type",
1472+
getter: StandardByteSliceLikeGetter[any]{
1473+
Getter: func(_ context.Context, _ any) (any, error) {
1474+
return []byte{49}, nil
1475+
},
1476+
},
1477+
want: []byte{49},
1478+
valid: true,
1479+
},
1480+
{
1481+
name: "int64 type",
1482+
getter: StandardByteSliceLikeGetter[any]{
1483+
Getter: func(_ context.Context, _ any) (any, error) {
1484+
return int64(12), nil
1485+
},
1486+
},
1487+
want: []byte{0, 0, 0, 0, 0, 0, 0, 12},
1488+
valid: true,
1489+
},
1490+
{
1491+
name: "float64 type",
1492+
getter: StandardByteSliceLikeGetter[any]{
1493+
Getter: func(_ context.Context, _ any) (any, error) {
1494+
return 1.1, nil
1495+
},
1496+
},
1497+
want: []byte{63, 241, 153, 153, 153, 153, 153, 154},
1498+
valid: true,
1499+
},
1500+
{
1501+
name: "primitive bool true",
1502+
getter: StandardByteSliceLikeGetter[any]{
1503+
Getter: func(_ context.Context, _ any) (any, error) {
1504+
return true, nil
1505+
},
1506+
},
1507+
want: []byte{1},
1508+
valid: true,
1509+
},
1510+
{
1511+
name: "primitive bool false",
1512+
getter: StandardByteSliceLikeGetter[any]{
1513+
Getter: func(_ context.Context, _ any) (any, error) {
1514+
return false, nil
1515+
},
1516+
},
1517+
want: []byte{0},
1518+
valid: true,
1519+
},
1520+
{
1521+
name: "pcommon.value type int",
1522+
getter: StandardByteSliceLikeGetter[any]{
1523+
Getter: func(_ context.Context, _ any) (any, error) {
1524+
v := pcommon.NewValueInt(int64(100))
1525+
return v, nil
1526+
},
1527+
},
1528+
want: []byte{0, 0, 0, 0, 0, 0, 0, 100},
1529+
valid: true,
1530+
},
1531+
{
1532+
name: "pcommon.value type float",
1533+
getter: StandardByteSliceLikeGetter[any]{
1534+
Getter: func(_ context.Context, _ any) (any, error) {
1535+
v := pcommon.NewValueDouble(float64(1.9))
1536+
return v, nil
1537+
},
1538+
},
1539+
want: []byte{63, 254, 102, 102, 102, 102, 102, 102},
1540+
valid: true,
1541+
},
1542+
{
1543+
name: "pcommon.value type string",
1544+
getter: StandardByteSliceLikeGetter[any]{
1545+
Getter: func(_ context.Context, _ any) (any, error) {
1546+
v := pcommon.NewValueStr("1")
1547+
return v, nil
1548+
},
1549+
},
1550+
want: []byte{49},
1551+
valid: true,
1552+
},
1553+
{
1554+
name: "pcommon.value type bytes",
1555+
getter: StandardByteSliceLikeGetter[any]{
1556+
Getter: func(_ context.Context, _ any) (any, error) {
1557+
v := pcommon.NewValueBytes()
1558+
v.SetEmptyBytes().Append(byte(12))
1559+
return v, nil
1560+
},
1561+
},
1562+
want: []byte{12},
1563+
valid: true,
1564+
},
1565+
{
1566+
name: "pcommon.value type bool true",
1567+
getter: StandardByteSliceLikeGetter[any]{
1568+
Getter: func(_ context.Context, _ any) (any, error) {
1569+
v := pcommon.NewValueBool(true)
1570+
return v, nil
1571+
},
1572+
},
1573+
want: []byte{1},
1574+
valid: true,
1575+
},
1576+
{
1577+
name: "pcommon.value type bool false",
1578+
getter: StandardByteSliceLikeGetter[any]{
1579+
Getter: func(_ context.Context, _ any) (any, error) {
1580+
v := pcommon.NewValueBool(false)
1581+
return v, nil
1582+
},
1583+
},
1584+
want: []byte{0},
1585+
valid: true,
1586+
},
1587+
{
1588+
name: "nil",
1589+
getter: StandardByteSliceLikeGetter[any]{
1590+
Getter: func(_ context.Context, _ any) (any, error) {
1591+
return nil, nil
1592+
},
1593+
},
1594+
want: nil,
1595+
valid: true,
1596+
},
1597+
{
1598+
name: "invalid type",
1599+
getter: StandardByteSliceLikeGetter[any]{
1600+
Getter: func(_ context.Context, _ any) (any, error) {
1601+
return map[string]string{}, nil
1602+
},
1603+
},
1604+
valid: false,
1605+
expectedErrorMsg: "unsupported type: map[string]string",
1606+
},
1607+
{
1608+
name: "invalid pcommon.Value type",
1609+
getter: StandardByteSliceLikeGetter[any]{
1610+
Getter: func(_ context.Context, _ any) (any, error) {
1611+
v := pcommon.NewValueMap()
1612+
return v, nil
1613+
},
1614+
},
1615+
valid: false,
1616+
expectedErrorMsg: "unsupported value type: Map",
1617+
},
1618+
}
1619+
1620+
for _, tt := range tests {
1621+
t.Run(tt.name, func(t *testing.T) {
1622+
val, err := tt.getter.Get(context.Background(), nil)
1623+
if tt.valid {
1624+
assert.NoError(t, err)
1625+
if tt.want == nil {
1626+
assert.Nil(t, val)
1627+
} else {
1628+
assert.Equal(t, tt.want, val)
1629+
}
1630+
} else {
1631+
assert.IsType(t, TypeError(""), err)
1632+
assert.EqualError(t, err, tt.expectedErrorMsg)
1633+
}
1634+
})
1635+
}
1636+
}
1637+
1638+
// nolint:errorlint
1639+
func Test_StandardByteSliceLikeGetter_WrappedError(t *testing.T) {
1640+
getter := StandardByteSliceLikeGetter[any]{
1641+
Getter: func(_ context.Context, _ any) (any, error) {
1642+
return nil, TypeError("")
1643+
},
1644+
}
1645+
_, err := getter.Get(context.Background(), nil)
1646+
assert.Error(t, err)
1647+
_, ok := err.(TypeError)
1648+
assert.False(t, ok)
1649+
}
1650+
14521651
func Test_StandardBoolGetter(t *testing.T) {
14531652
tests := []struct {
14541653
name string

pkg/ottl/functions.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,12 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) {
493493
return nil, err
494494
}
495495
return StandardBoolLikeGetter[K]{Getter: arg.Get}, nil
496+
case strings.HasPrefix(name, "ByteSliceLikeGetter"):
497+
arg, err := p.newGetter(argVal)
498+
if err != nil {
499+
return nil, err
500+
}
501+
return StandardByteSliceLikeGetter[K]{Getter: arg.Get}, nil
496502
case name == "Enum":
497503
arg, err := p.enumParser((*EnumSymbol)(argVal.Enum))
498504
if err != nil {

pkg/ottl/functions_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,20 @@ func Test_NewFunctionCall(t *testing.T) {
12071207
},
12081208
want: nil,
12091209
},
1210+
{
1211+
name: "byteslicelikegetter arg",
1212+
inv: editor{
1213+
Function: "testing_byte_slice",
1214+
Arguments: []argument{
1215+
{
1216+
Value: value{
1217+
Bytes: &byteSlice{1},
1218+
},
1219+
},
1220+
},
1221+
},
1222+
want: nil,
1223+
},
12101224
{
12111225
name: "pmapgetter arg",
12121226
inv: editor{
@@ -1864,6 +1878,16 @@ func functionWithIntLikeGetter(IntLikeGetter[any]) (ExprFunc[any], error) {
18641878
}, nil
18651879
}
18661880

1881+
type byteSliceLikeGetterArguments struct {
1882+
ByteSliceLikeGetterArg ByteSliceLikeGetter[any]
1883+
}
1884+
1885+
func functionWithByteSliceLikeGetter(ByteSliceLikeGetter[any]) (ExprFunc[any], error) {
1886+
return func(context.Context, any) (any, error) {
1887+
return "anything", nil
1888+
}, nil
1889+
}
1890+
18671891
type pMapGetterArguments struct {
18681892
PMapArg PMapGetter[any]
18691893
}
@@ -2150,6 +2174,11 @@ func defaultFunctionsForTests() map[string]Factory[any] {
21502174
&intLikeGetterArguments{},
21512175
functionWithIntLikeGetter,
21522176
),
2177+
createFactory[any](
2178+
"testing_byteslicelikegetter",
2179+
&byteSliceLikeGetterArguments{},
2180+
functionWithByteSliceLikeGetter,
2181+
),
21532182
createFactory[any](
21542183
"testing_pmapgetter",
21552184
&pMapGetterArguments{},

0 commit comments

Comments
 (0)