From 335980f33bd2af0b68ab3a20dc0421d01d889b66 Mon Sep 17 00:00:00 2001 From: Thomas Sauvajon Date: Thu, 14 Jun 2018 17:30:27 +0200 Subject: [PATCH] Add match creation sub + sub on points scored for a match only --- resolvers/events.go | 18 ++++++--- resolvers/mutation.go | 37 ++++++++++++++++++ resolvers/root.go | 21 ++++++---- resolvers/subscribers.go | 13 ++++++- resolvers/subscriptions.go | 75 +++++++++++++++++++++--------------- schema/events.graphql | 8 +++- schema/subscriptions.graphql | 3 +- 7 files changed, 127 insertions(+), 48 deletions(-) diff --git a/resolvers/events.go b/resolvers/events.go index cb14295..a6d10c1 100644 --- a/resolvers/events.go +++ b/resolvers/events.go @@ -6,9 +6,13 @@ import ( // PointScoredEvent : dispatched when a point is scored type PointScoredEvent struct { - team bool - match *tennis.Match - tennis *tennis.Client + team bool + match *tennis.Match +} + +// MatchCreatedEvent : a match was created +type MatchCreatedEvent struct { + match *tennis.Match } // Team : resolves the team that scored the point @@ -18,6 +22,10 @@ func (r *PointScoredEvent) Team() bool { // Match : resolves the match where the point was score func (r *PointScoredEvent) Match() (*MatchResolver, error) { - match, err := r.tennis.GetMatchByID(r.match.ID) - return &MatchResolver{match: match}, err + return &MatchResolver{match: r.match}, nil +} + +// Match : resolves the match where the point was score +func (r *MatchCreatedEvent) Match() (*MatchResolver, error) { + return &MatchResolver{match: r.match}, nil } diff --git a/resolvers/mutation.go b/resolvers/mutation.go index 5f9a787..51e34d4 100644 --- a/resolvers/mutation.go +++ b/resolvers/mutation.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "time" graphql "github.com/poudre-aux-yeux/graphql-go" "github.com/poudre-aux-yeux/rapiquette/tennis" @@ -56,6 +57,11 @@ type updateStadiumArgs struct { Stadium tennis.Stadium } +type scorePointArgs struct { + MatchID graphql.ID + Team bool +} + // CreateMatch : mutation to create a match func (r *RootResolver) CreateMatch(ctx context.Context, args *createMatchArgs) (*MatchResolver, error) { if args.Match.RefLink == "" { @@ -114,6 +120,15 @@ func (r *RootResolver) CreateMatch(ctx context.Context, args *createMatchArgs) ( match, err := r.tennis.CreateMatch(args.Match) + // Dispatch the event + e := &MatchCreatedEvent{match: match} + go func() { + select { + case r.matchCreatedEvents <- e: + case <-time.After(1 * time.Second): + } + }() + return &MatchResolver{match: match, tennis: r.tennis}, err } @@ -283,6 +298,28 @@ func (r *RootResolver) UpdateStadium(ctx context.Context, args *updateStadiumArg return &StadiumResolver{stadium: stadium}, err } +// ScorePoint : score a point for a team in a match +func (r *RootResolver) ScorePoint(args struct{ Point struct{ scorePointArgs } }) (*PointScoredEvent, error) { + m, err := r.tennis.GetMatchByID(args.Point.MatchID) + + if err != nil { + return nil, fmt.Errorf("couldn't get the match %s: %v", args.Point.MatchID, err) + } + + e := &PointScoredEvent{match: m, team: args.Point.Team} + go func() { + select { + case r.pointScoredEvents <- e: + case <-time.After(1 * time.Second): + } + }() + + // TODO: save the event in the message broker + // or somewhere else + + return e, nil +} + func (r *RootResolver) existsInSet(set string, key graphql.ID) error { if ex, err := r.tennis.KeyExistsInSet(set, string(key)); err != nil { return err diff --git a/resolvers/root.go b/resolvers/root.go index d7c43ae..91a25d0 100644 --- a/resolvers/root.go +++ b/resolvers/root.go @@ -22,21 +22,26 @@ func NewRoot(tennis *tennis.Client, raquette *raquette.Client) (*RootResolver, e return nil, ErrUnableToResolve } r := &RootResolver{ - tennis: tennis, - raquette: raquette, - pointScoredEvents: make(chan *PointScoredEvent), - pointScoredSubscriber: make(chan *PointScoredSubscriber), + tennis: tennis, + raquette: raquette, + pointScoredEvents: make(chan *PointScoredEvent), + pointScoredSubscriber: make(chan *PointScoredSubscriber), + matchCreatedEvents: make(chan *MatchCreatedEvent), + matchCreatedSubscriber: make(chan *MatchCreatedSubscriber), } go r.broadcastPointScored() + go r.broadcastMatchCreated() return r, nil } // RootResolver : default resolver type RootResolver struct { - tennis *tennis.Client - raquette *raquette.Client - pointScoredEvents chan *PointScoredEvent - pointScoredSubscriber chan *PointScoredSubscriber + tennis *tennis.Client + raquette *raquette.Client + pointScoredEvents chan *PointScoredEvent + pointScoredSubscriber chan *PointScoredSubscriber + matchCreatedEvents chan *MatchCreatedEvent + matchCreatedSubscriber chan *MatchCreatedSubscriber } diff --git a/resolvers/subscribers.go b/resolvers/subscribers.go index 18a6c00..9395ebc 100644 --- a/resolvers/subscribers.go +++ b/resolvers/subscribers.go @@ -1,7 +1,16 @@ package resolvers -// PointScoredSubscriber : ?? +import graphql "github.com/poudre-aux-yeux/graphql-go" + +// PointScoredSubscriber : subscribers to pointScored type PointScoredSubscriber struct { + stop <-chan struct{} + events chan<- *PointScoredEvent + matchID graphql.ID +} + +// MatchCreatedSubscriber : subscribers to matchCreated +type MatchCreatedSubscriber struct { stop <-chan struct{} - events chan<- *PointScoredEvent + events chan<- *MatchCreatedEvent } diff --git a/resolvers/subscriptions.go b/resolvers/subscriptions.go index 9c2a831..e18e14b 100644 --- a/resolvers/subscriptions.go +++ b/resolvers/subscriptions.go @@ -2,50 +2,29 @@ package resolvers import ( "context" - "fmt" "math/rand" "time" graphql "github.com/poudre-aux-yeux/graphql-go" ) -type scorePointArgs struct { - MatchID graphql.ID - Team bool -} - -// ScorePoint : score a point for a team in a match -func (r *RootResolver) ScorePoint(args struct{ Point struct{ scorePointArgs } }) (*PointScoredEvent, error) { - m, err := r.tennis.GetMatchByID(args.Point.MatchID) - - if err != nil { - return nil, fmt.Errorf("couldn't get the match %s: %v", args.Point.MatchID, err) - } - - e := &PointScoredEvent{match: m, team: args.Point.Team, tennis: r.tennis} - go func() { - select { - case r.pointScoredEvents <- e: - case <-time.After(1 * time.Second): - } - }() - return e, nil -} - -// PointScored : A new point was scored in a match -func (r *RootResolver) PointScored(ctx context.Context) <-chan *PointScoredEvent { +// PointScored : subscribe to scored points for every match +func (r *RootResolver) PointScored(ctx context.Context, args struct{ MatchID graphql.ID }) <-chan *PointScoredEvent { c := make(chan *PointScoredEvent) - // NOTE: this could take a while - r.pointScoredSubscriber <- &PointScoredSubscriber{events: c, stop: ctx.Done()} + r.pointScoredSubscriber <- &PointScoredSubscriber{events: c, stop: ctx.Done(), matchID: args.MatchID} + return c +} +// MatchCreated : subscribe to match creations +func (r *RootResolver) MatchCreated(ctx context.Context) <-chan *MatchCreatedEvent { + c := make(chan *MatchCreatedEvent) + r.matchCreatedSubscriber <- &MatchCreatedSubscriber{events: c, stop: ctx.Done()} return c } func (r *RootResolver) broadcastPointScored() { subscribers := map[string]*PointScoredSubscriber{} unsubscribe := make(chan string) - - // NOTE: subscribing and sending events are at odds. for { select { case id := <-unsubscribe: @@ -54,6 +33,9 @@ func (r *RootResolver) broadcastPointScored() { subscribers[randomID()] = s case e := <-r.pointScoredEvents: for id, s := range subscribers { + if s.matchID != e.match.ID { + continue + } go func(id string, s *PointScoredSubscriber) { select { case <-s.stop: @@ -66,7 +48,38 @@ func (r *RootResolver) broadcastPointScored() { case <-s.stop: unsubscribe <- id case s.events <- e: - case <-time.After(time.Second): + case <-time.After(1 * time.Second): + } + }(id, s) + } + } + } +} + +func (r *RootResolver) broadcastMatchCreated() { + subscribers := map[string]*MatchCreatedSubscriber{} + unsubscribe := make(chan string) + for { + select { + case id := <-unsubscribe: + delete(subscribers, id) + case s := <-r.matchCreatedSubscriber: + subscribers[randomID()] = s + case e := <-r.matchCreatedEvents: + for id, s := range subscribers { + go func(id string, s *MatchCreatedSubscriber) { + select { + case <-s.stop: + unsubscribe <- id + return + default: + } + + select { + case <-s.stop: + unsubscribe <- id + case s.events <- e: + case <-time.After(1 * time.Second): } }(id, s) } diff --git a/schema/events.graphql b/schema/events.graphql index e67a24f..410491c 100644 --- a/schema/events.graphql +++ b/schema/events.graphql @@ -1,7 +1,13 @@ -# Dispatched when a point is scored +# A point was scored type PointScoredEvent { # The match where the point was scored match: Match! # The team that scored the point. True = home, false = away team: Boolean! } + +# A match was created +type MatchCreatedEvent { + # The created match + match: Match! +} diff --git a/schema/subscriptions.graphql b/schema/subscriptions.graphql index dd4130e..337537f 100644 --- a/schema/subscriptions.graphql +++ b/schema/subscriptions.graphql @@ -1,3 +1,4 @@ type Subscription { - pointScored(): PointScoredEvent! + pointScored(matchId: ID!): PointScoredEvent! + matchCreated(): MatchCreatedEvent! }