Repository: googleforgames/open-match
Pull request #605 (2019-07-02 18:40:06)

Add structs package which simplifies proto struct literals


Patch:
diff --git a/examples/functions/golang/pool/mmf/matchfunction.go b/examples/functions/golang/pool/mmf/matchfunction.go
index 0960e2c8..22ec7daf 100644
--- a/examples/functions/golang/pool/mmf/matchfunction.go
+++ b/examples/functions/golang/pool/mmf/matchfunction.go
@@ -20,12 +20,11 @@
 package mmf
 
 import (
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/rs/xid"
 	"open-match.dev/open-match/examples"
 	mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
-
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 var (
@@ -52,11 +51,9 @@ func MakeMatches(params *mmfHarness.MatchFunctionParams) []*pb.Match {
 			MatchFunction: matchName,
 			Ticket:        tickets,
 			Roster:        []*pb.Roster{roster},
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: scoreCalculator(tickets)}},
-				},
-			},
+			Properties: structs.Struct{
+				examples.MatchScore: structs.Number(scoreCalculator(tickets)),
+			}.S(),
 		})
 	}
 
diff --git a/pkg/harness/function/golang/matchfunction_service.go b/pkg/harness/function/golang/matchfunction_service.go
index 30e70a0d..c72be346 100644
--- a/pkg/harness/function/golang/matchfunction_service.go
+++ b/pkg/harness/function/golang/matchfunction_service.go
@@ -20,13 +20,12 @@ import (
 	"io"
 
 	structpb "github.com/golang/protobuf/ptypes/struct"
+	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"open-match.dev/open-match/internal/config"
 	"open-match.dev/open-match/internal/rpc"
 	"open-match.dev/open-match/pkg/pb"
-
-	"github.com/sirupsen/logrus"
 )
 
 var (
diff --git a/pkg/structs/structs.go b/pkg/structs/structs.go
new file mode 100644
index 00000000..90fe9996
--- /dev/null
+++ b/pkg/structs/structs.go
@@ -0,0 +1,99 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package structs simplifies the specification of protobuf's struct literals.
+// This package has nothing to do with Go's structs, or protobufs in general.
+package structs
+
+import (
+	structpb "github.com/golang/protobuf/ptypes/struct"
+)
+
+// Bool converts a boolean value into a proto struct value.
+func Bool(v bool) *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_BoolValue{
+			BoolValue: v,
+		},
+	}
+}
+
+// List is used to specify a proto list.  It can be used either as a literal, or
+// like you would a normal slice.  Call L() or V() when finished constructing to
+// retrieve the type you want.
+type List []*structpb.Value
+
+// L converts a List into a proto struct list.
+func (l List) L() *structpb.ListValue {
+	return &structpb.ListValue{
+		Values: ([]*structpb.Value)(l),
+	}
+}
+
+// V converts a List into a proto struct value.
+func (l List) V() *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_ListValue{
+			ListValue: l.L(),
+		},
+	}
+}
+
+// Null returns a proto struct value's null.
+func Null() *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_NullValue{
+			NullValue: structpb.NullValue_NULL_VALUE,
+		},
+	}
+}
+
+// Number converts a float64 into a proto struct value.
+func Number(v float64) *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_NumberValue{
+			NumberValue: v,
+		},
+	}
+}
+
+// String converts a string into a proto struct value.
+func String(v string) *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_StringValue{
+			StringValue: v,
+		},
+	}
+}
+
+// Struct is used to specify a proto struct.  It can be used either as a literal,
+// or like you would a normal map.  Call S() or V() when finished constructing
+// to retrieve the type you want.
+type Struct map[string]*structpb.Value
+
+// S converts a Struct into a proto struct struct.
+func (s Struct) S() *structpb.Struct {
+	return &structpb.Struct{
+		Fields: (map[string]*structpb.Value)(s),
+	}
+}
+
+// V converts a Struct into a proto struct value.
+func (s Struct) V() *structpb.Value {
+	return &structpb.Value{
+		Kind: &structpb.Value_StructValue{
+			StructValue: s.S(),
+		},
+	}
+}


