Skip to content

Commit

Permalink
Add a built in created time field for Tickets and the ability to filt…
Browse files Browse the repository at this point in the history
…er Tickets by created time. (#1162)
  • Loading branch information
sawagh committed Mar 20, 2020
1 parent 670f38d commit e15fd47
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 78 deletions.
20 changes: 18 additions & 2 deletions api/backend.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@
"items": {
"$ref": "#/definitions/openmatchDoubleRangeFilter"
},
"description": "Set of Filters indicating the filtering criteria. Selected players must\nmatch every Filter."
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
},
"string_equals_filters": {
"type": "array",
Expand All @@ -312,8 +312,19 @@
"items": {
"$ref": "#/definitions/openmatchTagPresentFilter"
}
},
"created_before": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created before the specified time are selected."
},
"created_after": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created after the specified time are selected."
}
}
},
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
},
"openmatchReleaseTicketsRequest": {
"type": "object",
Expand Down Expand Up @@ -401,6 +412,11 @@
"$ref": "#/definitions/protobufAny"
},
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
},
"create_time": {
"type": "string",
"format": "date-time",
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
}
},
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
Expand Down
5 changes: 5 additions & 0 deletions api/evaluator.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@
"$ref": "#/definitions/protobufAny"
},
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
},
"create_time": {
"type": "string",
"format": "date-time",
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
}
},
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
Expand Down
5 changes: 5 additions & 0 deletions api/frontend.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@
"$ref": "#/definitions/protobufAny"
},
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
},
"create_time": {
"type": "string",
"format": "date-time",
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
}
},
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
Expand Down
20 changes: 18 additions & 2 deletions api/matchfunction.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
"items": {
"$ref": "#/definitions/openmatchDoubleRangeFilter"
},
"description": "Set of Filters indicating the filtering criteria. Selected players must\nmatch every Filter."
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
},
"string_equals_filters": {
"type": "array",
Expand All @@ -178,8 +178,19 @@
"items": {
"$ref": "#/definitions/openmatchTagPresentFilter"
}
},
"created_before": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created before the specified time are selected."
},
"created_after": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created after the specified time are selected."
}
}
},
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
},
"openmatchRunRequest": {
"type": "object",
Expand Down Expand Up @@ -270,6 +281,11 @@
"$ref": "#/definitions/protobufAny"
},
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
},
"create_time": {
"type": "string",
"format": "date-time",
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
}
},
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
Expand Down
15 changes: 14 additions & 1 deletion api/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ option csharp_namespace = "OpenMatch";

import "google/rpc/status.proto";
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";

// A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an
// individual 'Player' or a 'Group' of players. Open Match will not interpret
Expand All @@ -42,6 +43,10 @@ message Ticket {
// Optional, depending on the requirements of the connected systems.
map<string, google.protobuf.Any> extensions = 5;

// Create time represents the time at which this Ticket was created. It is
// populated by Open Match at the time of Ticket creation.
google.protobuf.Timestamp create_time = 6;

// Deprecated fields.
reserved 2;
}
Expand Down Expand Up @@ -126,18 +131,26 @@ message TagPresentFilter {
string tag = 1;
}

// Pool specfies a set of criteria that are used to select a subset of Tickets
// that meet all the criteria.
message Pool {
// A developer-chosen human-readable name for this Pool.
string name = 1;

// Set of Filters indicating the filtering criteria. Selected players must
// Set of Filters indicating the filtering criteria. Selected tickets must
// match every Filter.
repeated DoubleRangeFilter double_range_filters = 2;

repeated StringEqualsFilter string_equals_filters = 4;

repeated TagPresentFilter tag_present_filters = 5;

// If specified, only Tickets created before the specified time are selected.
google.protobuf.Timestamp created_before = 6;

// If specified, only Tickets created after the specified time are selected.
google.protobuf.Timestamp created_after = 7;

// Deprecated fields.
reserved 3;
}
Expand Down
20 changes: 18 additions & 2 deletions api/query.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"items": {
"$ref": "#/definitions/openmatchDoubleRangeFilter"
},
"description": "Set of Filters indicating the filtering criteria. Selected players must\nmatch every Filter."
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
},
"string_equals_filters": {
"type": "array",
Expand All @@ -156,8 +156,19 @@
"items": {
"$ref": "#/definitions/openmatchTagPresentFilter"
}
},
"created_before": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created before the specified time are selected."
},
"created_after": {
"type": "string",
"format": "date-time",
"description": "If specified, only Tickets created after the specified time are selected."
}
}
},
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
},
"openmatchQueryTicketIdsRequest": {
"type": "object",
Expand Down Expand Up @@ -272,6 +283,11 @@
"$ref": "#/definitions/protobufAny"
},
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
},
"create_time": {
"type": "string",
"format": "date-time",
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
}
},
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
Expand Down
5 changes: 5 additions & 0 deletions internal/app/frontend/frontend_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/rs/xid"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
Expand Down Expand Up @@ -59,6 +60,9 @@ func (s *frontendService) CreateTicket(ctx context.Context, req *pb.CreateTicket
if req.Ticket.Assignment != nil {
return nil, status.Errorf(codes.InvalidArgument, "tickets cannot be created with an assignment")
}
if req.Ticket.CreateTime != nil {
return nil, status.Errorf(codes.InvalidArgument, "tickets cannot be created with create time set")
}

return doCreateTicket(ctx, req, s.store)
}
Expand All @@ -71,6 +75,7 @@ func doCreateTicket(ctx context.Context, req *pb.CreateTicketRequest, store stat
}

