1 | var __extends = (this && this.__extends) || (function () {
|
2 | var extendStatics = function (d, b) {
|
3 | extendStatics = Object.setPrototypeOf ||
|
4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
6 | return extendStatics(d, b);
|
7 | };
|
8 | return function (d, b) {
|
9 | extendStatics(d, b);
|
10 | function __() { this.constructor = d; }
|
11 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
12 | };
|
13 | })();
|
14 | var __assign = (this && this.__assign) || function () {
|
15 | __assign = Object.assign || function(t) {
|
16 | for (var s, i = 1, n = arguments.length; i < n; i++) {
|
17 | s = arguments[i];
|
18 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
19 | t[p] = s[p];
|
20 | }
|
21 | return t;
|
22 | };
|
23 | return __assign.apply(this, arguments);
|
24 | };
|
25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
27 | return new (P || (P = Promise))(function (resolve, reject) {
|
28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
31 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
32 | });
|
33 | };
|
34 | var __generator = (this && this.__generator) || function (thisArg, body) {
|
35 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
36 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
37 | function verb(n) { return function (v) { return step([n, v]); }; }
|
38 | function step(op) {
|
39 | if (f) throw new TypeError("Generator is already executing.");
|
40 | while (_) try {
|
41 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
42 | if (y = 0, t) op = [op[0] & 2, t.value];
|
43 | switch (op[0]) {
|
44 | case 0: case 1: t = op; break;
|
45 | case 4: _.label++; return { value: op[1], done: false };
|
46 | case 5: _.label++; y = op[1]; op = [0]; continue;
|
47 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
48 | default:
|
49 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
50 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
51 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
52 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
53 | if (t[2]) _.ops.pop();
|
54 | _.trys.pop(); continue;
|
55 | }
|
56 | op = body.call(thisArg, _);
|
57 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
58 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
59 | }
|
60 | };
|
61 | var __read = (this && this.__read) || function (o, n) {
|
62 | var m = typeof Symbol === "function" && o[Symbol.iterator];
|
63 | if (!m) return o;
|
64 | var i = m.call(o), r, ar = [], e;
|
65 | try {
|
66 | while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
67 | }
|
68 | catch (error) { e = { error: error }; }
|
69 | finally {
|
70 | try {
|
71 | if (r && !r.done && (m = i["return"])) m.call(i);
|
72 | }
|
73 | finally { if (e) throw e.error; }
|
74 | }
|
75 | return ar;
|
76 | };
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | import Observable from 'zen-observable-ts';
|
90 | import { GraphQLError } from 'graphql';
|
91 | import * as url from 'url';
|
92 | import { v4 as uuid } from 'uuid';
|
93 | import { Buffer } from 'buffer';
|
94 | import { Logger, Credentials, Signer, Hub, Constants, USER_AGENT_HEADER, jitteredExponentialRetry, NonRetryableError, } from '@aws-amplify/core';
|
95 | import Cache from '@aws-amplify/cache';
|
96 | import Auth from '@aws-amplify/auth';
|
97 | import { AbstractPubSubProvider } from '../PubSubProvider';
|
98 | import { CONTROL_MSG } from '../../index';
|
99 | import { AMPLIFY_SYMBOL, AWS_APPSYNC_REALTIME_HEADERS, CONNECTION_INIT_TIMEOUT, DEFAULT_KEEP_ALIVE_TIMEOUT, MAX_DELAY_MS, MESSAGE_TYPES, NON_RETRYABLE_CODES, SOCKET_STATUS, START_ACK_TIMEOUT, SUBSCRIPTION_STATUS, } from './constants';
|
100 | var logger = new Logger('AWSAppSyncRealTimeProvider');
|
101 | var dispatchApiEvent = function (event, data, message) {
|
102 | Hub.dispatch('api', { event: event, data: data, message: message }, 'PubSub', AMPLIFY_SYMBOL);
|
103 | };
|
104 | var standardDomainPattern = /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com\/graphql$/i;
|
105 | var customDomainPath = '/realtime';
|
106 | var AWSAppSyncRealTimeProvider = (function (_super) {
|
107 | __extends(AWSAppSyncRealTimeProvider, _super);
|
108 | function AWSAppSyncRealTimeProvider() {
|
109 | var _this = _super !== null && _super.apply(this, arguments) || this;
|
110 | _this.socketStatus = SOCKET_STATUS.CLOSED;
|
111 | _this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
112 | _this.subscriptionObserverMap = new Map();
|
113 | _this.promiseArray = [];
|
114 | return _this;
|
115 | }
|
116 | AWSAppSyncRealTimeProvider.prototype.getNewWebSocket = function (url, protocol) {
|
117 | return new WebSocket(url, protocol);
|
118 | };
|
119 | AWSAppSyncRealTimeProvider.prototype.getProviderName = function () {
|
120 | return 'AWSAppSyncRealTimeProvider';
|
121 | };
|
122 | AWSAppSyncRealTimeProvider.prototype.newClient = function () {
|
123 | throw new Error('Not used here');
|
124 | };
|
125 | AWSAppSyncRealTimeProvider.prototype.publish = function (_topics, _msg, _options) {
|
126 | return __awaiter(this, void 0, void 0, function () {
|
127 | return __generator(this, function (_a) {
|
128 | throw new Error('Operation not supported');
|
129 | });
|
130 | });
|
131 | };
|
132 |
|
133 | AWSAppSyncRealTimeProvider.prototype.isCustomDomain = function (url) {
|
134 | return url.match(standardDomainPattern) === null;
|
135 | };
|
136 | AWSAppSyncRealTimeProvider.prototype.subscribe = function (_topics, options) {
|
137 | var _this = this;
|
138 | var appSyncGraphqlEndpoint = options === null || options === void 0 ? void 0 : options.appSyncGraphqlEndpoint;
|
139 | return new Observable(function (observer) {
|
140 | if (!options || !appSyncGraphqlEndpoint) {
|
141 | observer.error({
|
142 | errors: [
|
143 | __assign({}, new GraphQLError("Subscribe only available for AWS AppSync endpoint")),
|
144 | ],
|
145 | });
|
146 | observer.complete();
|
147 | }
|
148 | else {
|
149 | var subscriptionId_1 = uuid();
|
150 | _this._startSubscriptionWithAWSAppSyncRealTime({
|
151 | options: options,
|
152 | observer: observer,
|
153 | subscriptionId: subscriptionId_1,
|
154 | }).catch(function (err) {
|
155 | observer.error({
|
156 | errors: [
|
157 | __assign({}, new GraphQLError(CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR + ": " + err)),
|
158 | ],
|
159 | });
|
160 | observer.complete();
|
161 | });
|
162 | return function () { return __awaiter(_this, void 0, void 0, function () {
|
163 | var subscriptionState, err_1;
|
164 | return __generator(this, function (_a) {
|
165 | switch (_a.label) {
|
166 | case 0:
|
167 | _a.trys.push([0, 2, 3, 4]);
|
168 |
|
169 | return [4 , this._waitForSubscriptionToBeConnected(subscriptionId_1)];
|
170 | case 1:
|
171 |
|
172 | _a.sent();
|
173 | subscriptionState = (this.subscriptionObserverMap.get(subscriptionId_1) || {}).subscriptionState;
|
174 | if (!subscriptionState) {
|
175 |
|
176 | return [2 ];
|
177 | }
|
178 | if (subscriptionState === SUBSCRIPTION_STATUS.CONNECTED) {
|
179 | this._sendUnsubscriptionMessage(subscriptionId_1);
|
180 | }
|
181 | else {
|
182 | throw new Error('Subscription never connected');
|
183 | }
|
184 | return [3 , 4];
|
185 | case 2:
|
186 | err_1 = _a.sent();
|
187 | logger.debug("Error while unsubscribing " + err_1);
|
188 | return [3 , 4];
|
189 | case 3:
|
190 | this._removeSubscriptionObserver(subscriptionId_1);
|
191 | return [7 ];
|
192 | case 4: return [2 ];
|
193 | }
|
194 | });
|
195 | }); };
|
196 | }
|
197 | });
|
198 | };
|
199 | Object.defineProperty(AWSAppSyncRealTimeProvider.prototype, "isSSLEnabled", {
|
200 | get: function () {
|
201 | return !this.options
|
202 | .aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
|
203 | },
|
204 | enumerable: true,
|
205 | configurable: true
|
206 | });
|
207 | AWSAppSyncRealTimeProvider.prototype._startSubscriptionWithAWSAppSyncRealTime = function (_a) {
|
208 | var options = _a.options, observer = _a.observer, subscriptionId = _a.subscriptionId;
|
209 | var _b, _c;
|
210 | return __awaiter(this, void 0, void 0, function () {
|
211 | var appSyncGraphqlEndpoint, authenticationType, query, variables, apiKey, region, _d, graphql_headers, _e, additionalHeaders, subscriptionState, data, dataString, headerObj, _f, _g, subscriptionMessage, stringToAWSRealTime, err_2, message, subscriptionFailedCallback_1, _h, subscriptionFailedCallback, subscriptionReadyCallback;
|
212 | var _j;
|
213 | var _this = this;
|
214 | return __generator(this, function (_k) {
|
215 | switch (_k.label) {
|
216 | case 0:
|
217 | appSyncGraphqlEndpoint = options.appSyncGraphqlEndpoint, authenticationType = options.authenticationType, query = options.query, variables = options.variables, apiKey = options.apiKey, region = options.region, _d = options.graphql_headers, graphql_headers = _d === void 0 ? function () { return ({}); } : _d, _e = options.additionalHeaders, additionalHeaders = _e === void 0 ? {} : _e;
|
218 | subscriptionState = SUBSCRIPTION_STATUS.PENDING;
|
219 | data = {
|
220 | query: query,
|
221 | variables: variables,
|
222 | };
|
223 |
|
224 | this.subscriptionObserverMap.set(subscriptionId, {
|
225 | observer: observer,
|
226 | query: query !== null && query !== void 0 ? query : '',
|
227 | variables: variables !== null && variables !== void 0 ? variables : {},
|
228 | subscriptionState: subscriptionState,
|
229 | startAckTimeoutId: undefined,
|
230 | });
|
231 | dataString = JSON.stringify(data);
|
232 | _f = [{}];
|
233 | return [4 , this._awsRealTimeHeaderBasedAuth({
|
234 | apiKey: apiKey,
|
235 | appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
236 | authenticationType: authenticationType,
|
237 | payload: dataString,
|
238 | canonicalUri: '',
|
239 | region: region,
|
240 | additionalHeaders: additionalHeaders,
|
241 | })];
|
242 | case 1:
|
243 | _g = [__assign.apply(void 0, _f.concat([(_k.sent())]))];
|
244 | return [4 , graphql_headers()];
|
245 | case 2:
|
246 | headerObj = __assign.apply(void 0, [__assign.apply(void 0, [__assign.apply(void 0, _g.concat([(_k.sent())])), additionalHeaders]), (_j = {}, _j[USER_AGENT_HEADER] = Constants.userAgent, _j)]);
|
247 | subscriptionMessage = {
|
248 | id: subscriptionId,
|
249 | payload: {
|
250 | data: dataString,
|
251 | extensions: {
|
252 | authorization: __assign({}, headerObj),
|
253 | },
|
254 | },
|
255 | type: MESSAGE_TYPES.GQL_START,
|
256 | };
|
257 | stringToAWSRealTime = JSON.stringify(subscriptionMessage);
|
258 | _k.label = 3;
|
259 | case 3:
|
260 | _k.trys.push([3, 5, , 6]);
|
261 | return [4 , this._initializeWebSocketConnection({
|
262 | apiKey: apiKey,
|
263 | appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
264 | authenticationType: authenticationType,
|
265 | region: region,
|
266 | additionalHeaders: additionalHeaders,
|
267 | })];
|
268 | case 4:
|
269 | _k.sent();
|
270 | return [3 , 6];
|
271 | case 5:
|
272 | err_2 = _k.sent();
|
273 | logger.debug({ err: err_2 });
|
274 | message = (_b = err_2['message']) !== null && _b !== void 0 ? _b : '';
|
275 | observer.error({
|
276 | errors: [
|
277 | __assign({}, new GraphQLError(CONTROL_MSG.CONNECTION_FAILED + ": " + message)),
|
278 | ],
|
279 | });
|
280 | observer.complete();
|
281 | subscriptionFailedCallback_1 = (this.subscriptionObserverMap.get(subscriptionId) || {}).subscriptionFailedCallback;
|
282 |
|
283 | if (typeof subscriptionFailedCallback_1 === 'function') {
|
284 | subscriptionFailedCallback_1();
|
285 | }
|
286 | return [2 ];
|
287 | case 6:
|
288 | _h = (_c = this.subscriptionObserverMap.get(subscriptionId)) !== null && _c !== void 0 ? _c : {}, subscriptionFailedCallback = _h.subscriptionFailedCallback, subscriptionReadyCallback = _h.subscriptionReadyCallback;
|
289 |
|
290 | this.subscriptionObserverMap.set(subscriptionId, {
|
291 | observer: observer,
|
292 | subscriptionState: subscriptionState,
|
293 | query: query !== null && query !== void 0 ? query : '',
|
294 | variables: variables !== null && variables !== void 0 ? variables : {},
|
295 | subscriptionReadyCallback: subscriptionReadyCallback,
|
296 | subscriptionFailedCallback: subscriptionFailedCallback,
|
297 | startAckTimeoutId: setTimeout(function () {
|
298 | _this._timeoutStartSubscriptionAck.call(_this, subscriptionId);
|
299 | }, START_ACK_TIMEOUT),
|
300 | });
|
301 | if (this.awsRealTimeSocket) {
|
302 | this.awsRealTimeSocket.send(stringToAWSRealTime);
|
303 | }
|
304 | return [2 ];
|
305 | }
|
306 | });
|
307 | });
|
308 | };
|
309 |
|
310 | AWSAppSyncRealTimeProvider.prototype._waitForSubscriptionToBeConnected = function (subscriptionId) {
|
311 | return __awaiter(this, void 0, void 0, function () {
|
312 | var subscriptionObserver, subscriptionState;
|
313 | var _this = this;
|
314 | return __generator(this, function (_a) {
|
315 | subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
|
316 | if (subscriptionObserver) {
|
317 | subscriptionState = subscriptionObserver.subscriptionState;
|
318 |
|
319 | if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
|
320 | return [2 , new Promise(function (res, rej) {
|
321 | var observer = subscriptionObserver.observer, subscriptionState = subscriptionObserver.subscriptionState, variables = subscriptionObserver.variables, query = subscriptionObserver.query;
|
322 | _this.subscriptionObserverMap.set(subscriptionId, {
|
323 | observer: observer,
|
324 | subscriptionState: subscriptionState,
|
325 | variables: variables,
|
326 | query: query,
|
327 | subscriptionReadyCallback: res,
|
328 | subscriptionFailedCallback: rej,
|
329 | });
|
330 | })];
|
331 | }
|
332 | }
|
333 | return [2 ];
|
334 | });
|
335 | });
|
336 | };
|
337 | AWSAppSyncRealTimeProvider.prototype._sendUnsubscriptionMessage = function (subscriptionId) {
|
338 | try {
|
339 | if (this.awsRealTimeSocket &&
|
340 | this.awsRealTimeSocket.readyState === WebSocket.OPEN &&
|
341 | this.socketStatus === SOCKET_STATUS.READY) {
|
342 |
|
343 | var unsubscribeMessage = {
|
344 | id: subscriptionId,
|
345 | type: MESSAGE_TYPES.GQL_STOP,
|
346 | };
|
347 | var stringToAWSRealTime = JSON.stringify(unsubscribeMessage);
|
348 | this.awsRealTimeSocket.send(stringToAWSRealTime);
|
349 | }
|
350 | }
|
351 | catch (err) {
|
352 |
|
353 | logger.debug({ err: err });
|
354 | }
|
355 | };
|
356 | AWSAppSyncRealTimeProvider.prototype._removeSubscriptionObserver = function (subscriptionId) {
|
357 | this.subscriptionObserverMap.delete(subscriptionId);
|
358 |
|
359 | setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
360 | };
|
361 | AWSAppSyncRealTimeProvider.prototype._closeSocketIfRequired = function () {
|
362 | if (this.subscriptionObserverMap.size > 0) {
|
363 |
|
364 | return;
|
365 | }
|
366 | if (!this.awsRealTimeSocket) {
|
367 | this.socketStatus = SOCKET_STATUS.CLOSED;
|
368 | return;
|
369 | }
|
370 | if (this.awsRealTimeSocket.bufferedAmount > 0) {
|
371 |
|
372 | setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
373 | }
|
374 | else {
|
375 | logger.debug('closing WebSocket...');
|
376 | if (this.keepAliveTimeoutId)
|
377 | clearTimeout(this.keepAliveTimeoutId);
|
378 | var tempSocket = this.awsRealTimeSocket;
|
379 |
|
380 | tempSocket.onclose = null;
|
381 | tempSocket.onerror = null;
|
382 | tempSocket.close(1000);
|
383 | this.awsRealTimeSocket = undefined;
|
384 | this.socketStatus = SOCKET_STATUS.CLOSED;
|
385 | }
|
386 | };
|
387 | AWSAppSyncRealTimeProvider.prototype._handleIncomingSubscriptionMessage = function (message) {
|
388 | logger.debug("subscription message from AWS AppSync RealTime: " + message.data);
|
389 | var _a = JSON.parse(message.data), _b = _a.id, id = _b === void 0 ? '' : _b, payload = _a.payload, type = _a.type;
|
390 | var _c = this.subscriptionObserverMap.get(id) || {}, _d = _c.observer, observer = _d === void 0 ? null : _d, _e = _c.query, query = _e === void 0 ? '' : _e, _f = _c.variables, variables = _f === void 0 ? {} : _f, startAckTimeoutId = _c.startAckTimeoutId, subscriptionReadyCallback = _c.subscriptionReadyCallback, subscriptionFailedCallback = _c.subscriptionFailedCallback;
|
391 | logger.debug({ id: id, observer: observer, query: query, variables: variables });
|
392 | if (type === MESSAGE_TYPES.GQL_DATA && payload && payload.data) {
|
393 | if (observer) {
|
394 | observer.next(payload);
|
395 | }
|
396 | else {
|
397 | logger.debug("observer not found for id: " + id);
|
398 | }
|
399 | return;
|
400 | }
|
401 | if (type === MESSAGE_TYPES.GQL_START_ACK) {
|
402 | logger.debug("subscription ready for " + JSON.stringify({ query: query, variables: variables }));
|
403 | if (typeof subscriptionReadyCallback === 'function') {
|
404 | subscriptionReadyCallback();
|
405 | }
|
406 | if (startAckTimeoutId)
|
407 | clearTimeout(startAckTimeoutId);
|
408 | dispatchApiEvent(CONTROL_MSG.SUBSCRIPTION_ACK, { query: query, variables: variables }, 'Connection established for subscription');
|
409 | var subscriptionState = SUBSCRIPTION_STATUS.CONNECTED;
|
410 | if (observer) {
|
411 | this.subscriptionObserverMap.set(id, {
|
412 | observer: observer,
|
413 | query: query,
|
414 | variables: variables,
|
415 | startAckTimeoutId: undefined,
|
416 | subscriptionState: subscriptionState,
|
417 | subscriptionReadyCallback: subscriptionReadyCallback,
|
418 | subscriptionFailedCallback: subscriptionFailedCallback,
|
419 | });
|
420 | }
|
421 |
|
422 | return;
|
423 | }
|
424 | if (type === MESSAGE_TYPES.GQL_CONNECTION_KEEP_ALIVE) {
|
425 | if (this.keepAliveTimeoutId)
|
426 | clearTimeout(this.keepAliveTimeoutId);
|
427 | this.keepAliveTimeoutId = setTimeout(this._errorDisconnect.bind(this, CONTROL_MSG.TIMEOUT_DISCONNECT), this.keepAliveTimeout);
|
428 | return;
|
429 | }
|
430 | if (type === MESSAGE_TYPES.GQL_ERROR) {
|
431 | var subscriptionState = SUBSCRIPTION_STATUS.FAILED;
|
432 | if (observer) {
|
433 | this.subscriptionObserverMap.set(id, {
|
434 | observer: observer,
|
435 | query: query,
|
436 | variables: variables,
|
437 | startAckTimeoutId: startAckTimeoutId,
|
438 | subscriptionReadyCallback: subscriptionReadyCallback,
|
439 | subscriptionFailedCallback: subscriptionFailedCallback,
|
440 | subscriptionState: subscriptionState,
|
441 | });
|
442 | observer.error({
|
443 | errors: [
|
444 | __assign({}, new GraphQLError(CONTROL_MSG.CONNECTION_FAILED + ": " + JSON.stringify(payload))),
|
445 | ],
|
446 | });
|
447 | if (startAckTimeoutId)
|
448 | clearTimeout(startAckTimeoutId);
|
449 | observer.complete();
|
450 | if (typeof subscriptionFailedCallback === 'function') {
|
451 | subscriptionFailedCallback();
|
452 | }
|
453 | }
|
454 | }
|
455 | };
|
456 | AWSAppSyncRealTimeProvider.prototype._errorDisconnect = function (msg) {
|
457 | logger.debug("Disconnect error: " + msg);
|
458 | this.subscriptionObserverMap.forEach(function (_a) {
|
459 | var observer = _a.observer;
|
460 | if (observer && !observer.closed) {
|
461 | observer.error({
|
462 | errors: [__assign({}, new GraphQLError(msg))],
|
463 | });
|
464 | }
|
465 | });
|
466 | this.subscriptionObserverMap.clear();
|
467 | if (this.awsRealTimeSocket) {
|
468 | this.awsRealTimeSocket.close();
|
469 | }
|
470 | this.socketStatus = SOCKET_STATUS.CLOSED;
|
471 | };
|
472 | AWSAppSyncRealTimeProvider.prototype._timeoutStartSubscriptionAck = function (subscriptionId) {
|
473 | var subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
|
474 | if (subscriptionObserver) {
|
475 | var observer = subscriptionObserver.observer, query = subscriptionObserver.query, variables = subscriptionObserver.variables;
|
476 | if (!observer) {
|
477 | return;
|
478 | }
|
479 | this.subscriptionObserverMap.set(subscriptionId, {
|
480 | observer: observer,
|
481 | query: query,
|
482 | variables: variables,
|
483 | subscriptionState: SUBSCRIPTION_STATUS.FAILED,
|
484 | });
|
485 | if (observer && !observer.closed) {
|
486 | observer.error({
|
487 | errors: [
|
488 | __assign({}, new GraphQLError("Subscription timeout " + JSON.stringify({
|
489 | query: query,
|
490 | variables: variables,
|
491 | }))),
|
492 | ],
|
493 | });
|
494 |
|
495 | observer.complete();
|
496 | }
|
497 | logger.debug('timeoutStartSubscription', JSON.stringify({ query: query, variables: variables }));
|
498 | }
|
499 | };
|
500 | AWSAppSyncRealTimeProvider.prototype._initializeWebSocketConnection = function (_a) {
|
501 | var _this = this;
|
502 | var appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, authenticationType = _a.authenticationType, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
503 | if (this.socketStatus === SOCKET_STATUS.READY) {
|
504 | return;
|
505 | }
|
506 | return new Promise(function (res, rej) { return __awaiter(_this, void 0, void 0, function () {
|
507 | var payloadString, headerString, _a, _b, headerQs, payloadQs, discoverableEndpoint, protocol, awsRealTimeUrl, err_3;
|
508 | return __generator(this, function (_c) {
|
509 | switch (_c.label) {
|
510 | case 0:
|
511 | this.promiseArray.push({ res: res, rej: rej });
|
512 | if (!(this.socketStatus === SOCKET_STATUS.CLOSED)) return [3 , 5];
|
513 | _c.label = 1;
|
514 | case 1:
|
515 | _c.trys.push([1, 4, , 5]);
|
516 | this.socketStatus = SOCKET_STATUS.CONNECTING;
|
517 | payloadString = '{}';
|
518 | _b = (_a = JSON).stringify;
|
519 | return [4 , this._awsRealTimeHeaderBasedAuth({
|
520 | authenticationType: authenticationType,
|
521 | payload: payloadString,
|
522 | canonicalUri: '/connect',
|
523 | apiKey: apiKey,
|
524 | appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
525 | region: region,
|
526 | additionalHeaders: additionalHeaders,
|
527 | })];
|
528 | case 2:
|
529 | headerString = _b.apply(_a, [_c.sent()]);
|
530 | headerQs = Buffer.from(headerString).toString('base64');
|
531 | payloadQs = Buffer.from(payloadString).toString('base64');
|
532 | discoverableEndpoint = appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '';
|
533 | if (this.isCustomDomain(discoverableEndpoint)) {
|
534 | discoverableEndpoint =
|
535 | discoverableEndpoint.concat(customDomainPath);
|
536 | }
|
537 | else {
|
538 | discoverableEndpoint = discoverableEndpoint
|
539 | .replace('appsync-api', 'appsync-realtime-api')
|
540 | .replace('gogi-beta', 'grt-beta');
|
541 | }
|
542 | protocol = this.isSSLEnabled ? 'wss://' : 'ws://';
|
543 | discoverableEndpoint = discoverableEndpoint
|
544 | .replace('https://', protocol)
|
545 | .replace('http://', protocol);
|
546 | awsRealTimeUrl = discoverableEndpoint + "?header=" + headerQs + "&payload=" + payloadQs;
|
547 | return [4 , this._initializeRetryableHandshake(awsRealTimeUrl)];
|
548 | case 3:
|
549 | _c.sent();
|
550 | this.promiseArray.forEach(function (_a) {
|
551 | var res = _a.res;
|
552 | logger.debug('Notifying connection successful');
|
553 | res();
|
554 | });
|
555 | this.socketStatus = SOCKET_STATUS.READY;
|
556 | this.promiseArray = [];
|
557 | return [3 , 5];
|
558 | case 4:
|
559 | err_3 = _c.sent();
|
560 | this.promiseArray.forEach(function (_a) {
|
561 | var rej = _a.rej;
|
562 | return rej(err_3);
|
563 | });
|
564 | this.promiseArray = [];
|
565 | if (this.awsRealTimeSocket &&
|
566 | this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
|
567 | this.awsRealTimeSocket.close(3001);
|
568 | }
|
569 | this.awsRealTimeSocket = undefined;
|
570 | this.socketStatus = SOCKET_STATUS.CLOSED;
|
571 | return [3 , 5];
|
572 | case 5: return [2 ];
|
573 | }
|
574 | });
|
575 | }); });
|
576 | };
|
577 | AWSAppSyncRealTimeProvider.prototype._initializeRetryableHandshake = function (awsRealTimeUrl) {
|
578 | return __awaiter(this, void 0, void 0, function () {
|
579 | return __generator(this, function (_a) {
|
580 | switch (_a.label) {
|
581 | case 0:
|
582 | logger.debug("Initializaling retryable Handshake");
|
583 | return [4 , jitteredExponentialRetry(this._initializeHandshake.bind(this), [awsRealTimeUrl], MAX_DELAY_MS)];
|
584 | case 1:
|
585 | _a.sent();
|
586 | return [2 ];
|
587 | }
|
588 | });
|
589 | });
|
590 | };
|
591 | AWSAppSyncRealTimeProvider.prototype._initializeHandshake = function (awsRealTimeUrl) {
|
592 | return __awaiter(this, void 0, void 0, function () {
|
593 | var err_4, _a, errorType, errorCode;
|
594 | var _this = this;
|
595 | return __generator(this, function (_b) {
|
596 | switch (_b.label) {
|
597 | case 0:
|
598 | logger.debug("Initializing handshake " + awsRealTimeUrl);
|
599 | _b.label = 1;
|
600 | case 1:
|
601 | _b.trys.push([1, 4, , 5]);
|
602 | return [4 , (function () {
|
603 | return new Promise(function (res, rej) {
|
604 | var newSocket = _this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws');
|
605 | newSocket.onerror = function () {
|
606 | logger.debug("WebSocket connection error");
|
607 | };
|
608 | newSocket.onclose = function () {
|
609 | rej(new Error('Connection handshake error'));
|
610 | };
|
611 | newSocket.onopen = function () {
|
612 | _this.awsRealTimeSocket = newSocket;
|
613 | return res();
|
614 | };
|
615 | });
|
616 | })()];
|
617 | case 2:
|
618 | _b.sent();
|
619 |
|
620 | return [4 , (function () {
|
621 | return new Promise(function (res, rej) {
|
622 | if (_this.awsRealTimeSocket) {
|
623 | var ackOk_1 = false;
|
624 | _this.awsRealTimeSocket.onerror = function (error) {
|
625 | logger.debug("WebSocket error " + JSON.stringify(error));
|
626 | };
|
627 | _this.awsRealTimeSocket.onclose = function (event) {
|
628 | logger.debug("WebSocket closed " + event.reason);
|
629 | rej(new Error(JSON.stringify(event)));
|
630 | };
|
631 | _this.awsRealTimeSocket.onmessage = function (message) {
|
632 | logger.debug("subscription message from AWS AppSyncRealTime: " + message.data + " ");
|
633 | var data = JSON.parse(message.data);
|
634 | var type = data.type, _a = data.payload, _b = (_a === void 0 ? {} : _a).connectionTimeoutMs, connectionTimeoutMs = _b === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _b;
|
635 | if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
|
636 | ackOk_1 = true;
|
637 | if (_this.awsRealTimeSocket) {
|
638 | _this.keepAliveTimeout = connectionTimeoutMs;
|
639 | _this.awsRealTimeSocket.onmessage =
|
640 | _this._handleIncomingSubscriptionMessage.bind(_this);
|
641 | _this.awsRealTimeSocket.onerror = function (err) {
|
642 | logger.debug(err);
|
643 | _this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
644 | };
|
645 | _this.awsRealTimeSocket.onclose = function (event) {
|
646 | logger.debug("WebSocket closed " + event.reason);
|
647 | _this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
648 | };
|
649 | }
|
650 | res('Cool, connected to AWS AppSyncRealTime');
|
651 | return;
|
652 | }
|
653 | if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
654 | 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;
|
655 | rej({ errorType: errorType, errorCode: errorCode });
|
656 | }
|
657 | };
|
658 | var gqlInit = {
|
659 | type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
|
660 | };
|
661 | _this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
|
662 | setTimeout(checkAckOk.bind(_this, ackOk_1), CONNECTION_INIT_TIMEOUT);
|
663 | }
|
664 | function checkAckOk(ackOk) {
|
665 | if (!ackOk) {
|
666 | rej(new Error("Connection timeout: ack from AWSRealTime was not received on " + CONNECTION_INIT_TIMEOUT + " ms"));
|
667 | }
|
668 | }
|
669 | });
|
670 | })()];
|
671 | case 3:
|
672 |
|
673 | _b.sent();
|
674 | return [3 , 5];
|
675 | case 4:
|
676 | err_4 = _b.sent();
|
677 | _a = err_4, errorType = _a.errorType, errorCode = _a.errorCode;
|
678 | if (NON_RETRYABLE_CODES.includes(errorCode)) {
|
679 | throw new NonRetryableError(errorType);
|
680 | }
|
681 | else if (errorType) {
|
682 | throw new Error(errorType);
|
683 | }
|
684 | else {
|
685 | throw err_4;
|
686 | }
|
687 | return [3 , 5];
|
688 | case 5: return [2 ];
|
689 | }
|
690 | });
|
691 | });
|
692 | };
|
693 | AWSAppSyncRealTimeProvider.prototype._awsRealTimeHeaderBasedAuth = function (_a) {
|
694 | var authenticationType = _a.authenticationType, payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
|
695 | return __awaiter(this, void 0, void 0, function () {
|
696 | var headerHandler, handler, host, result;
|
697 | return __generator(this, function (_b) {
|
698 | switch (_b.label) {
|
699 | case 0:
|
700 | headerHandler = {
|
701 | API_KEY: this._awsRealTimeApiKeyHeader.bind(this),
|
702 | AWS_IAM: this._awsRealTimeIAMHeader.bind(this),
|
703 | OPENID_CONNECT: this._awsRealTimeOPENIDHeader.bind(this),
|
704 | AMAZON_COGNITO_USER_POOLS: this._awsRealTimeCUPHeader.bind(this),
|
705 | AWS_LAMBDA: this._customAuthHeader,
|
706 | };
|
707 | if (!(!authenticationType || !headerHandler[authenticationType])) return [3 , 1];
|
708 | logger.debug("Authentication type " + authenticationType + " not supported");
|
709 | return [2 , ''];
|
710 | case 1:
|
711 | handler = headerHandler[authenticationType];
|
712 | host = url.parse(appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '').host;
|
713 | logger.debug("Authenticating with " + authenticationType);
|
714 | return [4 , handler({
|
715 | payload: payload,
|
716 | canonicalUri: canonicalUri,
|
717 | appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
|
718 | apiKey: apiKey,
|
719 | region: region,
|
720 | host: host,
|
721 | additionalHeaders: additionalHeaders,
|
722 | })];
|
723 | case 2:
|
724 | result = _b.sent();
|
725 | return [2 , result];
|
726 | }
|
727 | });
|
728 | });
|
729 | };
|
730 | AWSAppSyncRealTimeProvider.prototype._awsRealTimeCUPHeader = function (_a) {
|
731 | var host = _a.host;
|
732 | return __awaiter(this, void 0, void 0, function () {
|
733 | var session;
|
734 | return __generator(this, function (_b) {
|
735 | switch (_b.label) {
|
736 | case 0: return [4 , Auth.currentSession()];
|
737 | case 1:
|
738 | session = _b.sent();
|
739 | return [2 , {
|
740 | Authorization: session.getAccessToken().getJwtToken(),
|
741 | host: host,
|
742 | }];
|
743 | }
|
744 | });
|
745 | });
|
746 | };
|
747 | AWSAppSyncRealTimeProvider.prototype._awsRealTimeOPENIDHeader = function (_a) {
|
748 | var host = _a.host;
|
749 | return __awaiter(this, void 0, void 0, function () {
|
750 | var token, federatedInfo, currentUser;
|
751 | return __generator(this, function (_b) {
|
752 | switch (_b.label) {
|
753 | case 0: return [4 , Cache.getItem('federatedInfo')];
|
754 | case 1:
|
755 | federatedInfo = _b.sent();
|
756 | if (!federatedInfo) return [3 , 2];
|
757 | token = federatedInfo.token;
|
758 | return [3 , 4];
|
759 | case 2: return [4 , Auth.currentAuthenticatedUser()];
|
760 | case 3:
|
761 | currentUser = _b.sent();
|
762 | if (currentUser) {
|
763 | token = currentUser.token;
|
764 | }
|
765 | _b.label = 4;
|
766 | case 4:
|
767 | if (!token) {
|
768 | throw new Error('No federated jwt');
|
769 | }
|
770 | return [2 , {
|
771 | Authorization: token,
|
772 | host: host,
|
773 | }];
|
774 | }
|
775 | });
|
776 | });
|
777 | };
|
778 | AWSAppSyncRealTimeProvider.prototype._awsRealTimeApiKeyHeader = function (_a) {
|
779 | var apiKey = _a.apiKey, host = _a.host;
|
780 | return __awaiter(this, void 0, void 0, function () {
|
781 | var dt, dtStr;
|
782 | return __generator(this, function (_b) {
|
783 | dt = new Date();
|
784 | dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, '');
|
785 | return [2 , {
|
786 | host: host,
|
787 | 'x-amz-date': dtStr,
|
788 | 'x-api-key': apiKey,
|
789 | }];
|
790 | });
|
791 | });
|
792 | };
|
793 | AWSAppSyncRealTimeProvider.prototype._awsRealTimeIAMHeader = function (_a) {
|
794 | var payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, region = _a.region;
|
795 | return __awaiter(this, void 0, void 0, function () {
|
796 | var endpointInfo, credentialsOK, creds, request, signed_params;
|
797 | return __generator(this, function (_b) {
|
798 | switch (_b.label) {
|
799 | case 0:
|
800 | endpointInfo = {
|
801 | region: region,
|
802 | service: 'appsync',
|
803 | };
|
804 | return [4 , this._ensureCredentials()];
|
805 | case 1:
|
806 | credentialsOK = _b.sent();
|
807 | if (!credentialsOK) {
|
808 | throw new Error('No credentials');
|
809 | }
|
810 | return [4 , Credentials.get().then(function (credentials) {
|
811 | var _a = credentials, secretAccessKey = _a.secretAccessKey, accessKeyId = _a.accessKeyId, sessionToken = _a.sessionToken;
|
812 | return {
|
813 | secret_key: secretAccessKey,
|
814 | access_key: accessKeyId,
|
815 | session_token: sessionToken,
|
816 | };
|
817 | })];
|
818 | case 2:
|
819 | creds = _b.sent();
|
820 | request = {
|
821 | url: "" + appSyncGraphqlEndpoint + canonicalUri,
|
822 | data: payload,
|
823 | method: 'POST',
|
824 | headers: __assign({}, AWS_APPSYNC_REALTIME_HEADERS),
|
825 | };
|
826 | signed_params = Signer.sign(request, creds, endpointInfo);
|
827 | return [2 , signed_params.headers];
|
828 | }
|
829 | });
|
830 | });
|
831 | };
|
832 | AWSAppSyncRealTimeProvider.prototype._customAuthHeader = function (_a) {
|
833 | var host = _a.host, additionalHeaders = _a.additionalHeaders;
|
834 | if (!additionalHeaders || !additionalHeaders['Authorization']) {
|
835 | throw new Error('No auth token specified');
|
836 | }
|
837 | return {
|
838 | Authorization: additionalHeaders.Authorization,
|
839 | host: host,
|
840 | };
|
841 | };
|
842 | |
843 |
|
844 |
|
845 | AWSAppSyncRealTimeProvider.prototype._ensureCredentials = function () {
|
846 | return Credentials.get()
|
847 | .then(function (credentials) {
|
848 | if (!credentials)
|
849 | return false;
|
850 | var cred = Credentials.shear(credentials);
|
851 | logger.debug('set credentials for AWSAppSyncRealTimeProvider', cred);
|
852 | return true;
|
853 | })
|
854 | .catch(function (err) {
|
855 | logger.warn('ensure credentials error', err);
|
856 | return false;
|
857 | });
|
858 | };
|
859 | return AWSAppSyncRealTimeProvider;
|
860 | }(AbstractPubSubProvider));
|
861 | export { AWSAppSyncRealTimeProvider };
|
862 |
|
\ | No newline at end of file |