1
1
import Pusher from "pusher-js"
2
2
import Urql from "urql"
3
+ import { Consumer , Subscription } from "@rails/actioncable"
3
4
4
5
type ForwardCallback = ( ...args : any [ ] ) => void
5
6
6
7
const SubscriptionExchange = {
7
- create ( options : { pusher : Pusher } ) {
8
- const pusher = options . pusher
9
- return function ( operation : Urql . Operation ) {
10
- // urql will call `.subscribed` on the returned object:
11
- // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L68-L73
12
- // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L82-L97
13
- return {
14
- subscribe : ( { next, error, complete} : { next : ForwardCallback , error : ForwardCallback , complete : ForwardCallback } ) => {
15
- // Somehow forward the operation to be POSTed to the server,
16
- // I don't see an option for passing this on to the `fetchExchange`
17
- const fetchBody = JSON . stringify ( {
18
- query : operation . query ,
19
- variables : operation . variables ,
20
- } )
21
- var pusherChannelName : string
22
- const subscriptionId = "" + operation . key
23
- var fetchOptions = operation . context . fetchOptions
24
- if ( typeof fetchOptions === "function" ) {
25
- fetchOptions = fetchOptions ( )
26
- } else if ( fetchOptions == null ) {
27
- fetchOptions = { }
28
- }
8
+ create ( options : { pusher ?: Pusher , consumer ?: Consumer , channelName ?: string } ) {
9
+ if ( options . pusher ) {
10
+ return createPusherSubscription ( options . pusher )
11
+ } else if ( options . consumer ) {
12
+ return createUrqlActionCableSubscription ( options . consumer , options ?. channelName )
13
+ } else {
14
+ throw new Error ( "Either `pusher: ...` or `consumer: ...` is required." )
15
+ }
16
+ }
17
+ }
29
18
30
- const headers = {
31
- ...( fetchOptions . headers ) ,
32
- ...{
33
- 'Content-Type' : 'application/json' ,
34
- 'X-Subscription-ID' : subscriptionId
35
- }
36
- }
37
19
38
- const defaultFetchOptions = { method : "POST" }
39
- const mergedFetchOptions = {
40
- ...defaultFetchOptions ,
41
- ...fetchOptions ,
42
- body : fetchBody ,
43
- headers : headers ,
20
+ function createPusherSubscription ( pusher : Pusher ) {
21
+ return function ( operation : Urql . Operation ) {
22
+ // urql will call `.subscribed` on the returned object:
23
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L68-L73
24
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L82-L97
25
+ return {
26
+ subscribe : ( { next, error, complete} : { next : ForwardCallback , error : ForwardCallback , complete : ForwardCallback } ) => {
27
+ // Somehow forward the operation to be POSTed to the server,
28
+ // I don't see an option for passing this on to the `fetchExchange`
29
+ const fetchBody = JSON . stringify ( {
30
+ query : operation . query ,
31
+ variables : operation . variables ,
32
+ } )
33
+ var pusherChannelName : string
34
+ const subscriptionId = "" + operation . key
35
+ var fetchOptions = operation . context . fetchOptions
36
+ if ( typeof fetchOptions === "function" ) {
37
+ fetchOptions = fetchOptions ( )
38
+ } else if ( fetchOptions == null ) {
39
+ fetchOptions = { }
40
+ }
41
+
42
+ const headers = {
43
+ ...( fetchOptions . headers ) ,
44
+ ...{
45
+ 'Content-Type' : 'application/json' ,
46
+ 'X-Subscription-ID' : subscriptionId
44
47
}
45
- const fetchFn = operation . context . fetch || fetch
46
- fetchFn ( operation . context . url , mergedFetchOptions )
47
- . then ( ( fetchResult ) => {
48
- // Get the server-provided subscription ID
49
- pusherChannelName = fetchResult . headers . get ( "X-Subscription-ID" ) as string
50
- // Set up a subscription to Pusher, forwarding updates to
51
- // the `next` function provided by urql
52
- const pusherChannel = pusher . subscribe ( pusherChannelName )
53
- pusherChannel . bind ( "update" , ( payload : { result : object , more : boolean } ) => {
54
- // Here's an update to this subscription,
55
- // pass it on:
56
- if ( payload . result ) {
57
- next ( payload . result )
58
- }
59
- // If the server signals that this is the end,
60
- // then unsubscribe the client:
61
- if ( ! payload . more ) {
62
- complete ( )
63
- }
64
- } )
65
- // Continue processing the initial result for the subscription
66
- return fetchResult . json ( )
67
- } )
68
- . then ( ( jsonResult ) => {
69
- // forward the initial result to urql
70
- next ( jsonResult )
48
+ }
49
+
50
+ const defaultFetchOptions = { method : "POST" }
51
+ const mergedFetchOptions = {
52
+ ...defaultFetchOptions ,
53
+ ...fetchOptions ,
54
+ body : fetchBody ,
55
+ headers : headers ,
56
+ }
57
+ const fetchFn = operation . context . fetch || fetch
58
+ fetchFn ( operation . context . url , mergedFetchOptions )
59
+ . then ( ( fetchResult ) => {
60
+ // Get the server-provided subscription ID
61
+ pusherChannelName = fetchResult . headers . get ( "X-Subscription-ID" ) as string
62
+ // Set up a subscription to Pusher, forwarding updates to
63
+ // the `next` function provided by urql
64
+ const pusherChannel = pusher . subscribe ( pusherChannelName )
65
+ pusherChannel . bind ( "update" , ( payload : { result : object , more : boolean } ) => {
66
+ // Here's an update to this subscription,
67
+ // pass it on:
68
+ if ( payload . result ) {
69
+ next ( payload . result )
70
+ }
71
+ // If the server signals that this is the end,
72
+ // then unsubscribe the client:
73
+ if ( ! payload . more ) {
74
+ complete ( )
75
+ }
71
76
} )
72
- . catch ( error )
77
+ // Continue processing the initial result for the subscription
78
+ return fetchResult . json ( )
79
+ } )
80
+ . then ( ( jsonResult ) => {
81
+ // forward the initial result to urql
82
+ next ( jsonResult )
83
+ } )
84
+ . catch ( error )
73
85
74
- // urql will call `.unsubscribe()` if it's returned here:
75
- // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L102
76
- return {
77
- unsubscribe : ( ) => {
78
- // When requested by urql, disconnect from this channel
79
- pusherChannelName && pusher . unsubscribe ( pusherChannelName )
80
- }
86
+ // urql will call `.unsubscribe()` if it's returned here:
87
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L102
88
+ return {
89
+ unsubscribe : ( ) => {
90
+ // When requested by urql, disconnect from this channel
91
+ pusherChannelName && pusher . unsubscribe ( pusherChannelName )
81
92
}
82
93
}
83
94
}
@@ -86,4 +97,42 @@ const SubscriptionExchange = {
86
97
}
87
98
88
99
100
+
101
+ function createUrqlActionCableSubscription ( consumer : Consumer , channelName : string = "GraphqlChannel" ) {
102
+ return function ( operation : Urql . Operation ) {
103
+ let subscription : Subscription | null = null
104
+ // urql will call `.subscribed` on the returned object:
105
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L68-L73
106
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L82-L97
107
+ return {
108
+ subscribe : ( { next, error, complete} : { next : ForwardCallback , error : ForwardCallback , complete : ForwardCallback } ) => {
109
+ subscription = consumer . subscriptions . create ( channelName , {
110
+ connected ( ) {
111
+ console . log ( subscription )
112
+ subscription ?. perform ( "execute" , { query : operation . query , variables : operation . variables } )
113
+ } ,
114
+ received ( data : any ) {
115
+ if ( data ?. result ?. errors ) {
116
+ error ( data . errors )
117
+ }
118
+ if ( data ?. result ?. data ) {
119
+ next ( data . result )
120
+ }
121
+ if ( ! data . more ) {
122
+ complete ( )
123
+ }
124
+ }
125
+ } as any )
126
+ // urql will call `.unsubscribe()` if it's returned here:
127
+ // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L102
128
+ return {
129
+ unsubscribe : ( ) => {
130
+ subscription ?. unsubscribe ( )
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+
89
138
export default SubscriptionExchange
0 commit comments