ticket.Id = xid.New().String()
ticket.CreateTime = ptypes.TimestampNow()
err := store.CreateTicket(ctx, ticket)
if err != nil {
logger.WithFields(logrus.Fields{
Expand Down
18 changes: 14 additions & 4 deletions internal/app/query/query_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ func (s *queryService) QueryTickets(req *pb.QueryTicketsRequest, responseServer
return status.Error(codes.InvalidArgument, ".pool is required")
}

pf, err := filter.NewPoolFilter(pool)
if err != nil {
return err
}

var results []*pb.Ticket
err := s.tc.request(responseServer.Context(), func(tickets map[string]*pb.Ticket) {
err = s.tc.request(responseServer.Context(), func(tickets map[string]*pb.Ticket) {
for _, ticket := range tickets {
if filter.InPool(ticket, pool) {
if pf.In(ticket) {
results = append(results, ticket)
}
}
Expand Down Expand Up @@ -86,10 +91,15 @@ func (s *queryService) QueryTicketIds(req *pb.QueryTicketIdsRequest, responseSer
return status.Error(codes.InvalidArgument, ".pool is required")
}

pf, err := filter.NewPoolFilter(pool)
if err != nil {
return err
}

var results []string
err := s.tc.request(responseServer.Context(), func(tickets map[string]*pb.Ticket) {
err = s.tc.request(responseServer.Context(), func(tickets map[string]*pb.Ticket) {
for id, ticket := range tickets {
if filter.InPool(ticket, pool) {
if pf.In(ticket) {
results = append(results, id)
}
}
Expand Down
82 changes: 77 additions & 5 deletions internal/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,90 @@
package filter

import (
"time"

"github.com/golang/protobuf/ptypes"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"open-match.dev/open-match/pkg/pb"
)

var emptySearchFields = &pb.SearchFields{}

// InPool returns whether the ticket meets all the criteria of the pool.
func InPool(ticket *pb.Ticket, pool *pb.Pool) bool {
var (
logger = logrus.WithFields(logrus.Fields{
"app": "openmatch",
"component": "filter",
})
)

// PoolFilter contains all the filtering criteria from a Pool that the Ticket
// needs to meet to belong to that Pool.
type PoolFilter struct {
DoubleRangeFilters []*pb.DoubleRangeFilter
StringEqualsFilters []*pb.StringEqualsFilter
TagPresentFilters []*pb.TagPresentFilter
CreatedBefore time.Time
CreatedAfter time.Time
}

// NewPoolFilter validates a Pool's filtering criteria and returns a PoolFilter.
func NewPoolFilter(pool *pb.Pool) (*PoolFilter, error) {
var ca, cb time.Time
var err error

if pool.GetCreatedBefore() != nil {
if cb, err = ptypes.Timestamp(pool.GetCreatedBefore()); err != nil {
return nil, status.Error(codes.InvalidArgument, ".invalid created_before value")
}
}

if pool.GetCreatedAfter() != nil {
if ca, err = ptypes.Timestamp(pool.GetCreatedAfter()); err != nil {
return nil, status.Error(codes.InvalidArgument, ".invalid created_after value")
}
}

return &PoolFilter{
DoubleRangeFilters: pool.GetDoubleRangeFilters(),
StringEqualsFilters: pool.GetStringEqualsFilters(),
TagPresentFilters: pool.GetTagPresentFilters(),
CreatedBefore: cb,
CreatedAfter: ca,
}, nil
}

// In returns true if the Ticket meets all the criteria for this PoolFilter.
func (pf *PoolFilter) In(ticket *pb.Ticket) bool {
s := ticket.GetSearchFields()
if s == nil {
s = emptySearchFields
}
for _, f := range pool.GetDoubleRangeFilters() {

if !pf.CreatedAfter.IsZero() || !pf.CreatedBefore.IsZero() {
// CreateTime is only populated by Open Match and hence expected to be valid.
if ct, err := ptypes.Timestamp(ticket.CreateTime); err == nil {
if !pf.CreatedAfter.IsZero() {
if !ct.After(pf.CreatedAfter) {
return false
}
}

if !pf.CreatedBefore.IsZero() {
if !ct.Before(pf.CreatedBefore) {
return false
}
}
} else {
logger.WithFields(logrus.Fields{
"error": err.Error(),
"id": ticket.GetId(),
}).Error("failed to get time from Timestamp proto")
}
}

for _, f := range pf.DoubleRangeFilters {
v, ok := s.DoubleArgs[f.DoubleArg]
if !ok {
return false
Expand All @@ -40,7 +112,7 @@ func InPool(ticket *pb.Ticket, pool *pb.Pool) bool {
}
}

for _, f := range pool.GetStringEqualsFilters() {
for _, f := range pf.StringEqualsFilters {
v, ok := s.StringArgs[f.StringArg]
if !ok {
return false
Expand All @@ -51,7 +123,7 @@ func InPool(ticket *pb.Ticket, pool *pb.Pool) bool {
}

outer:
for _, f := range pool.GetTagPresentFilters() {
for _, f := range pf.TagPresentFilters {
for _, v := range s.Tags {
if v == f.Tag {
continue outer
Expand Down
Loading

0 comments on commit e15fd47

Please sign in to comment.