Skip to content

Commit

Permalink
feat: add new MarshalText() API (#72)
Browse files Browse the repository at this point in the history
adds `MarshalText(interface{}) (string, error)` and associated unit tests
  • Loading branch information
Dylan Bourque authored Sep 16, 2022
1 parent ff9e54b commit 8d9f2fa
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 0 deletions.
17 changes: 17 additions & 0 deletions example/proto2_gogo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,23 @@ func TestProto2GogoMarshalJSON(t *testing.T) {
})
}

func TestProto2GogoMarshalText(t *testing.T) {
msg := createTestProto2GogoMessage()
// replace the current date/time with a known value for reproducible output
now := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Timestamp = proto.Uint64(uint64(now.Unix()))
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches gogo/protobuf@v1.3.2
// - if this test breaks after updating gogo/protobuf, then update the expected string
// accordingly
expected := "eventID: \"test-event\"\nsourceID: \"test-source\"\ntimestamp: 946688523\neventType: EVENT_TYPE_ONE\ndata: \"\"\n[crowdstrike.csproto.example.proto2.gogo.TestEvent.eventExt]: <\n name: \"test\"\n info: \"\"\n labels: \"one\"\n labels: \"two\"\n labels: \"three\"\n embedded: <\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n >\n jedi: true\n nested: <\n details: \"these are some nested details\"\n >\n>\n"

s, err := csproto.MarshalText(msg)

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto2GogoMessage() *gogo.BaseEvent {
now := uint64(time.Now().UTC().Unix())
et := gogo.EventType_EVENT_TYPE_ONE
Expand Down
21 changes: 21 additions & 0 deletions example/proto2_googlev1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,27 @@ func TestProto2GoogleV1MarshalJSON(t *testing.T) {
})
}

func TestProto2GoogleV1MarshalText(t *testing.T) {
msg := createTestProto2GoogleV1Message()
// replace the current date/time with a known value for reproducible output
now := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Timestamp = proto.Uint64(uint64(now.Unix()))
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches github.com/golang/protobuf@v1.5.2
// - if this test breaks after updating golang/protobuf, then update the expected string
// accordingly
expected := "eventID: \"test-event\"\nsourceID: \"test-source\"\ntimestamp: 946688523\neventType: EVENT_TYPE_ONE\ndata: \"\"\n[crowdstrike.csproto.example.proto2.googlev1.TestEvent.eventExt]: {\n name: \"test\"\n info: \"\"\n labels: \"one\"\n labels: \"two\"\n labels: \"three\"\n embedded: {\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n }\n jedi: true\n nested: {\n details: \"these are some nested details\"\n }\n}\n"

s, err := csproto.MarshalText(msg)
// replace ": " with ": " to undo the Google library's intentional randomization of the output :(
// see: https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L226
// https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L238
s = strings.ReplaceAll(s, ": ", ": ")

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto2GoogleV1Message() *googlev1.BaseEvent {
now := uint64(time.Now().UTC().Unix())
et := googlev1.EventType_EVENT_TYPE_ONE
Expand Down
21 changes: 21 additions & 0 deletions example/proto2_googlev2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,27 @@ func TestProto2GoogleV2MarshalJSON(t *testing.T) {
})
}

