2
2
// This is basically just event emitters wrapped with a function that filters messages.
3
3
//
4
4
import { EventEmitter } from 'events' ;
5
- import graphql , {
5
+ import graphql , {
6
6
GraphQLSchema ,
7
7
GraphQLError ,
8
8
validate ,
@@ -21,7 +21,13 @@ import {
21
21
subscriptionHasSingleRootField
22
22
} from './validation' ;
23
23
24
- export class FilteredPubSub {
24
+ export interface PubSubEngine {
25
+ publish ( triggerName : string , payload : any ) : boolean
26
+ subscribe ( triggerName : string , onMessage : Function ) : number
27
+ unsubscribe ( subId : number )
28
+ }
29
+
30
+ export class PubSub implements PubSubEngine {
25
31
private ee : EventEmitter ;
26
32
private subscriptions : { [ key : string ] : [ string , Function ] } ;
27
33
private subIdCounter : number ;
@@ -32,27 +38,28 @@ export class FilteredPubSub {
32
38
this . subIdCounter = 0 ;
33
39
}
34
40
35
- public publish ( triggerName : string , payload : any ) {
41
+ public publish ( triggerName : string , payload : any ) : boolean {
36
42
this . ee . emit ( triggerName , payload ) ;
43
+ // Not using the value returned from emit method because it gives
44
+ // irrelevant false when there are no listeners.
45
+ return true ;
37
46
}
38
47
39
- public subscribe ( triggerName : string , filterFunc : Function , handler : Function ) : number {
40
- // notify handler only if filterFunc returns true
41
- const onMessage = ( data ) => filterFunc ( data ) ? handler ( data ) : null
48
+ public subscribe ( triggerName : string , onMessage : Function ) : number {
42
49
this . ee . addListener ( triggerName , onMessage ) ;
43
50
this . subIdCounter = this . subIdCounter + 1 ;
44
51
this . subscriptions [ this . subIdCounter ] = [ triggerName , onMessage ] ;
45
- return this . subIdCounter ;
52
+ return this . subIdCounter ;
46
53
}
47
54
48
- public unsubscribe ( subId : number ) : void {
55
+ public unsubscribe ( subId : number ) {
49
56
const [ triggerName , onMessage ] = this . subscriptions [ subId ] ;
50
57
delete this . subscriptions [ subId ] ;
51
58
this . ee . removeListener ( triggerName , onMessage ) ;
52
59
}
53
60
}
54
61
55
- export class ValidationError extends Error {
62
+ export class ValidationError extends Error {
56
63
errors : Array < GraphQLError > ;
57
64
message : string ;
58
65
@@ -75,21 +82,23 @@ export interface SubscriptionOptions {
75
82
76
83
// This manages actual GraphQL subscriptions.
77
84
export class SubscriptionManager {
78
- private pubsub : FilteredPubSub ;
85
+ private pubsub : PubSubEngine ;
79
86
private schema : GraphQLSchema ;
80
87
private setupFunctions : { [ subscriptionName : string ] : Function } ;
81
88
private subscriptions : { [ externalId : number ] : Array < number > } ;
82
89
private maxSubscriptionId : number ;
83
90
84
- constructor ( options : { schema : GraphQLSchema , setupFunctions : { [ subscriptionName : string ] : Function } } ) {
85
- this . pubsub = new FilteredPubSub ( ) ;
91
+ constructor ( options : { schema : GraphQLSchema ,
92
+ setupFunctions : { [ subscriptionName : string ] : Function } ,
93
+ pubsub : PubSubEngine } ) {
94
+ this . pubsub = options . pubsub ;
86
95
this . schema = options . schema ;
87
96
this . setupFunctions = options . setupFunctions || { } ;
88
97
this . subscriptions = { } ;
89
98
this . maxSubscriptionId = 0 ;
90
99
}
91
100
92
- public publish ( triggerName : string , payload : any ) {
101
+ public publish ( triggerName : string , payload : any ) {
93
102
this . pubsub . publish ( triggerName , payload ) ;
94
103
}
95
104
@@ -126,7 +135,7 @@ export class SubscriptionManager {
126
135
const fields = this . schema . getSubscriptionType ( ) . getFields ( ) ;
127
136
rootField . arguments . forEach ( arg => {
128
137
// we have to get the one arg's definition from the schema
129
- const argDefinition = fields [ subscriptionName ] . args . filter (
138
+ const argDefinition = fields [ subscriptionName ] . args . filter (
130
139
argDef => argDef . name === arg . name . value
131
140
) [ 0 ] ;
132
141
args [ argDefinition . name ] = valueFromAST ( arg . value , argDefinition . type , options . variables ) ;
@@ -146,7 +155,7 @@ export class SubscriptionManager {
146
155
Object . keys ( triggerMap ) . forEach ( triggerName => {
147
156
// 2. generate the filter function and the handler function
148
157
const onMessage = rootValue => {
149
- // rootValue is the payload sent by the event emitter / trigger
158
+ // rootValue is the payload sent by the event emitter / trigger
150
159
// by convention this is the value returned from the mutation resolver
151
160
152
161
try {
@@ -166,9 +175,12 @@ export class SubscriptionManager {
166
175
}
167
176
}
168
177
178
+ const isTriggering : Function = triggerMap [ triggerName ] ;
179
+
169
180
// 3. subscribe and return the subscription id
170
181
this . subscriptions [ externalSubscriptionId ] . push (
171
- this . pubsub . subscribe ( triggerName , triggerMap [ triggerName ] , onMessage )
182
+ // Will run the onMessage function only if the message passes the filter function.
183
+ this . pubsub . subscribe ( triggerName , ( data ) => isTriggering ( data ) && onMessage ( data ) )
172
184
) ;
173
185
} ) ;
174
186
return externalSubscriptionId ;
@@ -180,4 +192,4 @@ export class SubscriptionManager {
180
192
this . pubsub . unsubscribe ( internalId ) ;
181
193
} ) ;
182
194
}
183
- }
195
+ }
0 commit comments