Test patch:
diff --git a/examples/evaluator/golang/simple/evaluate/evaluator_test.go b/examples/evaluator/golang/simple/evaluate/evaluator_test.go
index 7f18494a..300627b2 100644
--- a/examples/evaluator/golang/simple/evaluate/evaluator_test.go
+++ b/examples/evaluator/golang/simple/evaluate/evaluator_test.go
@@ -17,11 +17,11 @@ package evaluate
 import (
 	"testing"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/stretchr/testify/assert"
 	"open-match.dev/open-match/examples"
 	harness "open-match.dev/open-match/pkg/harness/evaluator/golang"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestEvaluate(t *testing.T) {
@@ -31,38 +31,30 @@ func TestEvaluate(t *testing.T) {
 
 	ticket12Score1 := &pb.Match{
 		Ticket: []*pb.Ticket{ticket1, ticket2},
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-			},
-		},
+		Properties: structs.Struct{
+			examples.MatchScore: structs.Number(1),
+		}.S(),
 	}
 
 	ticket12Score10 := &pb.Match{
 		Ticket: []*pb.Ticket{ticket2, ticket1},
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: 10}},
-			},
-		},
+		Properties: structs.Struct{
+			examples.MatchScore: structs.Number(10),
+		}.S(),
 	}
 
 	ticket123Score5 := &pb.Match{
 		Ticket: []*pb.Ticket{ticket1, ticket2, ticket3},
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: 5}},
-			},
-		},
+		Properties: structs.Struct{
+			examples.MatchScore: structs.Number(5),
+		}.S(),
 	}
 
 	ticket3Score50 := &pb.Match{
 		Ticket: []*pb.Ticket{ticket3},
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: 50}},
-			},
-		},
+		Properties: structs.Struct{
+			examples.MatchScore: structs.Number(50),
+		}.S(),
 	}
 
 	tests := []struct {
diff --git a/examples/functions/golang/pool/mmf/matchfunction_test.go b/examples/functions/golang/pool/mmf/matchfunction_test.go
index ea8bb82e..ae5ad36b 100644
--- a/examples/functions/golang/pool/mmf/matchfunction_test.go
+++ b/examples/functions/golang/pool/mmf/matchfunction_test.go
@@ -20,10 +20,10 @@ import (
 	"open-match.dev/open-match/examples"
 	"open-match.dev/open-match/pkg/pb"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestMakeMatches(t *testing.T) {
@@ -32,38 +32,30 @@ func TestMakeMatches(t *testing.T) {
 	tickets := []*pb.Ticket{
 		{
 			Id: "1",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"level":   {Kind: &structpb.Value_NumberValue{NumberValue: 10}},
-					"defense": {Kind: &structpb.Value_NumberValue{NumberValue: 100}},
-				},
-			},
+			Properties: structs.Struct{
+				"level":   structs.Number(10),
+				"defense": structs.Number(100),
+			}.S(),
 		},
 		{
 			Id: "2",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"level":  {Kind: &structpb.Value_NumberValue{NumberValue: 10}},
-					"attack": {Kind: &structpb.Value_NumberValue{NumberValue: 50}},
-				},
-			},
+			Properties: structs.Struct{
+				"level":  structs.Number(10),
+				"attack": structs.Number(50),
+			}.S(),
 		},
 		{
 			Id: "3",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"level": {Kind: &structpb.Value_NumberValue{NumberValue: 10}},
-					"speed": {Kind: &structpb.Value_NumberValue{NumberValue: 522}},
-				},
-			},
+			Properties: structs.Struct{
+				"level": structs.Number(10),
+				"speed": structs.Number(522),
+			}.S(),
 		}, {
 			Id: "4",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"level": {Kind: &structpb.Value_NumberValue{NumberValue: 10}},
-					"mana":  {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-				},
-			},
+			Properties: structs.Struct{
+				"level": structs.Number(10),
+				"mana":  structs.Number(1),
+			}.S(),
 		},
 	}
 
