@aws-amplify/api-graphql 4.0.1-api-v6-models.9351bcf.0 → 4.0.1-api-v6-models.58f2536.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/internals/server/package.json +8 -0
- package/lib/GraphQLAPI.d.ts +1 -1
- package/lib/GraphQLAPI.js +23 -28
- package/lib/Providers/AWSAppSyncRealTimeProvider/index.js +467 -582
- package/lib/index.js +1 -1
- package/lib/internals/APIClient.d.ts +16 -1
- package/lib/internals/APIClient.js +232 -206
- package/lib/internals/InternalGraphQLAPI.d.ts +1 -1
- package/lib/internals/InternalGraphQLAPI.js +205 -247
- package/lib/internals/generateClient.js +14 -13
- package/lib/internals/generateModelsProperty.js +19 -312
- package/lib/internals/operations/get.d.ts +2 -0
- package/lib/internals/operations/get.js +59 -0
- package/lib/internals/operations/list.d.ts +2 -0
- package/lib/internals/operations/list.js +71 -0
- package/lib/internals/operations/observeQuery.d.ts +2 -0
- package/lib/internals/operations/observeQuery.js +119 -0
- package/lib/internals/operations/subscription.d.ts +1 -0
- package/lib/internals/operations/subscription.js +27 -0
- package/lib/internals/server/generateClient.d.ts +11 -0
- package/lib/internals/server/generateClient.js +30 -0
- package/lib/internals/server/generateModelsProperty.d.ts +3 -0
- package/lib/internals/server/generateModelsProperty.js +45 -0
- package/lib/internals/server/index.d.ts +1 -0
- package/lib/internals/server/index.js +7 -0
- package/lib/internals/v6.js +3 -3
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/index.d.ts +36 -4
- 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/findIndexByFields.js +4 -9
- package/lib/utils/resolveConfig.js +9 -10
- package/lib/utils/resolveLibraryOptions.js +4 -5
- package/lib/utils/resolveOwnerFields.js +10 -33
- package/lib/utils/resolvePKFields.js +2 -3
- package/lib-esm/GraphQLAPI.d.ts +1 -1
- package/lib-esm/GraphQLAPI.js +23 -29
- package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js +459 -575
- package/lib-esm/Providers/constants.js +10 -10
- package/lib-esm/internals/APIClient.d.ts +16 -1
- package/lib-esm/internals/APIClient.js +230 -205
- package/lib-esm/internals/InternalGraphQLAPI.d.ts +1 -1
- package/lib-esm/internals/InternalGraphQLAPI.js +201 -244
- package/lib-esm/internals/generateClient.js +11 -10
- package/lib-esm/internals/generateModelsProperty.js +19 -312
- package/lib-esm/internals/operations/get.d.ts +2 -0
- package/lib-esm/internals/operations/get.js +55 -0
- package/lib-esm/internals/operations/list.d.ts +2 -0
- package/lib-esm/internals/operations/list.js +67 -0
- package/lib-esm/internals/operations/observeQuery.d.ts +2 -0
- package/lib-esm/internals/operations/observeQuery.js +115 -0
- package/lib-esm/internals/operations/subscription.d.ts +1 -0
- package/lib-esm/internals/operations/subscription.js +23 -0
- package/lib-esm/internals/server/generateClient.d.ts +11 -0
- package/lib-esm/internals/server/generateClient.js +26 -0
- package/lib-esm/internals/server/generateModelsProperty.d.ts +3 -0
- package/lib-esm/internals/server/generateModelsProperty.js +41 -0
- package/lib-esm/internals/server/index.d.ts +1 -0
- package/lib-esm/internals/server/index.js +3 -0
- package/lib-esm/internals/v6.js +1 -1
- package/lib-esm/tsconfig.tsbuildinfo +1 -1
- package/lib-esm/types/index.d.ts +36 -4
- package/lib-esm/types/index.js +3 -3
- 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/findIndexByFields.js +4 -9
- package/lib-esm/utils/resolveConfig.js +8 -9
- package/lib-esm/utils/resolveLibraryOptions.js +4 -5
- package/lib-esm/utils/resolveOwnerFields.js +10 -33
- package/lib-esm/utils/resolvePKFields.js +2 -3
- package/package.json +8 -7
- package/src/GraphQLAPI.ts +6 -2
- package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +6 -5
- package/src/internals/APIClient.ts +122 -35
- package/src/internals/InternalGraphQLAPI.ts +35 -10
- package/src/internals/generateClient.ts +0 -1
- package/src/internals/generateModelsProperty.ts +27 -340
- package/src/internals/operations/get.ts +121 -0
- package/src/internals/operations/list.ts +111 -0
- package/src/internals/operations/observeQuery.ts +140 -0
- package/src/internals/operations/subscription.ts +59 -0
- package/src/internals/server/generateClient.ts +45 -0
- package/src/internals/server/generateModelsProperty.ts +70 -0
- package/src/internals/server/index.ts +4 -0
- package/src/types/index.ts +65 -9
|
@@ -1,27 +1,22 @@
|
|
|
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';
|
|
5
4
|
import { GraphQLError } from 'graphql';
|
|
6
|
-
import * as url from 'url';
|
|
7
|
-
import { Buffer } from 'buffer';
|
|
8
5
|
import { Hub, fetchAuthSession, ConsoleLogger } from '@aws-amplify/core';
|
|
9
6
|
import { signRequest } from '@aws-amplify/core/internals/aws-client-utils';
|
|
10
|
-
import { NonRetryableError, USER_AGENT_HEADER, getAmplifyUserAgent, isNonRetryableError, jitteredExponentialRetry, amplifyUuid, AmplifyUrl, } from '@aws-amplify/core/internals/utils';
|
|
7
|
+
import { base64Encoder, NonRetryableError, USER_AGENT_HEADER, getAmplifyUserAgent, isNonRetryableError, jitteredExponentialRetry, amplifyUuid, AmplifyUrl, } from '@aws-amplify/core/internals/utils';
|
|
11
8
|
import { CONTROL_MSG, ConnectionState, } from '../../types/PubSub';
|
|
12
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';
|
|
13
10
|
import { ConnectionStateMonitor, CONNECTION_CHANGE, } from '../../utils/ConnectionStateMonitor';
|
|
14
11
|
import { ReconnectEvent, ReconnectionMonitor, } from '../../utils/ReconnectionMonitor';
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const logger = new ConsoleLogger('AWSAppSyncRealTimeProvider');
|
|
13
|
+
const dispatchApiEvent = payload => {
|
|
17
14
|
Hub.dispatch('api', payload, 'PubSub', AMPLIFY_SYMBOL);
|
|
18
15
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (options === void 0) { options = {}; }
|
|
24
|
-
var _this = this;
|
|
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 = {}) {
|
|
25
20
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
26
21
|
this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
|
27
22
|
this.subscriptionObserverMap = new Map();
|
|
@@ -30,19 +25,19 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
30
25
|
this.reconnectionMonitor = new ReconnectionMonitor();
|
|
31
26
|
// Monitor the connection state and pass changes along to Hub
|
|
32
27
|
this.connectionStateMonitorSubscription =
|
|
33
|
-
this.connectionStateMonitor.connectionStateObservable.subscribe(
|
|
28
|
+
this.connectionStateMonitor.connectionStateObservable.subscribe(connectionState => {
|
|
34
29
|
dispatchApiEvent({
|
|
35
30
|
event: CONNECTION_STATE_CHANGE,
|
|
36
31
|
data: {
|
|
37
|
-
provider:
|
|
38
|
-
connectionState
|
|
32
|
+
provider: this,
|
|
33
|
+
connectionState,
|
|
39
34
|
},
|
|
40
|
-
message:
|
|
35
|
+
message: `Connection state is ${connectionState}`,
|
|
41
36
|
});
|
|
42
|
-
|
|
37
|
+
this.connectionState = connectionState;
|
|
43
38
|
// Trigger START_RECONNECT when the connection is disrupted
|
|
44
39
|
if (connectionState === ConnectionState.ConnectionDisrupted) {
|
|
45
|
-
|
|
40
|
+
this.reconnectionMonitor.record(ReconnectEvent.START_RECONNECT);
|
|
46
41
|
}
|
|
47
42
|
// Trigger HALT_RECONNECT to halt reconnection attempts when the state is anything other than
|
|
48
43
|
// ConnectionDisrupted or Connecting
|
|
@@ -54,14 +49,14 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
54
49
|
ConnectionState.ConnectionDisruptedPendingNetwork,
|
|
55
50
|
ConnectionState.Disconnected,
|
|
56
51
|
].includes(connectionState)) {
|
|
57
|
-
|
|
52
|
+
this.reconnectionMonitor.record(ReconnectEvent.HALT_RECONNECT);
|
|
58
53
|
}
|
|
59
54
|
});
|
|
60
55
|
}
|
|
61
56
|
/**
|
|
62
57
|
* Mark the socket closed and release all active listeners
|
|
63
58
|
*/
|
|
64
|
-
|
|
59
|
+
close() {
|
|
65
60
|
// Mark the socket closed both in status and the connection monitor
|
|
66
61
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
67
62
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
@@ -69,202 +64,177 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
69
64
|
this.connectionStateMonitorSubscription.unsubscribe();
|
|
70
65
|
// Complete all reconnect observers
|
|
71
66
|
this.reconnectionMonitor.close();
|
|
72
|
-
}
|
|
73
|
-
|
|
67
|
+
}
|
|
68
|
+
getNewWebSocket(url, protocol) {
|
|
74
69
|
return new WebSocket(url, protocol);
|
|
75
|
-
}
|
|
76
|
-
|
|
70
|
+
}
|
|
71
|
+
getProviderName() {
|
|
77
72
|
return 'AWSAppSyncRealTimeProvider';
|
|
78
|
-
}
|
|
73
|
+
}
|
|
79
74
|
// Check if url matches standard domain pattern
|
|
80
|
-
|
|
75
|
+
isCustomDomain(url) {
|
|
81
76
|
return url.match(standardDomainPattern) === null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
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 => {
|
|
87
81
|
if (!options || !appSyncGraphqlEndpoint) {
|
|
88
82
|
observer.error({
|
|
89
83
|
errors: [
|
|
90
|
-
|
|
84
|
+
{
|
|
85
|
+
...new GraphQLError(`Subscribe only available for AWS AppSync endpoint`),
|
|
86
|
+
},
|
|
91
87
|
],
|
|
92
88
|
});
|
|
93
89
|
observer.complete();
|
|
94
90
|
}
|
|
95
91
|
else {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
let subscriptionStartActive = false;
|
|
93
|
+
const subscriptionId = amplifyUuid();
|
|
94
|
+
const startSubscription = () => {
|
|
95
|
+
if (!subscriptionStartActive) {
|
|
96
|
+
subscriptionStartActive = true;
|
|
97
|
+
const startSubscriptionPromise = this._startSubscriptionWithAWSAppSyncRealTime({
|
|
102
98
|
options: {
|
|
103
|
-
query
|
|
104
|
-
variables
|
|
105
|
-
region
|
|
106
|
-
authenticationType
|
|
107
|
-
appSyncGraphqlEndpoint
|
|
108
|
-
additionalHeaders
|
|
109
|
-
apiKey
|
|
99
|
+
query,
|
|
100
|
+
variables,
|
|
101
|
+
region,
|
|
102
|
+
authenticationType,
|
|
103
|
+
appSyncGraphqlEndpoint,
|
|
104
|
+
additionalHeaders,
|
|
105
|
+
apiKey,
|
|
110
106
|
},
|
|
111
|
-
observer
|
|
112
|
-
subscriptionId
|
|
113
|
-
customUserAgentDetails
|
|
114
|
-
}).catch(
|
|
115
|
-
logger.debug(
|
|
116
|
-
|
|
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);
|
|
117
113
|
});
|
|
118
|
-
startSubscriptionPromise.finally(
|
|
119
|
-
|
|
114
|
+
startSubscriptionPromise.finally(() => {
|
|
115
|
+
subscriptionStartActive = false;
|
|
120
116
|
});
|
|
121
117
|
}
|
|
122
118
|
};
|
|
123
|
-
|
|
119
|
+
let reconnectSubscription;
|
|
124
120
|
// Add an observable to the reconnection list to manage reconnection for this subscription
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}).subscribe(
|
|
128
|
-
|
|
121
|
+
reconnectSubscription = new Observable(observer => {
|
|
122
|
+
this.reconnectionMonitor.addObserver(observer);
|
|
123
|
+
}).subscribe(() => {
|
|
124
|
+
startSubscription();
|
|
129
125
|
});
|
|
130
|
-
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return [4 /*yield*/, this._waitForSubscriptionToBeConnected(subscriptionId_1)];
|
|
143
|
-
case 2:
|
|
144
|
-
// Waiting that subscription has been connected before trying to unsubscribe
|
|
145
|
-
_a.sent();
|
|
146
|
-
subscriptionState = (this.subscriptionObserverMap.get(subscriptionId_1) || {}).subscriptionState;
|
|
147
|
-
if (!subscriptionState) {
|
|
148
|
-
// subscription already unsubscribed
|
|
149
|
-
return [2 /*return*/];
|
|
150
|
-
}
|
|
151
|
-
if (subscriptionState === SUBSCRIPTION_STATUS.CONNECTED) {
|
|
152
|
-
this._sendUnsubscriptionMessage(subscriptionId_1);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
throw new Error('Subscription never connected');
|
|
156
|
-
}
|
|
157
|
-
return [3 /*break*/, 5];
|
|
158
|
-
case 3:
|
|
159
|
-
err_1 = _a.sent();
|
|
160
|
-
logger.debug("Error while unsubscribing ".concat(err_1));
|
|
161
|
-
return [3 /*break*/, 5];
|
|
162
|
-
case 4:
|
|
163
|
-
this._removeSubscriptionObserver(subscriptionId_1);
|
|
164
|
-
return [7 /*endfinally*/];
|
|
165
|
-
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;
|
|
166
138
|
}
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
};
|
|
169
153
|
}
|
|
170
154
|
});
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
case 3:
|
|
225
|
-
_j.trys.push([3, 5, , 6]);
|
|
226
|
-
this.connectionStateMonitor.record(CONNECTION_CHANGE.OPENING_CONNECTION);
|
|
227
|
-
return [4 /*yield*/, this._initializeWebSocketConnection({
|
|
228
|
-
apiKey: apiKey,
|
|
229
|
-
appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
|
230
|
-
authenticationType: authenticationType,
|
|
231
|
-
region: region,
|
|
232
|
-
additionalHeaders: additionalHeaders,
|
|
233
|
-
})];
|
|
234
|
-
case 4:
|
|
235
|
-
_j.sent();
|
|
236
|
-
return [3 /*break*/, 6];
|
|
237
|
-
case 5:
|
|
238
|
-
err_2 = _j.sent();
|
|
239
|
-
this._logStartSubscriptionError(subscriptionId, observer, err_2);
|
|
240
|
-
return [2 /*return*/];
|
|
241
|
-
case 6:
|
|
242
|
-
_g = (_b = this.subscriptionObserverMap.get(subscriptionId)) !== null && _b !== void 0 ? _b : {}, subscriptionFailedCallback = _g.subscriptionFailedCallback, subscriptionReadyCallback = _g.subscriptionReadyCallback;
|
|
243
|
-
// This must be done before sending the message in order to be listening immediately
|
|
244
|
-
this.subscriptionObserverMap.set(subscriptionId, {
|
|
245
|
-
observer: observer,
|
|
246
|
-
subscriptionState: subscriptionState,
|
|
247
|
-
query: query !== null && query !== void 0 ? query : '',
|
|
248
|
-
variables: variables !== null && variables !== void 0 ? variables : {},
|
|
249
|
-
subscriptionReadyCallback: subscriptionReadyCallback,
|
|
250
|
-
subscriptionFailedCallback: subscriptionFailedCallback,
|
|
251
|
-
startAckTimeoutId: setTimeout(function () {
|
|
252
|
-
_this._timeoutStartSubscriptionAck.call(_this, subscriptionId);
|
|
253
|
-
}, START_ACK_TIMEOUT),
|
|
254
|
-
});
|
|
255
|
-
if (this.awsRealTimeSocket) {
|
|
256
|
-
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
257
|
-
}
|
|
258
|
-
return [2 /*return*/];
|
|
259
|
-
}
|
|
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,
|
|
260
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),
|
|
261
229
|
});
|
|
262
|
-
|
|
230
|
+
if (this.awsRealTimeSocket) {
|
|
231
|
+
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
263
234
|
// Log logic for start subscription failures
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
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 ?? '');
|
|
268
238
|
// Resolving to give the state observer time to propogate the update
|
|
269
239
|
Promise.resolve(this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED));
|
|
270
240
|
// Capture the error only when the network didn't cause disruption
|
|
@@ -273,73 +243,68 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
273
243
|
if (isNonRetryableError(err)) {
|
|
274
244
|
observer.error({
|
|
275
245
|
errors: [
|
|
276
|
-
|
|
246
|
+
{
|
|
247
|
+
...new GraphQLError(`${CONTROL_MSG.CONNECTION_FAILED}: ${message}`),
|
|
248
|
+
},
|
|
277
249
|
],
|
|
278
250
|
});
|
|
279
251
|
}
|
|
280
252
|
else {
|
|
281
|
-
logger.debug(
|
|
253
|
+
logger.debug(`${CONTROL_MSG.CONNECTION_FAILED}: ${message}`);
|
|
282
254
|
}
|
|
283
|
-
|
|
255
|
+
const { subscriptionFailedCallback } = this.subscriptionObserverMap.get(subscriptionId) || {};
|
|
284
256
|
// Notify concurrent unsubscription
|
|
285
257
|
if (typeof subscriptionFailedCallback === 'function') {
|
|
286
258
|
subscriptionFailedCallback();
|
|
287
259
|
}
|
|
288
260
|
}
|
|
289
|
-
}
|
|
261
|
+
}
|
|
290
262
|
// Waiting that subscription has been connected before trying to unsubscribe
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
subscriptionState = subscriptionObserver
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return [2 /*return*/];
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
};
|
|
318
|
-
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) {
|
|
319
284
|
try {
|
|
320
285
|
if (this.awsRealTimeSocket &&
|
|
321
286
|
this.awsRealTimeSocket.readyState === WebSocket.OPEN &&
|
|
322
287
|
this.socketStatus === SOCKET_STATUS.READY) {
|
|
323
288
|
// Preparing unsubscribe message to stop receiving messages for that subscription
|
|
324
|
-
|
|
289
|
+
const unsubscribeMessage = {
|
|
325
290
|
id: subscriptionId,
|
|
326
291
|
type: MESSAGE_TYPES.GQL_STOP,
|
|
327
292
|
};
|
|
328
|
-
|
|
293
|
+
const stringToAWSRealTime = JSON.stringify(unsubscribeMessage);
|
|
329
294
|
this.awsRealTimeSocket.send(stringToAWSRealTime);
|
|
330
295
|
}
|
|
331
296
|
}
|
|
332
297
|
catch (err) {
|
|
333
298
|
// If GQL_STOP is not sent because of disconnection issue, then there is nothing the client can do
|
|
334
|
-
logger.debug({ err
|
|
299
|
+
logger.debug({ err });
|
|
335
300
|
}
|
|
336
|
-
}
|
|
337
|
-
|
|
301
|
+
}
|
|
302
|
+
_removeSubscriptionObserver(subscriptionId) {
|
|
338
303
|
this.subscriptionObserverMap.delete(subscriptionId);
|
|
339
304
|
// Verifying 1000ms after removing subscription in case there are new subscription unmount/mount
|
|
340
305
|
setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
|
341
|
-
}
|
|
342
|
-
|
|
306
|
+
}
|
|
307
|
+
_closeSocketIfRequired() {
|
|
343
308
|
if (this.subscriptionObserverMap.size > 0) {
|
|
344
309
|
// Active subscriptions on the WebSocket
|
|
345
310
|
return;
|
|
@@ -361,7 +326,7 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
361
326
|
if (this.keepAliveAlertTimeoutId) {
|
|
362
327
|
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
363
328
|
}
|
|
364
|
-
|
|
329
|
+
const tempSocket = this.awsRealTimeSocket;
|
|
365
330
|
// Cleaning callbacks to avoid race condition, socket still exists
|
|
366
331
|
tempSocket.onclose = null;
|
|
367
332
|
tempSocket.onerror = null;
|
|
@@ -370,27 +335,26 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
370
335
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
371
336
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
372
337
|
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
var _this = this;
|
|
338
|
+
}
|
|
339
|
+
_handleIncomingSubscriptionMessage(message) {
|
|
376
340
|
if (typeof message.data !== 'string') {
|
|
377
341
|
return;
|
|
378
342
|
}
|
|
379
|
-
logger.debug(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
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 });
|
|
383
347
|
if (type === MESSAGE_TYPES.GQL_DATA && payload && payload.data) {
|
|
384
348
|
if (observer) {
|
|
385
349
|
observer.next(payload);
|
|
386
350
|
}
|
|
387
351
|
else {
|
|
388
|
-
logger.debug(
|
|
352
|
+
logger.debug(`observer not found for id: ${id}`);
|
|
389
353
|
}
|
|
390
354
|
return;
|
|
391
355
|
}
|
|
392
356
|
if (type === MESSAGE_TYPES.GQL_START_ACK) {
|
|
393
|
-
logger.debug(
|
|
357
|
+
logger.debug(`subscription ready for ${JSON.stringify({ query, variables })}`);
|
|
394
358
|
if (typeof subscriptionReadyCallback === 'function') {
|
|
395
359
|
subscriptionReadyCallback();
|
|
396
360
|
}
|
|
@@ -398,19 +362,19 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
398
362
|
clearTimeout(startAckTimeoutId);
|
|
399
363
|
dispatchApiEvent({
|
|
400
364
|
event: CONTROL_MSG.SUBSCRIPTION_ACK,
|
|
401
|
-
data: { query
|
|
365
|
+
data: { query, variables },
|
|
402
366
|
message: 'Connection established for subscription',
|
|
403
367
|
});
|
|
404
|
-
|
|
368
|
+
const subscriptionState = SUBSCRIPTION_STATUS.CONNECTED;
|
|
405
369
|
if (observer) {
|
|
406
370
|
this.subscriptionObserverMap.set(id, {
|
|
407
|
-
observer
|
|
408
|
-
query
|
|
409
|
-
variables
|
|
371
|
+
observer,
|
|
372
|
+
query,
|
|
373
|
+
variables,
|
|
410
374
|
startAckTimeoutId: undefined,
|
|
411
|
-
subscriptionState
|
|
412
|
-
subscriptionReadyCallback
|
|
413
|
-
subscriptionFailedCallback
|
|
375
|
+
subscriptionState,
|
|
376
|
+
subscriptionReadyCallback,
|
|
377
|
+
subscriptionFailedCallback,
|
|
414
378
|
});
|
|
415
379
|
}
|
|
416
380
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_ESTABLISHED);
|
|
@@ -421,29 +385,31 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
421
385
|
clearTimeout(this.keepAliveTimeoutId);
|
|
422
386
|
if (this.keepAliveAlertTimeoutId)
|
|
423
387
|
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
424
|
-
this.keepAliveTimeoutId = setTimeout(
|
|
425
|
-
this.keepAliveAlertTimeoutId = setTimeout(
|
|
426
|
-
|
|
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);
|
|
427
391
|
}, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT);
|
|
428
392
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
|
|
429
393
|
return;
|
|
430
394
|
}
|
|
431
395
|
if (type === MESSAGE_TYPES.GQL_ERROR) {
|
|
432
|
-
|
|
396
|
+
const subscriptionState = SUBSCRIPTION_STATUS.FAILED;
|
|
433
397
|
if (observer) {
|
|
434
398
|
this.subscriptionObserverMap.set(id, {
|
|
435
|
-
observer
|
|
436
|
-
query
|
|
437
|
-
variables
|
|
438
|
-
startAckTimeoutId
|
|
439
|
-
subscriptionReadyCallback
|
|
440
|
-
subscriptionFailedCallback
|
|
441
|
-
subscriptionState
|
|
399
|
+
observer,
|
|
400
|
+
query,
|
|
401
|
+
variables,
|
|
402
|
+
startAckTimeoutId,
|
|
403
|
+
subscriptionReadyCallback,
|
|
404
|
+
subscriptionFailedCallback,
|
|
405
|
+
subscriptionState,
|
|
442
406
|
});
|
|
443
|
-
logger.debug(
|
|
407
|
+
logger.debug(`${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`);
|
|
444
408
|
observer.error({
|
|
445
409
|
errors: [
|
|
446
|
-
|
|
410
|
+
{
|
|
411
|
+
...new GraphQLError(`${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`),
|
|
412
|
+
},
|
|
447
413
|
],
|
|
448
414
|
});
|
|
449
415
|
if (startAckTimeoutId)
|
|
@@ -453,351 +419,269 @@ var AWSAppSyncRealTimeProvider = /** @class */ (function () {
|
|
|
453
419
|
}
|
|
454
420
|
}
|
|
455
421
|
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
logger.debug(
|
|
422
|
+
}
|
|
423
|
+
_errorDisconnect(msg) {
|
|
424
|
+
logger.debug(`Disconnect error: ${msg}`);
|
|
459
425
|
if (this.awsRealTimeSocket) {
|
|
460
426
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
461
427
|
this.awsRealTimeSocket.close();
|
|
462
428
|
}
|
|
463
429
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
430
|
+
}
|
|
431
|
+
_timeoutStartSubscriptionAck(subscriptionId) {
|
|
432
|
+
const subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
|
|
467
433
|
if (subscriptionObserver) {
|
|
468
|
-
|
|
434
|
+
const { observer, query, variables } = subscriptionObserver;
|
|
469
435
|
if (!observer) {
|
|
470
436
|
return;
|
|
471
437
|
}
|
|
472
438
|
this.subscriptionObserverMap.set(subscriptionId, {
|
|
473
|
-
observer
|
|
474
|
-
query
|
|
475
|
-
variables
|
|
439
|
+
observer,
|
|
440
|
+
query,
|
|
441
|
+
variables,
|
|
476
442
|
subscriptionState: SUBSCRIPTION_STATUS.FAILED,
|
|
477
443
|
});
|
|
478
444
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
479
|
-
logger.debug('timeoutStartSubscription', JSON.stringify({ query
|
|
445
|
+
logger.debug('timeoutStartSubscription', JSON.stringify({ query, variables }));
|
|
480
446
|
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
var _this = this;
|
|
484
|
-
var appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, authenticationType = _a.authenticationType, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
|
447
|
+
}
|
|
448
|
+
_initializeWebSocketConnection({ appSyncGraphqlEndpoint, authenticationType, apiKey, region, additionalHeaders, }) {
|
|
485
449
|
if (this.socketStatus === SOCKET_STATUS.READY) {
|
|
486
450
|
return;
|
|
487
451
|
}
|
|
488
|
-
return new Promise(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
headerQs = Buffer.from(headerString).toString('base64');
|
|
513
|
-
payloadQs = Buffer.from(payloadString).toString('base64');
|
|
514
|
-
discoverableEndpoint = appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '';
|
|
515
|
-
if (this.isCustomDomain(discoverableEndpoint)) {
|
|
516
|
-
discoverableEndpoint =
|
|
517
|
-
discoverableEndpoint.concat(customDomainPath);
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
discoverableEndpoint = discoverableEndpoint
|
|
521
|
-
.replace('appsync-api', 'appsync-realtime-api')
|
|
522
|
-
.replace('gogi-beta', 'grt-beta');
|
|
523
|
-
}
|
|
524
|
-
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 {
|
|
525
476
|
discoverableEndpoint = discoverableEndpoint
|
|
526
|
-
.replace('
|
|
527
|
-
.replace('
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
logger.debug('Connection exited with', err_3);
|
|
543
|
-
this.promiseArray.forEach(function (_a) {
|
|
544
|
-
var rej = _a.rej;
|
|
545
|
-
return rej(err_3);
|
|
546
|
-
});
|
|
547
|
-
this.promiseArray = [];
|
|
548
|
-
if (this.awsRealTimeSocket &&
|
|
549
|
-
this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
|
|
550
|
-
this.awsRealTimeSocket.close(3001);
|
|
551
|
-
}
|
|
552
|
-
this.awsRealTimeSocket = undefined;
|
|
553
|
-
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
554
|
-
return [3 /*break*/, 5];
|
|
555
|
-
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 = [];
|
|
556
493
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
case 1:
|
|
568
|
-
_a.sent();
|
|
569
|
-
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;
|
|
570
504
|
}
|
|
571
|
-
}
|
|
505
|
+
}
|
|
572
506
|
});
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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);
|
|
593
560
|
};
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
561
|
+
this.awsRealTimeSocket.onclose = event => {
|
|
562
|
+
logger.debug(`WebSocket closed ${event.reason}`);
|
|
563
|
+
this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
597
564
|
};
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
var ackOk_1 = false;
|
|
607
|
-
_this.awsRealTimeSocket.onerror = function (error) {
|
|
608
|
-
logger.debug("WebSocket error ".concat(JSON.stringify(error)));
|
|
609
|
-
};
|
|
610
|
-
_this.awsRealTimeSocket.onclose = function (event) {
|
|
611
|
-
logger.debug("WebSocket closed ".concat(event.reason));
|
|
612
|
-
rej(new Error(JSON.stringify(event)));
|
|
613
|
-
};
|
|
614
|
-
_this.awsRealTimeSocket.onmessage = function (message) {
|
|
615
|
-
if (typeof message.data !== 'string') {
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
logger.debug("subscription message from AWS AppSyncRealTime: ".concat(message.data, " "));
|
|
619
|
-
var data = JSON.parse(message.data);
|
|
620
|
-
var type = data.type, _a = data.payload, _b = _a === void 0 ? {} : _a, _c = _b.connectionTimeoutMs, connectionTimeoutMs = _c === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _c;
|
|
621
|
-
if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
|
|
622
|
-
ackOk_1 = true;
|
|
623
|
-
if (_this.awsRealTimeSocket) {
|
|
624
|
-
_this.keepAliveTimeout = connectionTimeoutMs;
|
|
625
|
-
_this.awsRealTimeSocket.onmessage =
|
|
626
|
-
_this._handleIncomingSubscriptionMessage.bind(_this);
|
|
627
|
-
_this.awsRealTimeSocket.onerror = function (err) {
|
|
628
|
-
logger.debug(err);
|
|
629
|
-
_this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
630
|
-
};
|
|
631
|
-
_this.awsRealTimeSocket.onclose = function (event) {
|
|
632
|
-
logger.debug("WebSocket closed ".concat(event.reason));
|
|
633
|
-
_this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
res('Cool, connected to AWS AppSyncRealTime');
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
|
640
|
-
var _d = data.payload, _e = _d === void 0 ? {} : _d, _f = _e.errors, _g = _f === void 0 ? [] : _f, _h = __read(_g, 1), _j = _h[0], _k = _j === void 0 ? {} : _j, _l = _k.errorType, errorType = _l === void 0 ? '' : _l, _m = _k.errorCode, errorCode = _m === void 0 ? 0 : _m;
|
|
641
|
-
rej({ errorType: errorType, errorCode: errorCode });
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
var gqlInit = {
|
|
645
|
-
type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
|
|
646
|
-
};
|
|
647
|
-
_this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
|
|
648
|
-
var checkAckOk_1 = function (ackOk) {
|
|
649
|
-
if (!ackOk) {
|
|
650
|
-
_this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
651
|
-
rej(new Error("Connection timeout: ack from AWSAppSyncRealTime was not received after ".concat(CONNECTION_INIT_TIMEOUT, " ms")));
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
setTimeout(function () { return checkAckOk_1(ackOk_1); }, CONNECTION_INIT_TIMEOUT);
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
})()];
|
|
658
|
-
case 3:
|
|
659
|
-
// Step 2: wait for ack from AWS AppSyncReaTime after sending init
|
|
660
|
-
_b.sent();
|
|
661
|
-
return [3 /*break*/, 5];
|
|
662
|
-
case 4:
|
|
663
|
-
err_4 = _b.sent();
|
|
664
|
-
_a = err_4, errorType = _a.errorType, errorCode = _a.errorCode;
|
|
665
|
-
if (NON_RETRYABLE_CODES.includes(errorCode)) {
|
|
666
|
-
throw new NonRetryableError(errorType);
|
|
667
|
-
}
|
|
668
|
-
else if (errorType) {
|
|
669
|
-
throw new Error(errorType);
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
throw err_4;
|
|
673
|
-
}
|
|
674
|
-
return [3 /*break*/, 5];
|
|
675
|
-
case 5: return [2 /*return*/];
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
});
|
|
679
|
-
};
|
|
680
|
-
AWSAppSyncRealTimeProvider.prototype._awsRealTimeHeaderBasedAuth = function (_a) {
|
|
681
|
-
var apiKey = _a.apiKey, authenticationType = _a.authenticationType, payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
|
682
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
683
|
-
var headerHandler, handler, host, resolvedApiKey, result;
|
|
684
|
-
return __generator(this, function (_b) {
|
|
685
|
-
switch (_b.label) {
|
|
686
|
-
case 0:
|
|
687
|
-
headerHandler = {
|
|
688
|
-
apiKey: this._awsRealTimeApiKeyHeader.bind(this),
|
|
689
|
-
iam: this._awsRealTimeIAMHeader.bind(this),
|
|
690
|
-
oidc: this._awsAuthTokenHeader.bind(this),
|
|
691
|
-
userPool: this._awsAuthTokenHeader.bind(this),
|
|
692
|
-
lambda: this._customAuthHeader,
|
|
693
|
-
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
|
+
}
|
|
694
573
|
};
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return [2 /*return*/, undefined];
|
|
698
|
-
case 1:
|
|
699
|
-
handler = headerHandler[authenticationType];
|
|
700
|
-
host = url.parse(appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '').host;
|
|
701
|
-
logger.debug("Authenticating with ".concat(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 _b, _c;
|
|
724
|
-
var host = _a.host;
|
|
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
|
+
}
|