@aws-amplify/api-graphql 4.0.1-console-preview.d745d43.0 → 4.0.1-ssr-api.df60e41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/GraphQLAPI.d.ts +1 -1
- package/lib/GraphQLAPI.js +23 -30
- package/lib/Providers/AWSAppSyncRealTimeProvider/index.js +467 -582
- package/lib/index.js +1 -1
- package/lib/internals/InternalGraphQLAPI.d.ts +1 -1
- package/lib/internals/InternalGraphQLAPI.js +204 -246
- package/lib/internals/v6.js +3 -3
- package/lib/tsconfig.tsbuildinfo +180 -20
- package/lib/types/index.d.ts +17 -3
- package/lib/utils/ConnectionStateMonitor.js +51 -64
- package/lib/utils/ReachabilityMonitor/index.js +2 -2
- package/lib/utils/ReachabilityMonitor/index.native.js +4 -6
- package/lib/utils/ReconnectionMonitor.js +20 -24
- package/lib/utils/errors/APIError.js +7 -11
- package/lib/utils/errors/assertValidationError.js +4 -4
- package/lib/utils/errors/validation.js +5 -6
- package/lib/utils/resolveConfig.js +9 -10
- package/lib/utils/resolveLibraryOptions.js +4 -5
- package/lib-esm/GraphQLAPI.d.ts +1 -1
- package/lib-esm/GraphQLAPI.js +22 -30
- package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js +458 -574
- package/lib-esm/Providers/constants.js +10 -10
- package/lib-esm/internals/InternalGraphQLAPI.d.ts +1 -1
- package/lib-esm/internals/InternalGraphQLAPI.js +201 -244
- package/lib-esm/internals/v6.js +1 -1
- package/lib-esm/tsconfig.tsbuildinfo +180 -20
- package/lib-esm/types/index.d.ts +17 -3
- package/lib-esm/types/index.js +1 -1
- package/lib-esm/utils/ConnectionStateMonitor.js +49 -63
- package/lib-esm/utils/ReachabilityMonitor/index.js +1 -1
- package/lib-esm/utils/ReachabilityMonitor/index.native.js +1 -3
- package/lib-esm/utils/ReconnectionMonitor.js +19 -24
- package/lib-esm/utils/errors/APIError.js +6 -11
- package/lib-esm/utils/errors/assertValidationError.js +2 -2
- package/lib-esm/utils/errors/validation.js +5 -6
- package/lib-esm/utils/resolveConfig.js +8 -9
- package/lib-esm/utils/resolveLibraryOptions.js +4 -5
- package/package.json +6 -6
- package/src/GraphQLAPI.ts +3 -2
- package/src/internals/InternalGraphQLAPI.ts +35 -10
- package/src/types/index.ts +38 -8
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { __assign, __awaiter, __generator, __read } from "tslib";
|
|
2
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
3
|
import { Observable } from 'rxjs';
|
|
@@ -10,16 +9,14 @@ import { CONTROL_MSG, ConnectionState, } from '../../types/PubSub';
|
|
|
10
9
|
import { AMPLIFY_SYMBOL, AWS_APPSYNC_REALTIME_HEADERS, CONNECTION_INIT_TIMEOUT, DEFAULT_KEEP_ALIVE_TIMEOUT, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT, MAX_DELAY_MS, MESSAGE_TYPES, NON_RETRYABLE_CODES, SOCKET_STATUS, START_ACK_TIMEOUT, SUBSCRIPTION_STATUS, CONNECTION_STATE_CHANGE, } from '../constants';
|
|
11
10
|
import { ConnectionStateMonitor, CONNECTION_CHANGE, } from '../../utils/ConnectionStateMonitor';
|
|
12
11
|
import { ReconnectEvent, ReconnectionMonitor, } from '../../utils/ReconnectionMonitor';
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const logger = new ConsoleLogger('AWSAppSyncRealTimeProvider');
|
|
13
|
+
const dispatchApiEvent = payload => {
|
|
15
14
|
Hub.dispatch('api', payload, 'PubSub', AMPLIFY_SYMBOL);
|
|
16
15
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var _this = this;
|
|
22
|
-
if (options === void 0) { options = {}; }
|
|
16
|
+
const standardDomainPattern = /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com(?:\.cn)?\/graphql$/i;
|
|
17
|
+
const customDomainPath = '/realtime';
|
|
18
|
+
export class AWSAppSyncRealTimeProvider {
|
|
19
|
+
constructor(options = {}) {
|
|
23
20
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
24
21
|
this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
|
25
22
|
this.subscriptionObserverMap = new Map();
|
|
@@ -28,19 +25,19 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
28
25
|
this.reconnectionMonitor = new ReconnectionMonitor();
|
|
29
26
|
// Monitor the connection state and pass changes along to Hub
|
|
30
27
|
this.connectionStateMonitorSubscription =
|
|
31
|
-
this.connectionStateMonitor.connectionStateObservable.subscribe(
|
|
28
|
+
this.connectionStateMonitor.connectionStateObservable.subscribe(connectionState => {
|
|
32
29
|
dispatchApiEvent({
|
|
33
30
|
event: CONNECTION_STATE_CHANGE,
|
|
34
31
|
data: {
|
|
35
|
-
provider:
|
|
36
|
-
connectionState
|
|
32
|
+
provider: this,
|
|
33
|
+
connectionState,
|
|
37
34
|
},
|
|
38
|
-
message:
|
|
35
|
+
message: `Connection state is ${connectionState}`,
|
|
39
36
|
});
|
|
40
|
-
|
|
37
|
+
this.connectionState = connectionState;
|
|
41
38
|
// Trigger START_RECONNECT when the connection is disrupted
|
|
42
39
|
if (connectionState === ConnectionState.ConnectionDisrupted) {
|
|
43
|
-
|
|
40
|
+
this.reconnectionMonitor.record(ReconnectEvent.START_RECONNECT);
|
|
44
41
|
}
|
|
45
42
|
// Trigger HALT_RECONNECT to halt reconnection attempts when the state is anything other than
|
|
46
43
|
// ConnectionDisrupted or Connecting
|
|
@@ -52,14 +49,14 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
52
49
|
ConnectionState.ConnectionDisruptedPendingNetwork,
|
|
53
50
|
ConnectionState.Disconnected,
|
|
54
51
|
].includes(connectionState)) {
|
|
55
|
-
|
|
52
|
+
this.reconnectionMonitor.record(ReconnectEvent.HALT_RECONNECT);
|
|
56
53
|
}
|
|
57
54
|
});
|
|
58
55
|
}
|
|
59
56
|
/**
|
|
60
57
|
* Mark the socket closed and release all active listeners
|
|
61
58
|
*/
|
|
62
|
-
|
|
59
|
+
close() {
|
|
63
60
|
// Mark the socket closed both in status and the connection monitor
|
|
64
61
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
65
62
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
@@ -67,202 +64,177 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
67
64
|
this.connectionStateMonitorSubscription.unsubscribe();
|
|
68
65
|
// Complete all reconnect observers
|
|
69
66
|
this.reconnectionMonitor.close();
|
|
70
|
-
}
|
|
71
|
-
|
|
67
|
+
}
|
|
68
|
+
getNewWebSocket(url, protocol) {
|
|
72
69
|
return new WebSocket(url, protocol);
|
|
73
|
-
}
|
|
74
|
-
|
|
70
|
+
}
|
|
71
|
+
getProviderName() {
|
|
75
72
|
return 'AWSAppSyncRealTimeProvider';
|
|
76
|
-
}
|
|
73
|
+
}
|
|
77
74
|
// Check if url matches standard domain pattern
|
|
78
|
-
|
|
75
|
+
isCustomDomain(url) {
|
|
79
76
|
return url.match(standardDomainPattern) === null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return new Observable(function (observer) {
|
|
77
|
+
}
|
|
78
|
+
subscribe(options, customUserAgentDetails) {
|
|
79
|
+
const { appSyncGraphqlEndpoint, region, query, variables, authenticationType, additionalHeaders, apiKey, } = options || {};
|
|
80
|
+
return new Observable(observer => {
|
|
85
81
|
if (!options || !appSyncGraphqlEndpoint) {
|
|
86
82
|
observer.error({
|
|
87
83
|
errors: [
|
|
88
|
-
|
|
84
|
+
{
|
|
85
|
+
...new GraphQLError(`Subscribe only available for AWS AppSync endpoint`),
|
|
86
|
+
},
|
|
89
87
|
],
|
|
90
88
|
});
|
|
91
89
|
observer.complete();
|
|
92
90
|
}
|
|
93
91
|
else {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
let subscriptionStartActive = false;
|
|
93
|
+
const subscriptionId = amplifyUuid();
|
|
94
|
+
const startSubscription = () => {
|
|
95
|
+
if (!subscriptionStartActive) {
|
|
96
|
+
subscriptionStartActive = true;
|
|
97
|
+
const startSubscriptionPromise = this._startSubscriptionWithAWSAppSyncRealTime({
|
|
100
98
|
options: {
|
|
101
|
-
query
|
|
102
|
-
variables
|
|
103
|
-
region
|
|
104
|
-
authenticationType
|
|
105
|
-
appSyncGraphqlEndpoint
|
|
106
|
-
additionalHeaders
|
|
107
|
-
apiKey
|
|
99
|
+
query,
|
|
100
|
+
variables,
|
|
101
|
+
region,
|
|
102
|
+
authenticationType,
|
|
103
|
+
appSyncGraphqlEndpoint,
|
|
104
|
+
additionalHeaders,
|
|
105
|
+
apiKey,
|
|
108
106
|
},
|
|
109
|
-
observer
|
|
110
|
-
subscriptionId
|
|
111
|
-
customUserAgentDetails
|
|
112
|
-
}).catch(
|
|
113
|
-
logger.debug(CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR
|
|
114
|
-
|
|
107
|
+
observer,
|
|
108
|
+
subscriptionId,
|
|
109
|
+
customUserAgentDetails,
|
|
110
|
+
}).catch(err => {
|
|
111
|
+
logger.debug(`${CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR}: ${err}`);
|
|
112
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
115
113
|
});
|
|
116
|
-
startSubscriptionPromise.finally(
|
|
117
|
-
|
|
114
|
+
startSubscriptionPromise.finally(() => {
|
|
115
|
+
subscriptionStartActive = false;
|
|
118
116
|
});
|
|
119
117
|
}
|
|
120
118
|
};
|
|
121
|
-
|
|
119
|
+
let reconnectSubscription;
|
|
122
120
|
// Add an observable to the reconnection list to manage reconnection for this subscription
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}).subscribe(
|
|
126
|
-
|
|
121
|
+
reconnectSubscription = new Observable(observer => {
|
|
122
|
+
this.reconnectionMonitor.addObserver(observer);
|
|
123
|
+
}).subscribe(() => {
|
|
124
|
+
startSubscription();
|
|
127
125
|
});
|
|
128
|
-
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return [4 /*yield*/, this._waitForSubscriptionToBeConnected(subscriptionId_1)];
|
|
141
|
-
case 2:
|
|
142
|
-
// Waiting that subscription has been connected before trying to unsubscribe
|
|
143
|
-
_a.sent();
|
|
144
|
-
subscriptionState = (this.subscriptionObserverMap.get(subscriptionId_1) || {}).subscriptionState;
|
|
145
|
-
if (!subscriptionState) {
|
|
146
|
-
// subscription already unsubscribed
|
|
147
|
-
return [2 /*return*/];
|
|
148
|
-
}
|
|
149
|
-
if (subscriptionState === SUBSCRIPTION_STATUS.CONNECTED) {
|
|
150
|
-
this._sendUnsubscriptionMessage(subscriptionId_1);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
throw new Error('Subscription never connected');
|
|
154
|
-
}
|
|
155
|
-
return [3 /*break*/, 5];
|
|
156
|
-
case 3:
|
|
157
|
-
err_1 = _a.sent();
|
|
158
|
-
logger.debug("Error while unsubscribing " + err_1);
|
|
159
|
-
return [3 /*break*/, 5];
|
|
160
|
-
case 4:
|
|
161
|
-
this._removeSubscriptionObserver(subscriptionId_1);
|
|
162
|
-
return [7 /*endfinally*/];
|
|
163
|
-
case 5: return [2 /*return*/];
|
|
126
|
+
startSubscription();
|
|
127
|
+
return async () => {
|
|
128
|
+
// Cleanup reconnection subscription
|
|
129
|
+
reconnectSubscription?.unsubscribe();
|
|
130
|
+
// Cleanup after unsubscribing or observer.complete was called after _startSubscriptionWithAWSAppSyncRealTime
|
|
131
|
+
try {
|
|
132
|
+
// Waiting that subscription has been connected before trying to unsubscribe
|
|
133
|
+
await this._waitForSubscriptionToBeConnected(subscriptionId);
|
|
134
|
+
const { subscriptionState } = this.subscriptionObserverMap.get(subscriptionId) || {};
|
|
135
|
+
if (!subscriptionState) {
|
|
136
|
+
// subscription already unsubscribed
|
|
137
|
+
return;
|
|
164
138
|
}
|
|
165
|
-
|
|
166
|
-
|
|
139
|
+
if (subscriptionState === SUBSCRIPTION_STATUS.CONNECTED) {
|
|
140
|
+
this._sendUnsubscriptionMessage(subscriptionId);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw new Error('Subscription never connected');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
logger.debug(`Error while unsubscribing ${err}`);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
this._removeSubscriptionObserver(subscriptionId);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
167
153
|
}
|
|
168
154
|
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
case 3:
|
|
223
|
-
_j.trys.push([3, 5, , 6]);
|
|
224
|
-
this.connectionStateMonitor.record(CONNECTION_CHANGE.OPENING_CONNECTION);
|
|
225
|
-
return [4 /*yield*/, this._initializeWebSocketConnection({
|
|
226
|
-
apiKey: apiKey,
|
|
227
|
-
appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
|
228
|
-
authenticationType: authenticationType,
|
|
229
|
-
region: region,
|
|
230
|
-
additionalHeaders: additionalHeaders,
|
|
231
|
-
})];
|
|
232
|
-
case 4:
|
|
233
|
-
_j.sent();
|
|
234
|
-
return [3 /*break*/, 6];
|
|
235
|
-
case 5:
|
|
236
|
-
err_2 = _j.sent();
|
|
237
|
-
this._logStartSubscriptionError(subscriptionId, observer, err_2);
|
|
238
|
-
return [2 /*return*/];
|
|
239
|
-
case 6:
|
|
240
|
-
_g = (_b = this.subscriptionObserverMap.get(subscriptionId)) !== null && _b !== void 0 ? _b : {}, subscriptionFailedCallback = _g.subscriptionFailedCallback, subscriptionReadyCallback = _g.subscriptionReadyCallback;
|
|
241
|
-
// This must be done before sending the message in order to be listening immediately
|
|
242
|
-
this.subscriptionObserverMap.set(subscriptionId, {
|
|
243
|
-
observer: observer,
|
|
244
|
-
subscriptionState: subscriptionState,
|
|
245
|
-
query: query !== null && query !== void 0 ? query : '',
|
|
246
|
-
variables: variables !== null && variables !== void 0 ? variables : {},
|
|
247
|
-
subscriptionReadyCallback: subscriptionReadyCallback,
|
|
248
|
-
subscriptionFailedCallback: subscriptionFailedCallback,
|
|
249
|
-
startAckTimeoutId: setTimeout(function () {
|
|
250
|
-
_this._timeoutStartSubscriptionAck.call(_this, subscriptionId);
|
|
251
|
-
}, START_ACK_TIMEOUT),
|
|
252
|
-
});
|
|
253
|
-
if (this.awsRealTimeSocket) {
|
|
254
|
-
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
255
|
-
}
|
|
256
|
-
return [2 /*return*/];
|
|
257
|
-
}
|
|
155
|
+
}
|
|
156
|
+
async _startSubscriptionWithAWSAppSyncRealTime({ options, observer, subscriptionId, customUserAgentDetails, }) {
|
|
157
|
+
const { appSyncGraphqlEndpoint, authenticationType, query, variables, apiKey, region, graphql_headers = () => ({}), additionalHeaders = {}, } = options;
|
|
158
|
+
const subscriptionState = SUBSCRIPTION_STATUS.PENDING;
|
|
159
|
+
const data = {
|
|
160
|
+
query,
|
|
161
|
+
variables,
|
|
162
|
+
};
|
|
163
|
+
// Having a subscription id map will make it simple to forward messages received
|
|
164
|
+
this.subscriptionObserverMap.set(subscriptionId, {
|
|
165
|
+
observer,
|
|
166
|
+
query: query ?? '',
|
|
167
|
+
variables: variables ?? {},
|
|
168
|
+
subscriptionState,
|
|
169
|
+
startAckTimeoutId: undefined,
|
|
170
|
+
});
|
|
171
|
+
// Preparing payload for subscription message
|
|
172
|
+
const dataString = JSON.stringify(data);
|
|
173
|
+
const headerObj = {
|
|
174
|
+
...(await this._awsRealTimeHeaderBasedAuth({
|
|
175
|
+
apiKey,
|
|
176
|
+
appSyncGraphqlEndpoint,
|
|
177
|
+
authenticationType,
|
|
178
|
+
payload: dataString,
|
|
179
|
+
canonicalUri: '',
|
|
180
|
+
region,
|
|
181
|
+
additionalHeaders,
|
|
182
|
+
})),
|
|
183
|
+
...(await graphql_headers()),
|
|
184
|
+
...additionalHeaders,
|
|
185
|
+
[USER_AGENT_HEADER]: getAmplifyUserAgent(customUserAgentDetails),
|
|
186
|
+
};
|
|
187
|
+
const subscriptionMessage = {
|
|
188
|
+
id: subscriptionId,
|
|
189
|
+
payload: {
|
|
190
|
+
data: dataString,
|
|
191
|
+
extensions: {
|
|
192
|
+
authorization: {
|
|
193
|
+
...headerObj,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
type: MESSAGE_TYPES.GQL_START,
|
|
198
|
+
};
|
|
199
|
+
const stringToAWSRealTime = JSON.stringify(subscriptionMessage);
|
|
200
|
+
try {
|
|
201
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.OPENING_CONNECTION);
|
|
202
|
+
await this._initializeWebSocketConnection({
|
|
203
|
+
apiKey,
|
|
204
|
+
appSyncGraphqlEndpoint,
|
|
205
|
+
authenticationType,
|
|
206
|
+
region,
|
|
207
|
+
additionalHeaders,
|
|
258
208
|
});
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
this._logStartSubscriptionError(subscriptionId, observer, err);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// Potential race condition can occur when unsubscribe is called during _initializeWebSocketConnection.
|
|
215
|
+
// E.g.unsubscribe gets invoked prior to finishing WebSocket handshake or START_ACK.
|
|
216
|
+
// Both subscriptionFailedCallback and subscriptionReadyCallback are used to synchronized this.
|
|
217
|
+
const { subscriptionFailedCallback, subscriptionReadyCallback } = this.subscriptionObserverMap.get(subscriptionId) ?? {};
|
|
218
|
+
// This must be done before sending the message in order to be listening immediately
|
|
219
|
+
this.subscriptionObserverMap.set(subscriptionId, {
|
|
220
|
+
observer,
|
|
221
|
+
subscriptionState,
|
|
222
|
+
query: query ?? '',
|
|
223
|
+
variables: variables ?? {},
|
|
224
|
+
subscriptionReadyCallback,
|
|
225
|
+
subscriptionFailedCallback,
|
|
226
|
+
startAckTimeoutId: setTimeout(() => {
|
|
227
|
+
this._timeoutStartSubscriptionAck.call(this, subscriptionId);
|
|
228
|
+
}, START_ACK_TIMEOUT),
|
|
259
229
|
});
|
|
260
|
-
|
|
230
|
+
if (this.awsRealTimeSocket) {
|
|
231
|
+
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
261
234
|
// Log logic for start subscription failures
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
var message = String((_a = err.message) !== null && _a !== void 0 ? _a : '');
|
|
235
|
+
_logStartSubscriptionError(subscriptionId, observer, err) {
|
|
236
|
+
logger.debug({ err });
|
|
237
|
+
const message = String(err.message ?? '');
|
|
266
238
|
// Resolving to give the state observer time to propogate the update
|
|
267
239
|
Promise.resolve(this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED));
|
|
268
240
|
// Capture the error only when the network didn't cause disruption
|
|
@@ -271,73 +243,68 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
271
243
|
if (isNonRetryableError(err)) {
|
|
272
244
|
observer.error({
|
|
273
245
|
errors: [
|
|
274
|
-
|
|
246
|
+
{
|
|
247
|
+
...new GraphQLError(`${CONTROL_MSG.CONNECTION_FAILED}: ${message}`),
|
|
248
|
+
},
|
|
275
249
|
],
|
|
276
250
|
});
|
|
277
251
|
}
|
|
278
252
|
else {
|
|
279
|
-
logger.debug(CONTROL_MSG.CONNECTION_FAILED
|
|
253
|
+
logger.debug(`${CONTROL_MSG.CONNECTION_FAILED}: ${message}`);
|
|
280
254
|
}
|
|
281
|
-
|
|
255
|
+
const { subscriptionFailedCallback } = this.subscriptionObserverMap.get(subscriptionId) || {};
|
|
282
256
|
// Notify concurrent unsubscription
|
|
283
257
|
if (typeof subscriptionFailedCallback === 'function') {
|
|
284
258
|
subscriptionFailedCallback();
|
|
285
259
|
}
|
|
286
260
|
}
|
|
287
|
-
}
|
|
261
|
+
}
|
|
288
262
|
// Waiting that subscription has been connected before trying to unsubscribe
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
subscriptionState = subscriptionObserver
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return [2 /*return*/];
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
};
|
|
316
|
-
AWSAppSyncRealTimeProvider.prototype._sendUnsubscriptionMessage = function (subscriptionId) {
|
|
263
|
+
async _waitForSubscriptionToBeConnected(subscriptionId) {
|
|
264
|
+
const subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
|
|
265
|
+
if (subscriptionObserver) {
|
|
266
|
+
const { subscriptionState } = subscriptionObserver;
|
|
267
|
+
// This in case unsubscribe is invoked before sending start subscription message
|
|
268
|
+
if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
|
|
269
|
+
return new Promise((res, rej) => {
|
|
270
|
+
const { observer, subscriptionState, variables, query } = subscriptionObserver;
|
|
271
|
+
this.subscriptionObserverMap.set(subscriptionId, {
|
|
272
|
+
observer,
|
|
273
|
+
subscriptionState,
|
|
274
|
+
variables,
|
|
275
|
+
query,
|
|
276
|
+
subscriptionReadyCallback: res,
|
|
277
|
+
subscriptionFailedCallback: rej,
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
_sendUnsubscriptionMessage(subscriptionId) {
|
|
317
284
|
try {
|
|
318
285
|
if (this.awsRealTimeSocket &&
|
|
319
286
|
this.awsRealTimeSocket.readyState === WebSocket.OPEN &&
|
|
320
287
|
this.socketStatus === SOCKET_STATUS.READY) {
|
|
321
288
|
// Preparing unsubscribe message to stop receiving messages for that subscription
|
|
322
|
-
|
|
289
|
+
const unsubscribeMessage = {
|
|
323
290
|
id: subscriptionId,
|
|
324
291
|
type: MESSAGE_TYPES.GQL_STOP,
|
|
325
292
|
};
|
|
326
|
-
|
|
293
|
+
const stringToAWSRealTime = JSON.stringify(unsubscribeMessage);
|
|
327
294
|
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
328
295
|
}
|
|
329
296
|
}
|
|
330
297
|
catch (err) {
|
|
331
298
|
// If GQL_STOP is not sent because of disconnection issue, then there is nothing the client can do
|
|
332
|
-
logger.debug({ err
|
|
299
|
+
logger.debug({ err });
|
|
333
300
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
301
|
+
}
|
|
302
|
+
_removeSubscriptionObserver(subscriptionId) {
|
|
336
303
|
this.subscriptionObserverMap.delete(subscriptionId);
|
|
337
304
|
// Verifying 1000ms after removing subscription in case there are new subscription unmount/mount
|
|
338
305
|
setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
|
339
|
-
}
|
|
340
|
-
|
|
306
|
+
}
|
|
307
|
+
_closeSocketIfRequired() {
|
|
341
308
|
if (this.subscriptionObserverMap.size > 0) {
|
|
342
309
|
// Active subscriptions on the WebSocket
|
|
343
310
|
return;
|
|
@@ -359,7 +326,7 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
359
326
|
if (this.keepAliveAlertTimeoutId) {
|
|
360
327
|
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
361
328
|
}
|
|
362
|
-
|
|
329
|
+
const tempSocket = this.awsRealTimeSocket;
|
|
363
330
|
// Cleaning callbacks to avoid race condition, socket still exists
|
|
364
331
|
tempSocket.onclose = null;
|
|
365
332
|
tempSocket.onerror = null;
|
|
@@ -368,27 +335,26 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
368
335
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
369
336
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
370
337
|
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
var _this = this;
|
|
338
|
+
}
|
|
339
|
+
_handleIncomingSubscriptionMessage(message) {
|
|
374
340
|
if (typeof message.data !== 'string') {
|
|
375
341
|
return;
|
|
376
342
|
}
|
|
377
|
-
logger.debug(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
logger.debug({ id
|
|
343
|
+
logger.debug(`subscription message from AWS AppSync RealTime: ${message.data}`);
|
|
344
|
+
const { id = '', payload, type, } = JSON.parse(String(message.data));
|
|
345
|
+
const { observer = null, query = '', variables = {}, startAckTimeoutId, subscriptionReadyCallback, subscriptionFailedCallback, } = this.subscriptionObserverMap.get(id) || {};
|
|
346
|
+
logger.debug({ id, observer, query, variables });
|
|
381
347
|
if (type === MESSAGE_TYPES.GQL_DATA && payload && payload.data) {
|
|
382
348
|
if (observer) {
|
|
383
349
|
observer.next(payload);
|
|
384
350
|
}
|
|
385
351
|
else {
|
|
386
|
-
logger.debug(
|
|
352
|
+
logger.debug(`observer not found for id: ${id}`);
|
|
387
353
|
}
|
|
388
354
|
return;
|
|
389
355
|
}
|
|
390
356
|
if (type === MESSAGE_TYPES.GQL_START_ACK) {
|
|
391
|
-
logger.debug(
|
|
357
|
+
logger.debug(`subscription ready for ${JSON.stringify({ query, variables })}`);
|
|
392
358
|
if (typeof subscriptionReadyCallback === 'function') {
|
|
393
359
|
subscriptionReadyCallback();
|
|
394
360
|
}
|
|
@@ -396,19 +362,19 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
396
362
|
clearTimeout(startAckTimeoutId);
|
|
397
363
|
dispatchApiEvent({
|
|
398
364
|
event: CONTROL_MSG.SUBSCRIPTION_ACK,
|
|
399
|
-
data: { query
|
|
365
|
+
data: { query, variables },
|
|
400
366
|
message: 'Connection established for subscription',
|
|
401
367
|
});
|
|
402
|
-
|
|
368
|
+
const subscriptionState = SUBSCRIPTION_STATUS.CONNECTED;
|
|
403
369
|
if (observer) {
|
|
404
370
|
this.subscriptionObserverMap.set(id, {
|
|
405
|
-
observer
|
|
406
|
-
query
|
|
407
|
-
variables
|
|
371
|
+
observer,
|
|
372
|
+
query,
|
|
373
|
+
variables,
|
|
408
374
|
startAckTimeoutId: undefined,
|
|
409
|
-
subscriptionState
|
|
410
|
-
subscriptionReadyCallback
|
|
411
|
-
subscriptionFailedCallback
|
|
375
|
+
subscriptionState,
|
|
376
|
+
subscriptionReadyCallback,
|
|
377
|
+
subscriptionFailedCallback,
|
|
412
378
|
});
|
|
413
379
|
}
|
|
414
380
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_ESTABLISHED);
|
|
@@ -419,29 +385,31 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
419
385
|
clearTimeout(this.keepAliveTimeoutId);
|
|
420
386
|
if (this.keepAliveAlertTimeoutId)
|
|
421
387
|
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
422
|
-
this.keepAliveTimeoutId = setTimeout(
|
|
423
|
-
this.keepAliveAlertTimeoutId = setTimeout(
|
|
424
|
-
|
|
388
|
+
this.keepAliveTimeoutId = setTimeout(() => this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT), this.keepAliveTimeout);
|
|
389
|
+
this.keepAliveAlertTimeoutId = setTimeout(() => {
|
|
390
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
|
|
425
391
|
}, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT);
|
|
426
392
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
|
|
427
393
|
return;
|
|
428
394
|
}
|
|
429
395
|
if (type === MESSAGE_TYPES.GQL_ERROR) {
|
|
430
|
-
|
|
396
|
+
const subscriptionState = SUBSCRIPTION_STATUS.FAILED;
|
|
431
397
|
if (observer) {
|
|
432
398
|
this.subscriptionObserverMap.set(id, {
|
|
433
|
-
observer
|
|
434
|
-
query
|
|
435
|
-
variables
|
|
436
|
-
startAckTimeoutId
|
|
437
|
-
subscriptionReadyCallback
|
|
438
|
-
subscriptionFailedCallback
|
|
439
|
-
subscriptionState
|
|
399
|
+
observer,
|
|
400
|
+
query,
|
|
401
|
+
variables,
|
|
402
|
+
startAckTimeoutId,
|
|
403
|
+
subscriptionReadyCallback,
|
|
404
|
+
subscriptionFailedCallback,
|
|
405
|
+
subscriptionState,
|
|
440
406
|
});
|
|
441
|
-
logger.debug(CONTROL_MSG.CONNECTION_FAILED
|
|
407
|
+
logger.debug(`${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`);
|
|
442
408
|
observer.error({
|
|
443
409
|
errors: [
|
|
444
|
-
|
|
410
|
+
{
|
|
411
|
+
...new GraphQLError(`${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`),
|
|
412
|
+
},
|
|
445
413
|
],
|
|
446
414
|
});
|
|
447
415
|
if (startAckTimeoutId)
|
|
@@ -451,353 +419,269 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
451
419
|
}
|
|
452
420
|
}
|
|
453
421
|
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
logger.debug(
|
|
422
|
+
}
|
|
423
|
+
_errorDisconnect(msg) {
|
|
424
|
+
logger.debug(`Disconnect error: ${msg}`);
|
|
457
425
|
if (this.awsRealTimeSocket) {
|
|
458
426
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
459
427
|
this.awsRealTimeSocket.close();
|
|
460
428
|
}
|
|
461
429
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
430
|
+
}
|
|
431
|
+
_timeoutStartSubscriptionAck(subscriptionId) {
|
|
432
|
+
const subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
|
|
465
433
|
if (subscriptionObserver) {
|
|
466
|
-
|
|
434
|
+
const { observer, query, variables } = subscriptionObserver;
|
|
467
435
|
if (!observer) {
|
|
468
436
|
return;
|
|
469
437
|
}
|
|
470
438
|
this.subscriptionObserverMap.set(subscriptionId, {
|
|
471
|
-
observer
|
|
472
|
-
query
|
|
473
|
-
variables
|
|
439
|
+
observer,
|
|
440
|
+
query,
|
|
441
|
+
variables,
|
|
474
442
|
subscriptionState: SUBSCRIPTION_STATUS.FAILED,
|
|
475
443
|
});
|
|
476
444
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
477
|
-
logger.debug('timeoutStartSubscription', JSON.stringify({ query
|
|
445
|
+
logger.debug('timeoutStartSubscription', JSON.stringify({ query, variables }));
|
|
478
446
|
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
var _this = this;
|
|
482
|
-
var appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, authenticationType = _a.authenticationType, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
|
447
|
+
}
|
|
448
|
+
_initializeWebSocketConnection({ appSyncGraphqlEndpoint, authenticationType, apiKey, region, additionalHeaders, }) {
|
|
483
449
|
if (this.socketStatus === SOCKET_STATUS.READY) {
|
|
484
450
|
return;
|
|
485
451
|
}
|
|
486
|
-
return new Promise(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
headerQs = base64Encoder.convert(headerString);
|
|
511
|
-
payloadQs = base64Encoder.convert(payloadString);
|
|
512
|
-
discoverableEndpoint = appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '';
|
|
513
|
-
if (this.isCustomDomain(discoverableEndpoint)) {
|
|
514
|
-
discoverableEndpoint =
|
|
515
|
-
discoverableEndpoint.concat(customDomainPath);
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
discoverableEndpoint = discoverableEndpoint
|
|
519
|
-
.replace('appsync-api', 'appsync-realtime-api')
|
|
520
|
-
.replace('gogi-beta', 'grt-beta');
|
|
521
|
-
}
|
|
522
|
-
protocol = 'wss://';
|
|
452
|
+
return new Promise(async (res, rej) => {
|
|
453
|
+
this.promiseArray.push({ res, rej });
|
|
454
|
+
if (this.socketStatus === SOCKET_STATUS.CLOSED) {
|
|
455
|
+
try {
|
|
456
|
+
this.socketStatus = SOCKET_STATUS.CONNECTING;
|
|
457
|
+
const payloadString = '{}';
|
|
458
|
+
const authHeader = await this._awsRealTimeHeaderBasedAuth({
|
|
459
|
+
authenticationType,
|
|
460
|
+
payload: payloadString,
|
|
461
|
+
canonicalUri: '/connect',
|
|
462
|
+
apiKey,
|
|
463
|
+
appSyncGraphqlEndpoint,
|
|
464
|
+
region,
|
|
465
|
+
additionalHeaders,
|
|
466
|
+
});
|
|
467
|
+
const headerString = authHeader ? JSON.stringify(authHeader) : '';
|
|
468
|
+
const headerQs = base64Encoder.convert(headerString);
|
|
469
|
+
const payloadQs = base64Encoder.convert(payloadString);
|
|
470
|
+
let discoverableEndpoint = appSyncGraphqlEndpoint ?? '';
|
|
471
|
+
if (this.isCustomDomain(discoverableEndpoint)) {
|
|
472
|
+
discoverableEndpoint =
|
|
473
|
+
discoverableEndpoint.concat(customDomainPath);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
523
476
|
discoverableEndpoint = discoverableEndpoint
|
|
524
|
-
.replace('
|
|
525
|
-
.replace('
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
logger.debug('Connection exited with', err_3);
|
|
541
|
-
this.promiseArray.forEach(function (_a) {
|
|
542
|
-
var rej = _a.rej;
|
|
543
|
-
return rej(err_3);
|
|
544
|
-
});
|
|
545
|
-
this.promiseArray = [];
|
|
546
|
-
if (this.awsRealTimeSocket &&
|
|
547
|
-
this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
|
|
548
|
-
this.awsRealTimeSocket.close(3001);
|
|
549
|
-
}
|
|
550
|
-
this.awsRealTimeSocket = undefined;
|
|
551
|
-
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
552
|
-
return [3 /*break*/, 5];
|
|
553
|
-
case 5: return [2 /*return*/];
|
|
477
|
+
.replace('appsync-api', 'appsync-realtime-api')
|
|
478
|
+
.replace('gogi-beta', 'grt-beta');
|
|
479
|
+
}
|
|
480
|
+
// Creating websocket url with required query strings
|
|
481
|
+
const protocol = 'wss://';
|
|
482
|
+
discoverableEndpoint = discoverableEndpoint
|
|
483
|
+
.replace('https://', protocol)
|
|
484
|
+
.replace('http://', protocol);
|
|
485
|
+
const awsRealTimeUrl = `${discoverableEndpoint}?header=${headerQs}&payload=${payloadQs}`;
|
|
486
|
+
await this._initializeRetryableHandshake(awsRealTimeUrl);
|
|
487
|
+
this.promiseArray.forEach(({ res }) => {
|
|
488
|
+
logger.debug('Notifying connection successful');
|
|
489
|
+
res();
|
|
490
|
+
});
|
|
491
|
+
this.socketStatus = SOCKET_STATUS.READY;
|
|
492
|
+
this.promiseArray = [];
|
|
554
493
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
case 1:
|
|
566
|
-
_a.sent();
|
|
567
|
-
return [2 /*return*/];
|
|
494
|
+
catch (err) {
|
|
495
|
+
logger.debug('Connection exited with', err);
|
|
496
|
+
this.promiseArray.forEach(({ rej }) => rej(err));
|
|
497
|
+
this.promiseArray = [];
|
|
498
|
+
if (this.awsRealTimeSocket &&
|
|
499
|
+
this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
|
|
500
|
+
this.awsRealTimeSocket.close(3001);
|
|
501
|
+
}
|
|
502
|
+
this.awsRealTimeSocket = undefined;
|
|
503
|
+
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
568
504
|
}
|
|
569
|
-
}
|
|
505
|
+
}
|
|
570
506
|
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
507
|
+
}
|
|
508
|
+
async _initializeRetryableHandshake(awsRealTimeUrl) {
|
|
509
|
+
logger.debug(`Initializaling retryable Handshake`);
|
|
510
|
+
await jitteredExponentialRetry(this._initializeHandshake.bind(this), [awsRealTimeUrl], MAX_DELAY_MS);
|
|
511
|
+
}
|
|
512
|
+
async _initializeHandshake(awsRealTimeUrl) {
|
|
513
|
+
logger.debug(`Initializing handshake ${awsRealTimeUrl}`);
|
|
514
|
+
// Because connecting the socket is async, is waiting until connection is open
|
|
515
|
+
// Step 1: connect websocket
|
|
516
|
+
try {
|
|
517
|
+
await (() => {
|
|
518
|
+
return new Promise((res, rej) => {
|
|
519
|
+
const newSocket = this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws');
|
|
520
|
+
newSocket.onerror = () => {
|
|
521
|
+
logger.debug(`WebSocket connection error`);
|
|
522
|
+
};
|
|
523
|
+
newSocket.onclose = () => {
|
|
524
|
+
rej(new Error('Connection handshake error'));
|
|
525
|
+
};
|
|
526
|
+
newSocket.onopen = () => {
|
|
527
|
+
this.awsRealTimeSocket = newSocket;
|
|
528
|
+
return res();
|
|
529
|
+
};
|
|
530
|
+
});
|
|
531
|
+
})();
|
|
532
|
+
// Step 2: wait for ack from AWS AppSyncReaTime after sending init
|
|
533
|
+
await (() => {
|
|
534
|
+
return new Promise((res, rej) => {
|
|
535
|
+
if (this.awsRealTimeSocket) {
|
|
536
|
+
let ackOk = false;
|
|
537
|
+
this.awsRealTimeSocket.onerror = error => {
|
|
538
|
+
logger.debug(`WebSocket error ${JSON.stringify(error)}`);
|
|
539
|
+
};
|
|
540
|
+
this.awsRealTimeSocket.onclose = event => {
|
|
541
|
+
logger.debug(`WebSocket closed ${event.reason}`);
|
|
542
|
+
rej(new Error(JSON.stringify(event)));
|
|
543
|
+
};
|
|
544
|
+
this.awsRealTimeSocket.onmessage = (message) => {
|
|
545
|
+
if (typeof message.data !== 'string') {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
logger.debug(`subscription message from AWS AppSyncRealTime: ${message.data} `);
|
|
549
|
+
const data = JSON.parse(message.data);
|
|
550
|
+
const { type, payload: { connectionTimeoutMs = DEFAULT_KEEP_ALIVE_TIMEOUT, } = {}, } = data;
|
|
551
|
+
if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
|
|
552
|
+
ackOk = true;
|
|
553
|
+
if (this.awsRealTimeSocket) {
|
|
554
|
+
this.keepAliveTimeout = connectionTimeoutMs;
|
|
555
|
+
this.awsRealTimeSocket.onmessage =
|
|
556
|
+
this._handleIncomingSubscriptionMessage.bind(this);
|
|
557
|
+
this.awsRealTimeSocket.onerror = err => {
|
|
558
|
+
logger.debug(err);
|
|
559
|
+
this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
591
560
|
};
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
561
|
+
this.awsRealTimeSocket.onclose = event => {
|
|
562
|
+
logger.debug(`WebSocket closed ${event.reason}`);
|
|
563
|
+
this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
595
564
|
};
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
var ackOk_1 = false;
|
|
605
|
-
_this.awsRealTimeSocket.onerror = function (error) {
|
|
606
|
-
logger.debug("WebSocket error " + JSON.stringify(error));
|
|
607
|
-
};
|
|
608
|
-
_this.awsRealTimeSocket.onclose = function (event) {
|
|
609
|
-
logger.debug("WebSocket closed " + event.reason);
|
|
610
|
-
rej(new Error(JSON.stringify(event)));
|
|
611
|
-
};
|
|
612
|
-
_this.awsRealTimeSocket.onmessage = function (message) {
|
|
613
|
-
if (typeof message.data !== 'string') {
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
logger.debug("subscription message from AWS AppSyncRealTime: " + message.data + " ");
|
|
617
|
-
var data = JSON.parse(message.data);
|
|
618
|
-
var type = data.type, _a = data.payload, _b = (_a === void 0 ? {} : _a).connectionTimeoutMs, connectionTimeoutMs = _b === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _b;
|
|
619
|
-
if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
|
|
620
|
-
ackOk_1 = true;
|
|
621
|
-
if (_this.awsRealTimeSocket) {
|
|
622
|
-
_this.keepAliveTimeout = connectionTimeoutMs;
|
|
623
|
-
_this.awsRealTimeSocket.onmessage =
|
|
624
|
-
_this._handleIncomingSubscriptionMessage.bind(_this);
|
|
625
|
-
_this.awsRealTimeSocket.onerror = function (err) {
|
|
626
|
-
logger.debug(err);
|
|
627
|
-
_this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
628
|
-
};
|
|
629
|
-
_this.awsRealTimeSocket.onclose = function (event) {
|
|
630
|
-
logger.debug("WebSocket closed " + event.reason);
|
|
631
|
-
_this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
res('Cool, connected to AWS AppSyncRealTime');
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
|
638
|
-
var _c = data.payload, _d = (_c === void 0 ? {} : _c).errors, _e = __read(_d === void 0 ? [] : _d, 1), _f = _e[0], _g = _f === void 0 ? {} : _f, _h = _g.errorType, errorType = _h === void 0 ? '' : _h, _j = _g.errorCode, errorCode = _j === void 0 ? 0 : _j;
|
|
639
|
-
rej({ errorType: errorType, errorCode: errorCode });
|
|
640
|
-
}
|
|
641
|
-
};
|
|
642
|
-
var gqlInit = {
|
|
643
|
-
type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
|
|
644
|
-
};
|
|
645
|
-
_this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
|
|
646
|
-
var checkAckOk_1 = function (ackOk) {
|
|
647
|
-
if (!ackOk) {
|
|
648
|
-
_this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
649
|
-
rej(new Error("Connection timeout: ack from AWSAppSyncRealTime was not received after " + CONNECTION_INIT_TIMEOUT + " ms"));
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
setTimeout(function () { return checkAckOk_1(ackOk_1); }, CONNECTION_INIT_TIMEOUT);
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
})()];
|
|
656
|
-
case 3:
|
|
657
|
-
// Step 2: wait for ack from AWS AppSyncReaTime after sending init
|
|
658
|
-
_b.sent();
|
|
659
|
-
return [3 /*break*/, 5];
|
|
660
|
-
case 4:
|
|
661
|
-
err_4 = _b.sent();
|
|
662
|
-
_a = err_4, errorType = _a.errorType, errorCode = _a.errorCode;
|
|
663
|
-
if (NON_RETRYABLE_CODES.includes(errorCode)) {
|
|
664
|
-
throw new NonRetryableError(errorType);
|
|
665
|
-
}
|
|
666
|
-
else if (errorType) {
|
|
667
|
-
throw new Error(errorType);
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
throw err_4;
|
|
671
|
-
}
|
|
672
|
-
return [3 /*break*/, 5];
|
|
673
|
-
case 5: return [2 /*return*/];
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
|
-
};
|
|
678
|
-
AWSAppSyncRealTimeProvider.prototype._awsRealTimeHeaderBasedAuth = function (_a) {
|
|
679
|
-
var apiKey = _a.apiKey, authenticationType = _a.authenticationType, payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
|
680
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
681
|
-
var headerHandler, handler, host, resolvedApiKey, result;
|
|
682
|
-
return __generator(this, function (_b) {
|
|
683
|
-
switch (_b.label) {
|
|
684
|
-
case 0:
|
|
685
|
-
headerHandler = {
|
|
686
|
-
apiKey: this._awsRealTimeApiKeyHeader.bind(this),
|
|
687
|
-
iam: this._awsRealTimeIAMHeader.bind(this),
|
|
688
|
-
oidc: this._awsAuthTokenHeader.bind(this),
|
|
689
|
-
userPool: this._awsAuthTokenHeader.bind(this),
|
|
690
|
-
lambda: this._customAuthHeader,
|
|
691
|
-
none: this._customAuthHeader,
|
|
565
|
+
}
|
|
566
|
+
res('Cool, connected to AWS AppSyncRealTime');
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
|
570
|
+
const { payload: { errors: [{ errorType = '', errorCode = 0 } = {}] = [], } = {}, } = data;
|
|
571
|
+
rej({ errorType, errorCode });
|
|
572
|
+
}
|
|
692
573
|
};
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
return [2 /*return*/, undefined];
|
|
696
|
-
case 1:
|
|
697
|
-
handler = headerHandler[authenticationType];
|
|
698
|
-
host = appSyncGraphqlEndpoint
|
|
699
|
-
? new AmplifyUrl(appSyncGraphqlEndpoint).host
|
|
700
|
-
: undefined;
|
|
701
|
-
logger.debug("Authenticating with " + JSON.stringify(authenticationType));
|
|
702
|
-
resolvedApiKey = void 0;
|
|
703
|
-
if (authenticationType === 'apiKey') {
|
|
704
|
-
resolvedApiKey = apiKey;
|
|
705
|
-
}
|
|
706
|
-
return [4 /*yield*/, handler({
|
|
707
|
-
payload: payload,
|
|
708
|
-
canonicalUri: canonicalUri,
|
|
709
|
-
appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
|
710
|
-
apiKey: resolvedApiKey,
|
|
711
|
-
region: region,
|
|
712
|
-
host: host,
|
|
713
|
-
additionalHeaders: additionalHeaders,
|
|
714
|
-
})];
|
|
715
|
-
case 2:
|
|
716
|
-
result = _b.sent();
|
|
717
|
-
return [2 /*return*/, result];
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
});
|
|
721
|
-
};
|
|
722
|
-
AWSAppSyncRealTimeProvider.prototype._awsAuthTokenHeader = function (_a) {
|
|
723
|
-
var host = _a.host;
|
|
724
|
-
var _b, _c;
|
|
725
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
726
|
-
var session;
|
|
727
|
-
return __generator(this, function (_d) {
|
|
728
|
-
switch (_d.label) {
|
|
729
|
-
case 0: return [4 /*yield*/, fetchAuthSession()];
|
|
730
|
-
case 1:
|
|
731
|
-
session = _d.sent();
|
|
732
|
-
return [2 /*return*/, {
|
|
733
|
-
Authorization: (_c = (_b = session === null || session === void 0 ? void 0 : session.tokens) === null || _b === void 0 ? void 0 : _b.accessToken) === null || _c === void 0 ? void 0 : _c.toString(),
|
|
734
|
-
host: host,
|
|
735
|
-
}];
|
|
736
|
-
}
|
|
737
|
-
});
|
|
738
|
-
});
|
|
739
|
-
};
|
|
740
|
-
AWSAppSyncRealTimeProvider.prototype._awsRealTimeApiKeyHeader = function (_a) {
|
|
741
|
-
var apiKey = _a.apiKey, host = _a.host;
|
|
742
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
743
|
-
var dt, dtStr;
|
|
744
|
-
return __generator(this, function (_b) {
|
|
745
|
-
dt = new Date();
|
|
746
|
-
dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, '');
|
|
747
|
-
return [2 /*return*/, {
|
|
748
|
-
host: host,
|
|
749
|
-
'x-amz-date': dtStr,
|
|
750
|
-
'x-api-key': apiKey,
|
|
751
|
-
}];
|
|
752
|
-
});
|
|
753
|
-
});
|
|
754
|
-
};
|
|
755
|
-
AWSAppSyncRealTimeProvider.prototype._awsRealTimeIAMHeader = function (_a) {
|
|
756
|
-
var payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, region = _a.region;
|
|
757
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
758
|
-
var endpointInfo, creds, request, signed_params;
|
|
759
|
-
return __generator(this, function (_b) {
|
|
760
|
-
switch (_b.label) {
|
|
761
|
-
case 0:
|
|
762
|
-
endpointInfo = {
|
|
763
|
-
region: region,
|
|
764
|
-
service: 'appsync',
|
|
574
|
+
const gqlInit = {
|
|
575
|
+
type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
|
|
765
576
|
};
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
method: 'POST',
|
|
773
|
-
headers: __assign({}, AWS_APPSYNC_REALTIME_HEADERS),
|
|
577
|
+
this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
|
|
578
|
+
const checkAckOk = (ackOk) => {
|
|
579
|
+
if (!ackOk) {
|
|
580
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
581
|
+
rej(new Error(`Connection timeout: ack from AWSAppSyncRealTime was not received after ${CONNECTION_INIT_TIMEOUT} ms`));
|
|
582
|
+
}
|
|
774
583
|
};
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
584
|
+
setTimeout(() => checkAckOk(ackOk), CONNECTION_INIT_TIMEOUT);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
})();
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
const { errorType, errorCode } = err;
|
|
591
|
+
if (NON_RETRYABLE_CODES.includes(errorCode)) {
|
|
592
|
+
throw new NonRetryableError(errorType);
|
|
593
|
+
}
|
|
594
|
+
else if (errorType) {
|
|
595
|
+
throw new Error(errorType);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
throw err;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
async _awsRealTimeHeaderBasedAuth({ apiKey, authenticationType, payload, canonicalUri, appSyncGraphqlEndpoint, region, additionalHeaders, }) {
|
|
603
|
+
const headerHandler = {
|
|
604
|
+
apiKey: this._awsRealTimeApiKeyHeader.bind(this),
|
|
605
|
+
iam: this._awsRealTimeIAMHeader.bind(this),
|
|
606
|
+
oidc: this._awsAuthTokenHeader.bind(this),
|
|
607
|
+
userPool: this._awsAuthTokenHeader.bind(this),
|
|
608
|
+
lambda: this._customAuthHeader,
|
|
609
|
+
none: this._customAuthHeader,
|
|
610
|
+
};
|
|
611
|
+
if (!authenticationType || !headerHandler[authenticationType]) {
|
|
612
|
+
logger.debug(`Authentication type ${authenticationType} not supported`);
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
const handler = headerHandler[authenticationType];
|
|
617
|
+
const host = appSyncGraphqlEndpoint
|
|
618
|
+
? new AmplifyUrl(appSyncGraphqlEndpoint).host
|
|
619
|
+
: undefined;
|
|
620
|
+
logger.debug(`Authenticating with ${JSON.stringify(authenticationType)}`);
|
|
621
|
+
let resolvedApiKey;
|
|
622
|
+
if (authenticationType === 'apiKey') {
|
|
623
|
+
resolvedApiKey = apiKey;
|
|
624
|
+
}
|
|
625
|
+
const result = await handler({
|
|
626
|
+
payload,
|
|
627
|
+
canonicalUri,
|
|
628
|
+
appSyncGraphqlEndpoint,
|
|
629
|
+
apiKey: resolvedApiKey,
|
|
630
|
+
region,
|
|
631
|
+
host,
|
|
632
|
+
additionalHeaders,
|
|
788
633
|
});
|
|
634
|
+
return result;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async _awsAuthTokenHeader({ host }) {
|
|
638
|
+
const session = await fetchAuthSession();
|
|
639
|
+
return {
|
|
640
|
+
Authorization: session?.tokens?.accessToken?.toString(),
|
|
641
|
+
host,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
async _awsRealTimeApiKeyHeader({ apiKey, host, }) {
|
|
645
|
+
const dt = new Date();
|
|
646
|
+
const dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, '');
|
|
647
|
+
return {
|
|
648
|
+
host,
|
|
649
|
+
'x-amz-date': dtStr,
|
|
650
|
+
'x-api-key': apiKey,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
async _awsRealTimeIAMHeader({ payload, canonicalUri, appSyncGraphqlEndpoint, region, }) {
|
|
654
|
+
const endpointInfo = {
|
|
655
|
+
region,
|
|
656
|
+
service: 'appsync',
|
|
657
|
+
};
|
|
658
|
+
const creds = (await fetchAuthSession()).credentials;
|
|
659
|
+
const request = {
|
|
660
|
+
url: `${appSyncGraphqlEndpoint}${canonicalUri}`,
|
|
661
|
+
data: payload,
|
|
662
|
+
method: 'POST',
|
|
663
|
+
headers: { ...AWS_APPSYNC_REALTIME_HEADERS },
|
|
664
|
+
};
|
|
665
|
+
const signed_params = signRequest({
|
|
666
|
+
headers: request.headers,
|
|
667
|
+
method: request.method,
|
|
668
|
+
url: new AmplifyUrl(request.url),
|
|
669
|
+
body: request.data,
|
|
670
|
+
}, {
|
|
671
|
+
// TODO: What do we need to do to remove these !'s?
|
|
672
|
+
credentials: creds,
|
|
673
|
+
signingRegion: endpointInfo.region,
|
|
674
|
+
signingService: endpointInfo.service,
|
|
789
675
|
});
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
if (!
|
|
676
|
+
return signed_params.headers;
|
|
677
|
+
}
|
|
678
|
+
_customAuthHeader({ host, additionalHeaders, }) {
|
|
679
|
+
if (!additionalHeaders?.['Authorization']) {
|
|
794
680
|
throw new Error('No auth token specified');
|
|
795
681
|
}
|
|
796
682
|
return {
|
|
797
683
|
Authorization: additionalHeaders.Authorization,
|
|
798
|
-
host
|
|
684
|
+
host,
|
|
799
685
|
};
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
}());
|
|
803
|
-
export { AWSAppSyncRealTimeProvider };
|
|
686
|
+
}
|
|
687
|
+
}
|