@@ -104,11 +96,9 @@ func TestMakeMatches(t *testing.T) {
 			MatchFunction: matchName,
 			Ticket:        tickets,
 			Roster:        []*pb.Roster{{Name: poolName, TicketId: tids}},
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					examples.MatchScore: {Kind: &structpb.Value_NumberValue{NumberValue: scoreCalculator(tickets)}},
-				},
-			},
+			Properties: structs.Struct{
+				examples.MatchScore: structs.Number(scoreCalculator(tickets)),
+			}.S(),
 		}
 	}
 
diff --git a/internal/app/backend/backend_service_test.go b/internal/app/backend/backend_service_test.go
index 2556dab4..1ba4ca6b 100644
--- a/internal/app/backend/backend_service_test.go
+++ b/internal/app/backend/backend_service_test.go
@@ -20,17 +20,15 @@ import (
 	"testing"
 	"time"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
-
-	"open-match.dev/open-match/internal/config"
-	"open-match.dev/open-match/internal/statestore"
-
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	"open-match.dev/open-match/internal/config"
+	"open-match.dev/open-match/internal/statestore"
 	statestoreTesting "open-match.dev/open-match/internal/statestore/testing"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestDoFetchMatchesInChannel(t *testing.T) {
@@ -197,19 +195,15 @@ func TestDoAssignTickets(t *testing.T) {
 	fakeTickets := []*pb.Ticket{
 		{
 			Id: "1",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					fakeProperty: {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-				},
-			},
+			Properties: structs.Struct{
+				fakeProperty: structs.Number(1),
+			}.S(),
 		},
 		{
 			Id: "2",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					fakeProperty: {Kind: &structpb.Value_NumberValue{NumberValue: 2}},
-				},
-			},
+			Properties: structs.Struct{
+				fakeProperty: structs.Number(2),
+			}.S(),
 		},
 	}
 
