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