func TestProto2GoogleV2MarshalText(t *testing.T) {
msg := createTestProto2GoogleV2Message()
// replace the current date/time with a known value for reproducible output
now := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Timestamp = proto.Uint64(uint64(now.Unix()))
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches google.golang.org/protobuf@v1.28.1
// - if this test breaks after updating google.golang.org/protobuf, then update the expected string
// accordingly
expected := "eventID: \"test-event\"\nsourceID: \"test-source\"\ntimestamp: 946688523\neventType: EVENT_TYPE_ONE\ndata: \"\"\n[crowdstrike.csproto.example.proto2.googlev2.TestEvent.eventExt]: {\n name: \"test\"\n info: \"\"\n labels: \"one\"\n labels: \"two\"\n labels: \"three\"\n embedded: {\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n }\n jedi: true\n nested: {\n details: \"these are some nested details\"\n }\n}\n"

s, err := csproto.MarshalText(msg)
// replace ": " with ": " to undo the Google library's intentional randomization of the output :(
// see: https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L226
// https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L238
s = strings.ReplaceAll(s, ": ", ": ")

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto2GoogleV2Message() *googlev2.BaseEvent {
now := uint64(time.Now().UTC().Unix())
et := googlev2.EventType_EVENT_TYPE_ONE
Expand Down
18 changes: 18 additions & 0 deletions example/proto3_gogo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package example_test
import (
"fmt"
"testing"
"time"

"github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/types"
Expand Down Expand Up @@ -129,6 +130,23 @@ func TestProto3GogoMarshalJSON(t *testing.T) {
})
}

func TestProto3GogoMarshalText(t *testing.T) {
msg := createTestProto3GogoMessage()
// replace the current date/time with a known value for reproducible output
now := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Ts, _ = types.TimestampProto(now)
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches gogo/protobuf@v1.3.2
// - if this test breaks after updating gogo/protobuf, then update the expected string
// accordingly
expected := "name: \"test\"\nlabels: \"one\"\nlabels: \"two\"\nlabels: \"three\"\nembedded: <\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n>\njedi: true\nnested: <\n details: \"these are some nested details\"\n>\nts: <\n seconds: 946688523\n>\n"

s, err := csproto.MarshalText(msg)

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto3GogoMessage() *gogo.TestEvent {
event := gogo.TestEvent{
Name: "test",
Expand Down
22 changes: 22 additions & 0 deletions example/proto3_googlev1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"testing"
"time"

"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -133,6 +134,27 @@ func TestProto3GoogleV1MarshalJSON(t *testing.T) {
})
}

func TestProto3GoogleV1MarshalText(t *testing.T) {
msg := createTestProto3GoogleV1Message()
// replace the current date/time with a known value for reproducible output
ts := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Ts = timestamppb.New(ts)
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches github.com/golang/protobuf@v1.5.2
// - if this test breaks after updating golang/protobuf, then update the expected string
// accordingly
expected := "name: \"test\"\nlabels: \"one\"\nlabels: \"two\"\nlabels: \"three\"\nembedded: {\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n}\njedi: true\nnested: {\n details: \"these are some nested details\"\n}\nts: {\n seconds: 946688523\n}\n"

s, err := csproto.MarshalText(msg)
// replace ": " with ": " to undo the Google library's intentional randomization of the output :(
// see: https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L226
// https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L238
s = strings.ReplaceAll(s, ": ", ": ")

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto3GoogleV1Message() *googlev1.TestEvent {
event := googlev1.TestEvent{
Name: "test",
Expand Down
22 changes: 22 additions & 0 deletions example/proto3_googlev2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -133,6 +134,27 @@ func TestProto3GoogleV2MarshalJSON(t *testing.T) {
})
}

func TestProto3GoogleV2MarshalText(t *testing.T) {
msg := createTestProto3GoogleV2Message()
// replace the current date/time with a known value for reproducible output
ts := time.Date(2000, time.January, 1, 1, 2, 3, 0, time.UTC)
msg.Ts = timestamppb.New(ts)
// NOTE: the prototext format is explicitly documented as not stable
// - this string matches google.golang.org/protobuf@v1.28.1
// - if this test breaks after updating google.golang.org/protobuf, then update the expected string
// accordingly
expected := "name: \"test\"\nlabels: \"one\"\nlabels: \"two\"\nlabels: \"three\"\nembedded: {\n ID: 42\n stuff: \"some stuff\"\n favoriteNumbers: 42\n favoriteNumbers: 1138\n}\njedi: true\nnested: {\n details: \"these are some nested details\"\n}\nts: {\n seconds: 946688523\n}\n"

s, err := csproto.MarshalText(msg)
// replace ": " with ": " to undo the Google library's intentional randomization of the output :(
// see: https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L226
// https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/internal/encoding/text/encode.go#L238
s = strings.ReplaceAll(s, ": ", ": ")

assert.NoError(t, err)
assert.Equal(t, expected, s)
}

func createTestProto3GoogleV2Message() *googlev2.TestEvent {
event := googlev2.TestEvent{
Name: "test",
Expand Down
33 changes: 33 additions & 0 deletions marshal_text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package csproto

import (
"encoding"
"fmt"

gogo "github.com/gogo/protobuf/proto"
googlev1 "github.com/golang/protobuf/proto" //nolint: staticcheck // we're using this deprecated package intentionally"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
)

// MarshalText converts the specified message to prototext string format
func MarshalText(msg interface{}) (string, error) {
if tm, ok := msg.(encoding.TextMarshaler); ok {
res, err := tm.MarshalText()
if err != nil {
return "", err
}
return string(res), nil
}

switch MsgType(msg) {
case MessageTypeGoogle:
return prototext.Format(msg.(proto.Message)), nil
case MessageTypeGoogleV1:
return googlev1.MarshalTextString(msg.(googlev1.Message)), nil
case MessageTypeGogo:
return gogo.MarshalTextString(msg.(gogo.Message)), nil
default:
return "", fmt.Errorf("unsupported message type: %T", msg)
}
}

0 comments on commit 8d9f2fa

Please sign in to comment.