diff --git a/internal/app/frontend/frontend_service_test.go b/internal/app/frontend/frontend_service_test.go
index 9b2cb4af..848ca759 100644
--- a/internal/app/frontend/frontend_service_test.go
+++ b/internal/app/frontend/frontend_service_test.go
@@ -21,7 +21,6 @@ import (
 	"testing"
 	"time"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/codes"
@@ -29,6 +28,7 @@ import (
 	"open-match.dev/open-match/internal/statestore"
 	statestoreTesting "open-match.dev/open-match/internal/statestore/testing"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestDoCreateTickets(t *testing.T) {
@@ -45,11 +45,9 @@ func TestDoCreateTickets(t *testing.T) {
 			description: "expect precondition error since open-match does not support properties other than number",
 			preAction:   func(_ context.CancelFunc) {},
 			ticket: &pb.Ticket{
-				Properties: &structpb.Struct{
-					Fields: map[string]*structpb.Value{
-						"test-property": {Kind: &structpb.Value_StringValue{StringValue: "string"}},
-					},
-				},
+				Properties: structs.Struct{
+					"test-property": structs.String("string"),
+				}.S(),
 			},
 			wantCode: codes.FailedPrecondition,
 		},
@@ -57,11 +55,9 @@ func TestDoCreateTickets(t *testing.T) {
 			description: "expect error with canceled context",
 			preAction:   func(cancel context.CancelFunc) { cancel() },
 			ticket: &pb.Ticket{
-				Properties: &structpb.Struct{
-					Fields: map[string]*structpb.Value{
-						"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-					},
-				},
+				Properties: structs.Struct{
+					"test-property": structs.Number(1),
+				}.S(),
 			},
 			wantCode: codes.Unavailable,
 		},
@@ -69,11 +65,9 @@ func TestDoCreateTickets(t *testing.T) {
 			description: "expect normal return with default context",
 			preAction:   func(_ context.CancelFunc) {},
 			ticket: &pb.Ticket{
-				Properties: &structpb.Struct{
-					Fields: map[string]*structpb.Value{
-						"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-					},
-				},
+				Properties: structs.Struct{
+					"test-property": structs.Number(1),
+				}.S(),
 			},
 			wantCode: codes.OK,
 		},
@@ -174,11 +168,9 @@ func TestDoGetAssignments(t *testing.T) {
 func TestDoDeleteTicket(t *testing.T) {
 	fakeTicket := &pb.Ticket{
 		Id: "1",
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-			},
-		},
+		Properties: structs.Struct{
+			"test-property": structs.Number(1),
+		}.S(),
 	}
 
 	tests := []struct {
@@ -224,11 +216,9 @@ func TestDoDeleteTicket(t *testing.T) {
 func TestDoGetTicket(t *testing.T) {
 	fakeTicket := &pb.Ticket{
 		Id: "1",
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-			},
-		},
+		Properties: structs.Struct{
+			"test-property": structs.Number(1),
+		}.S(),
 	}
 
 	tests := []struct {
diff --git a/internal/filter/filter_test.go b/internal/filter/filter_test.go
index d3f52cd5..fd4d6e8a 100644
--- a/internal/filter/filter_test.go
+++ b/internal/filter/filter_test.go
@@ -17,9 +17,9 @@ package filter
 import (
 	"testing"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"open-match.dev/open-match/internal/filter/testcases"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 // Only test Filter and InFilters for general use cases, not specific scenearios
@@ -30,42 +30,34 @@ func TestFilter(t *testing.T) {
 	tickets := []*pb.Ticket{
 		{
 			Id: "good",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"field1": {Kind: &structpb.Value_NumberValue{NumberValue: 5}},
-					"field2": {Kind: &structpb.Value_NumberValue{NumberValue: 5}},
-				},
-			},
+			Properties: structs.Struct{
+				"field1": structs.Number(5),
+				"field2": structs.Number(5),
+			}.S(),
 		},
 
 		{
 			Id: "first_bad",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"field1": {Kind: &structpb.Value_NumberValue{NumberValue: -5}},
-					"field2": {Kind: &structpb.Value_NumberValue{NumberValue: 5}},
-				},
-			},
+			Properties: structs.Struct{
+				"field1": structs.Number(-5),
+				"field2": structs.Number(5),
+			}.S(),
 		},
 
 		{
 			Id: "second_bad",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"field1": {Kind: &structpb.Value_NumberValue{NumberValue: 5}},
-					"field2": {Kind: &structpb.Value_NumberValue{NumberValue: -5}},
-				},
-			},
+			Properties: structs.Struct{
+				"field1": structs.Number(5),
+				"field2": structs.Number(-5),
+			}.S(),
 		},
 
 		{
 			Id: "both_bad",
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"field1": {Kind: &structpb.Value_NumberValue{NumberValue: -5}},
-					"field2": {Kind: &structpb.Value_NumberValue{NumberValue: -5}},
-				},
-			},
+			Properties: structs.Struct{
+				"field1": structs.Number(-5),
+				"field2": structs.Number(-5),
+			}.S(),
 		},
 	}
 
diff --git a/internal/filter/testcases/testcases.go b/internal/filter/testcases/testcases.go
index 29cc69d7..8a791b17 100644
--- a/internal/filter/testcases/testcases.go
+++ b/internal/filter/testcases/testcases.go
@@ -20,6 +20,7 @@ import (
 
 	structpb "github.com/golang/protobuf/ptypes/struct"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 // TestCase defines a single filtering test case to run.
@@ -47,6 +48,7 @@ func ExcludedTestCases() []TestCase {
 		{
 			"missingField",
 			&pb.Ticket{
+				// Using struct literals without helper for degenerate cases only.
 				Properties: &structpb.Struct{
 					Fields: map[string]*structpb.Value{},
 				},
@@ -61,6 +63,7 @@ func ExcludedTestCases() []TestCase {
 		{
 			"missingFieldsMap",
 			&pb.Ticket{
+				// Using struct literals without helper for degenerate cases only.
 				Properties: &structpb.Struct{
 					Fields: nil,
 				},
@@ -75,6 +78,7 @@ func ExcludedTestCases() []TestCase {
 		{
 			"missingProperties",
 			&pb.Ticket{
+				// Using struct literals without helper for degenerate cases only.
 				Properties: nil,
 			},
 			&pb.Filter{
@@ -96,11 +100,9 @@ func simpleRangeTest(name string, value, min, max float64) TestCase {
 	return TestCase{
 		name,
 		&pb.Ticket{
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"field": {Kind: &structpb.Value_NumberValue{NumberValue: value}},
-				},
-			},
+			Properties: structs.Struct{
+				"field": structs.Number(value),
+			}.S(),
 		},
 		&pb.Filter{
 			Attribute: "field",
diff --git a/internal/statestore/redis_test.go b/internal/statestore/redis_test.go
index 2aeb9cdf..532f6d90 100644
--- a/internal/statestore/redis_test.go
+++ b/internal/statestore/redis_test.go
@@ -22,7 +22,6 @@ import (
 	"time"
 
 	miniredis "github.com/alicebob/miniredis/v2"
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/rs/xid"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
@@ -31,6 +30,7 @@ import (
 	"open-match.dev/open-match/internal/config"
 	internalTesting "open-match.dev/open-match/internal/testing"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestStatestoreSetup(t *testing.T) {
@@ -55,11 +55,9 @@ func TestTicketLifecycle(t *testing.T) {
 	id := xid.New().String()
 	ticket := &pb.Ticket{
 		Id: id,
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				"testindex1": {Kind: &structpb.Value_NumberValue{NumberValue: 42}},
-			},
-		},
+		Properties: structs.Struct{
+			"testindex1": structs.Number(42),
+		}.S(),
 		Assignment: &pb.Assignment{
 			Connection: "test-tbd",
 		},
@@ -154,12 +152,10 @@ func TestTicketIndexing(t *testing.T) {
 
 		ticket := &pb.Ticket{
 			Id: id,
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"testindex1": {Kind: &structpb.Value_NumberValue{NumberValue: float64(i)}},
-					"testindex2": {Kind: &structpb.Value_NumberValue{NumberValue: 0.5}},
-				},
-			},
+			Properties: structs.Struct{
+				"testindex1": structs.Number(float64(i)),
+				"testindex2": structs.Number(0.5),
+			}.S(),
 			Assignment: &pb.Assignment{
 				Connection: "test-tbd",
 			},
diff --git a/internal/testing/e2e/e2e_test.go b/internal/testing/e2e/e2e_test.go
index 9a25eb0d..27403358 100644
--- a/internal/testing/e2e/e2e_test.go
+++ b/internal/testing/e2e/e2e_test.go
@@ -15,9 +15,10 @@
 package e2e
 
 import (
-	structpb "github.com/golang/protobuf/ptypes/struct"
-	pb "open-match.dev/open-match/pkg/pb"
 	"testing"
+
+	pb "open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestServiceHealth(t *testing.T) {
@@ -51,11 +52,9 @@ func TestCreateTicket(t *testing.T) {
 	fe := om.MustFrontendGRPC()
 	resp, err := fe.CreateTicket(om.Context(), &pb.CreateTicketRequest{
 		Ticket: &pb.Ticket{
-			Properties: &structpb.Struct{
-				Fields: map[string]*structpb.Value{
-					"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-				},
-			},
+			Properties: structs.Struct{
+				"test-property": structs.Number(1),
+			}.S(),
 		},
 	})
 	if err != nil {
diff --git a/internal/testing/ticket_generator.go b/internal/testing/ticket_generator.go
index ea006cec..4612d8e8 100644
--- a/internal/testing/ticket_generator.go
+++ b/internal/testing/ticket_generator.go
@@ -18,8 +18,8 @@ package testing
 import (
 	"fmt"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 // Property defines the required fields that we need to generate tickets for testing.
@@ -38,12 +38,10 @@ func GenerateTickets(manifest1, manifest2 Property) []*pb.Ticket {
 		for j := manifest2.Min; j < manifest2.Max; j += manifest2.Interval {
 			testTickets = append(testTickets, &pb.Ticket{
 				Id: fmt.Sprintf("%s%f-%s%f", manifest1.Name, i, manifest2.Name, j),
-				Properties: &structpb.Struct{
-					Fields: map[string]*structpb.Value{
-						manifest1.Name: {Kind: &structpb.Value_NumberValue{NumberValue: i}},
-						manifest2.Name: {Kind: &structpb.Value_NumberValue{NumberValue: j}},
-					},
-				},
+				Properties: structs.Struct{
+					manifest1.Name: structs.Number(i),
+					manifest2.Name: structs.Number(j),
+				}.S(),
 			})
 		}
 	}
diff --git a/test/e2e/minimatch/minimatch_test.go b/test/e2e/minimatch/minimatch_test.go
index e6cfa722..6f3e47c3 100644
--- a/test/e2e/minimatch/minimatch_test.go
+++ b/test/e2e/minimatch/minimatch_test.go
@@ -23,13 +23,13 @@ import (
 	"testing"
 	"time"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	rpcTesting "open-match.dev/open-match/internal/rpc/testing"
 	"open-match.dev/open-match/internal/testing/e2e"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 type testProfile struct {
@@ -148,12 +148,10 @@ func TestMinimatch(t *testing.T) {
 				// to expected player pools.
 				for i, td := range testTickets {
 					resp, err := fe.CreateTicket(minimatchTc.Context(), &pb.CreateTicketRequest{Ticket: &pb.Ticket{
-						Properties: &structpb.Struct{
-							Fields: map[string]*structpb.Value{
-								e2e.SkillAttribute: {Kind: &structpb.Value_NumberValue{NumberValue: td.skill}},
-								td.mapValue:        {Kind: &structpb.Value_NumberValue{NumberValue: float64(time.Now().Unix())}},
-							},
-						},
+						Properties: structs.Struct{
+							e2e.SkillAttribute: structs.Number(td.skill),
+							td.mapValue:        structs.Number(float64(time.Now().Unix())),
+						}.S(),
 					}})
 
 					assert.NotNil(t, resp)
diff --git a/test/e2e/minimatch/ticket_test.go b/test/e2e/minimatch/ticket_test.go
index e8411a76..c1f1712a 100644
--- a/test/e2e/minimatch/ticket_test.go
+++ b/test/e2e/minimatch/ticket_test.go
@@ -22,7 +22,6 @@ import (
 	"testing"
 	"time"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"google.golang.org/grpc/codes"
@@ -30,6 +29,7 @@ import (
 	internalTesting "open-match.dev/open-match/internal/testing"
 	"open-match.dev/open-match/internal/testing/e2e"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestAssignTickets(t *testing.T) {
@@ -124,11 +124,9 @@ func TestTicketLifeCycle(t *testing.T) {
 	assert.NotNil(fe)
 
 	ticket := &pb.Ticket{
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-			},
-		},
+		Properties: structs.Struct{
+			"test-property": structs.Number(1),
+		}.S(),
 		Assignment: &pb.Assignment{
 			Connection: "test-tbd",
 		},
diff --git a/test/e2e/tickets/ticket_test.go b/test/e2e/tickets/ticket_test.go
index 4b3fdfc7..0126cc2a 100644
--- a/test/e2e/tickets/ticket_test.go
+++ b/test/e2e/tickets/ticket_test.go
@@ -19,12 +19,12 @@ import (
 	"testing"
 	"time"
 
-	structpb "github.com/golang/protobuf/ptypes/struct"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"open-match.dev/open-match/internal/testing/e2e"
 	"open-match.dev/open-match/pkg/pb"
+	"open-match.dev/open-match/pkg/structs"
 )
 
 func TestAssignTickets(t *testing.T) {
@@ -113,11 +113,9 @@ func TestTicketLifeCycle(t *testing.T) {
 	assert.NotNil(fe)
 
 	ticket := &pb.Ticket{
-		Properties: &structpb.Struct{
-			Fields: map[string]*structpb.Value{
-				"test-property": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
-			},
-		},
+		Properties: structs.Struct{
+			"test-property": structs.Number(1),
+		}.S(),
 		Assignment: &pb.Assignment{
 			Connection: "test-tbd",
 		},