PluginProbe
WebTotem Security / 2.1.6
WebTotem Security v2.1.6
3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 2.2.4 All 109 releases
wt-security / htdocs / js / subscriber.js

subscriber.js in WebTotem Security 2.1.6, at htdocs/js/subscriber.js

137 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2
3 const GQL = {
4 CONNECTION_INIT: 'connection_init',
5 CONNECTION_ACK: 'connection_ack',
6 CONNECTION_ERROR: 'connection_error',
7 CONNECTION_KEEP_ALIVE: 'ka',
8 START: 'start',
9 STOP: 'stop',
10 CONNECTION_TERMINATE: 'connection_terminate',
11 DATA: 'data',
12 ERROR: 'error',
13 COMPLETE: 'complete'
14 }
15
16 class Subscriber {
17 constructor (url, options, callback, protocols = 'graphql-ws') {
18 this.callback = callback
19
20 this.nextId = 1
21 this.subscriptions = new Map()
22 this.webSocket = new WebSocket(url, protocols)
23
24 this.webSocket.onopen = event => {
25 // Initiate the connection
26 this.webSocket.send(JSON.stringify({
27 type: GQL.CONNECTION_INIT,
28 payload: options
29 }))
30 }
31
32 this.webSocket.onclose = event => {
33 // The code 1000 (Normal Closure) is special, and results in no error or payload.
34 const error = event.code === 1000 ? null : new Error(event)
35 // Notify the subscriber.
36 this.callback(error)
37 // Notify the subscriptions.
38 const callbacks = Array.from(this.subscriptions.values())
39 this.subscriptions.clear()
40 for (const callback of callbacks) {
41 callback(error, null)
42 }
43 }
44
45 this.webSocket.onmessage = this.onMessage.bind(this)
46 }
47
48 subscribe (query, variables, operationName, callback) {
49 const id = (this.nextId++).toString()
50 this.subscriptions.set(id, callback)
51
52 this.webSocket.send(JSON.stringify({
53 type: GQL.START,
54 id,
55 payload: { query, variables, operationName }
56 }))
57
58 // Return the unsubscriber.
59 return () => {
60 this.subscriptions.delete(id)
61
62 this.webSocket.send(JSON.stringify({
63 type: GQL.STOP,
64 id
65 }))
66 }
67 }
68
69 shutdown () {
70 this.webSocket.send(JSON.stringify({
71 type: GQL.CONNECTION_TERMINATE
72 }))
73 this.webSocket.close()
74 }
75
76 onMessage (event) {
77 const data = JSON.parse(event.data)
78
79 switch (data.type) {
80 case GQL.CONNECTION_ACK: {
81 // This is the successful response to GQL.CONNECTION_INIT
82 if (this.callback) {
83 this.callback(null, this.subscribe.bind(this))
84 }
85 break
86 }
87 case GQL.CONNECTION_ERROR: {
88 // This may occur:
89 // 1. In response to GQL.CONNECTION_INIT
90 // 2. In case of parsing errors in the client which will not disconnect.
91 if (this.callback) {
92 this.callback(new Error(data.payload), this)
93 }
94 break
95 }
96 case GQL.CONNECTION_KEEP_ALIVE: {
97 // This may occur:
98 // 1. After GQL.CONNECTION_ACK,
99 // 2. Periodically to keep the connection alive.
100 break
101 }
102 case GQL.DATA: {
103 // This message is sent after GQL.START to transfer the result of the GraphQL subscription.
104 const callback = this.subscriptions.get(data.id)
105 if (callback) {
106 const error = data.payload.errors ? new Error(data.payload.errors) : null
107 callback(error, data.payload.data)
108 }
109 break
110 }
111 case GQL.ERROR: {
112 // This method is sent when a subscription fails. This is usually dues to validation errors
113 // as resolver errors are returned in GQL.DATA messages.
114 const callback = this.subscriptions.get(data.id)
115 if (callback) {
116 callback(new Error(data.payload), null)
117 }
118 break
119 }
120 case GQL.COMPLETE: {
121 // This is sent when the operation is done and no more dta will be sent.
122 const callback = this.subscriptions.get(data.id)
123 if (callback) {
124 this.subscriptions.delete(data.id)
125 // Return a null error and payload to indicate the subscription is closed.
126 callback(null, null)
127 }
128 break
129 }
130 }
131 }
132 }
133
134 function graphQLSubscriber (url, options, callback, protocols = 'graphql-ws') {
135 const subscriber = new Subscriber(url, options, callback, protocols)
136 return subscriber.shutdown.bind(subscriber)
137 }