@aws-amplify/api-graphql 4.7.2-graphql-data-ka.518fb2c.0 → 4.7.2-publish-event-websocket.65d9752.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/dist/cjs/Providers/AWSAppSyncEventsProvider/index.js +15 -14
- package/dist/cjs/Providers/AWSAppSyncEventsProvider/index.js.map +1 -1
- package/dist/cjs/Providers/AWSWebSocketProvider/index.js +55 -49
- package/dist/cjs/Providers/AWSWebSocketProvider/index.js.map +1 -1
- package/dist/cjs/Providers/constants.js +1 -5
- package/dist/cjs/Providers/constants.js.map +1 -1
- package/dist/cjs/internals/events/index.js +11 -1
- package/dist/cjs/internals/events/index.js.map +1 -1
- package/dist/esm/Providers/AWSAppSyncEventsProvider/index.mjs +15 -14
- package/dist/esm/Providers/AWSAppSyncEventsProvider/index.mjs.map +1 -1
- package/dist/esm/Providers/AWSWebSocketProvider/index.d.ts +3 -5
- package/dist/esm/Providers/AWSWebSocketProvider/index.mjs +56 -50
- package/dist/esm/Providers/AWSWebSocketProvider/index.mjs.map +1 -1
- package/dist/esm/Providers/constants.d.ts +0 -4
- package/dist/esm/Providers/constants.mjs +1 -5
- package/dist/esm/Providers/constants.mjs.map +1 -1
- package/dist/esm/internals/events/index.mjs +11 -1
- package/dist/esm/internals/events/index.mjs.map +1 -1
- package/dist/esm/internals/events/types.d.ts +10 -0
- package/package.json +4 -4
- package/src/Providers/AWSAppSyncEventsProvider/index.ts +15 -13
- package/src/Providers/AWSWebSocketProvider/index.ts +63 -64
- package/src/Providers/constants.ts +0 -5
- package/src/internals/events/index.ts +2 -2
- package/src/internals/events/types.ts +10 -0
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
CONNECTION_INIT_TIMEOUT,
|
|
24
24
|
CONNECTION_STATE_CHANGE,
|
|
25
25
|
DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT,
|
|
26
|
-
|
|
26
|
+
DEFAULT_KEEP_ALIVE_TIMEOUT,
|
|
27
27
|
MAX_DELAY_MS,
|
|
28
28
|
MESSAGE_TYPES,
|
|
29
29
|
NON_RETRYABLE_CODES,
|
|
@@ -83,8 +83,9 @@ export abstract class AWSWebSocketProvider {
|
|
|
83
83
|
|
|
84
84
|
protected awsRealTimeSocket?: WebSocket;
|
|
85
85
|
private socketStatus: SOCKET_STATUS = SOCKET_STATUS.CLOSED;
|
|
86
|
-
private
|
|
87
|
-
private
|
|
86
|
+
private keepAliveTimeoutId?: ReturnType<typeof setTimeout>;
|
|
87
|
+
private keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
|
88
|
+
private keepAliveAlertTimeoutId?: ReturnType<typeof setTimeout>;
|
|
88
89
|
private promiseArray: { res(): void; rej(reason?: any): void }[] = [];
|
|
89
90
|
private connectionState: ConnectionState | undefined;
|
|
90
91
|
private readonly connectionStateMonitor = new ConnectionStateMonitor();
|
|
@@ -118,7 +119,6 @@ export abstract class AWSWebSocketProvider {
|
|
|
118
119
|
return new Promise<void>((resolve, reject) => {
|
|
119
120
|
if (this.awsRealTimeSocket) {
|
|
120
121
|
this.awsRealTimeSocket.onclose = (_: CloseEvent) => {
|
|
121
|
-
this._closeSocket();
|
|
122
122
|
this.subscriptionObserverMap = new Map();
|
|
123
123
|
this.awsRealTimeSocket = undefined;
|
|
124
124
|
resolve();
|
|
@@ -171,7 +171,7 @@ export abstract class AWSWebSocketProvider {
|
|
|
171
171
|
this.logger.debug(
|
|
172
172
|
`${CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR}: ${err}`,
|
|
173
173
|
);
|
|
174
|
-
this.
|
|
174
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
175
175
|
})
|
|
176
176
|
.finally(() => {
|
|
177
177
|
subscriptionStartInProgress = false;
|
|
@@ -255,6 +255,11 @@ export abstract class AWSWebSocketProvider {
|
|
|
255
255
|
|
|
256
256
|
return new Promise((resolve, reject) => {
|
|
257
257
|
if (this.awsRealTimeSocket) {
|
|
258
|
+
const timeoutId = setTimeout(() => {
|
|
259
|
+
cleanup();
|
|
260
|
+
reject(new Error('Publish operation timed out'));
|
|
261
|
+
}, 30000); // 30 seconds timeout
|
|
262
|
+
|
|
258
263
|
const publishListener = (event: MessageEvent) => {
|
|
259
264
|
const data = JSON.parse(event.data);
|
|
260
265
|
if (data.id === subscriptionId && data.type === 'publish_success') {
|
|
@@ -263,22 +268,47 @@ export abstract class AWSWebSocketProvider {
|
|
|
263
268
|
'message',
|
|
264
269
|
publishListener,
|
|
265
270
|
);
|
|
266
|
-
|
|
271
|
+
cleanup();
|
|
267
272
|
resolve();
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
if (data.erroredEvents && data.erroredEvents.length > 0) {
|
|
271
276
|
// TODO: handle errors
|
|
277
|
+
const errors = data.erroredEvents.map(
|
|
278
|
+
(errorEvent: any) => errorEvent.error,
|
|
279
|
+
);
|
|
280
|
+
cleanup();
|
|
281
|
+
reject(new Error(`Publish errors: ${errors.join(', ')}`));
|
|
272
282
|
}
|
|
273
283
|
};
|
|
274
|
-
|
|
275
|
-
|
|
284
|
+
|
|
285
|
+
const errorListener = (error: Event) => {
|
|
286
|
+
cleanup();
|
|
287
|
+
reject(new Error(`WebSocket error: ${error}`));
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const closeListener = () => {
|
|
291
|
+
cleanup();
|
|
276
292
|
reject(new Error('WebSocket is closed'));
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const cleanup = () => {
|
|
296
|
+
clearTimeout(timeoutId);
|
|
297
|
+
this.awsRealTimeSocket?.removeEventListener(
|
|
298
|
+
'message',
|
|
299
|
+
publishListener,
|
|
300
|
+
);
|
|
301
|
+
this.awsRealTimeSocket?.removeEventListener('error', errorListener);
|
|
302
|
+
this.awsRealTimeSocket?.removeEventListener('close', closeListener);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
this.awsRealTimeSocket.addEventListener('message', publishListener);
|
|
306
|
+
this.awsRealTimeSocket.addEventListener('error', errorListener);
|
|
307
|
+
this.awsRealTimeSocket.addEventListener('close', closeListener);
|
|
280
308
|
|
|
281
309
|
this.awsRealTimeSocket.send(serializedSubscriptionMessage);
|
|
310
|
+
} else {
|
|
311
|
+
reject(new Error('WebSocket is not connected'));
|
|
282
312
|
}
|
|
283
313
|
});
|
|
284
314
|
}
|
|
@@ -435,7 +465,7 @@ export abstract class AWSWebSocketProvider {
|
|
|
435
465
|
this.logger.debug({ err });
|
|
436
466
|
const message = String(err.message ?? '');
|
|
437
467
|
// Resolving to give the state observer time to propogate the update
|
|
438
|
-
this.
|
|
468
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
439
469
|
|
|
440
470
|
// Capture the error only when the network didn't cause disruption
|
|
441
471
|
if (
|
|
@@ -544,7 +574,12 @@ export abstract class AWSWebSocketProvider {
|
|
|
544
574
|
setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
|
545
575
|
} else {
|
|
546
576
|
this.logger.debug('closing WebSocket...');
|
|
547
|
-
|
|
577
|
+
if (this.keepAliveTimeoutId) {
|
|
578
|
+
clearTimeout(this.keepAliveTimeoutId);
|
|
579
|
+
}
|
|
580
|
+
if (this.keepAliveAlertTimeoutId) {
|
|
581
|
+
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
582
|
+
}
|
|
548
583
|
const tempSocket = this.awsRealTimeSocket;
|
|
549
584
|
// Cleaning callbacks to avoid race condition, socket still exists
|
|
550
585
|
tempSocket.onclose = null;
|
|
@@ -552,7 +587,7 @@ export abstract class AWSWebSocketProvider {
|
|
|
552
587
|
tempSocket.close(1000);
|
|
553
588
|
this.awsRealTimeSocket = undefined;
|
|
554
589
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
555
|
-
this.
|
|
590
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
556
591
|
}
|
|
557
592
|
}
|
|
558
593
|
|
|
@@ -572,43 +607,13 @@ export abstract class AWSWebSocketProvider {
|
|
|
572
607
|
errorType: string;
|
|
573
608
|
};
|
|
574
609
|
|
|
575
|
-
private maintainKeepAlive() {
|
|
576
|
-
this.kaTimestamp = Date.now();
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
private keepAliveHeartbeat(connectionTimeoutMs: number) {
|
|
580
|
-
const currentTime = Date.now();
|
|
581
|
-
|
|
582
|
-
// Check for missed KA message
|
|
583
|
-
if (
|
|
584
|
-
this.kaTimestamp &&
|
|
585
|
-
currentTime - this.kaTimestamp > DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT
|
|
586
|
-
) {
|
|
587
|
-
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
|
|
588
|
-
} else {
|
|
589
|
-
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Recognize we are disconnected if we haven't seen messages in the keep alive timeout period
|
|
593
|
-
if (
|
|
594
|
-
this.kaTimestamp &&
|
|
595
|
-
currentTime - this.kaTimestamp > connectionTimeoutMs
|
|
596
|
-
) {
|
|
597
|
-
this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
610
|
private _handleIncomingSubscriptionMessage(message: MessageEvent) {
|
|
602
611
|
if (typeof message.data !== 'string') {
|
|
603
612
|
return;
|
|
604
613
|
}
|
|
605
614
|
|
|
606
615
|
const [isData, data] = this._handleSubscriptionData(message);
|
|
607
|
-
if (isData)
|
|
608
|
-
this.maintainKeepAlive();
|
|
609
|
-
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
616
|
+
if (isData) return;
|
|
612
617
|
|
|
613
618
|
const { type, id, payload } = data;
|
|
614
619
|
|
|
@@ -657,7 +662,16 @@ export abstract class AWSWebSocketProvider {
|
|
|
657
662
|
}
|
|
658
663
|
|
|
659
664
|
if (type === MESSAGE_TYPES.GQL_CONNECTION_KEEP_ALIVE) {
|
|
660
|
-
this.
|
|
665
|
+
if (this.keepAliveTimeoutId) clearTimeout(this.keepAliveTimeoutId);
|
|
666
|
+
if (this.keepAliveAlertTimeoutId)
|
|
667
|
+
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
668
|
+
this.keepAliveTimeoutId = setTimeout(() => {
|
|
669
|
+
this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT);
|
|
670
|
+
}, this.keepAliveTimeout);
|
|
671
|
+
this.keepAliveAlertTimeoutId = setTimeout(() => {
|
|
672
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
|
|
673
|
+
}, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT);
|
|
674
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
|
|
661
675
|
|
|
662
676
|
return;
|
|
663
677
|
}
|
|
@@ -702,21 +716,13 @@ export abstract class AWSWebSocketProvider {
|
|
|
702
716
|
this.logger.debug(`Disconnect error: ${msg}`);
|
|
703
717
|
|
|
704
718
|
if (this.awsRealTimeSocket) {
|
|
705
|
-
this.
|
|
719
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
706
720
|
this.awsRealTimeSocket.close();
|
|
707
721
|
}
|
|
708
722
|
|
|
709
723
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
710
724
|
}
|
|
711
725
|
|
|
712
|
-
private _closeSocket() {
|
|
713
|
-
if (this.keepAliveHeartbeatIntervalId) {
|
|
714
|
-
clearInterval(this.keepAliveHeartbeatIntervalId);
|
|
715
|
-
this.keepAliveHeartbeatIntervalId = undefined;
|
|
716
|
-
}
|
|
717
|
-
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
726
|
private _timeoutStartSubscriptionAck(subscriptionId: string) {
|
|
721
727
|
const subscriptionObserver =
|
|
722
728
|
this.subscriptionObserverMap.get(subscriptionId);
|
|
@@ -732,7 +738,7 @@ export abstract class AWSWebSocketProvider {
|
|
|
732
738
|
subscriptionState: SUBSCRIPTION_STATUS.FAILED,
|
|
733
739
|
});
|
|
734
740
|
|
|
735
|
-
this.
|
|
741
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
736
742
|
this.logger.debug(
|
|
737
743
|
'timeoutStartSubscription',
|
|
738
744
|
JSON.stringify({ query, variables }),
|
|
@@ -844,7 +850,6 @@ export abstract class AWSWebSocketProvider {
|
|
|
844
850
|
this.logger.debug(`WebSocket connection error`);
|
|
845
851
|
};
|
|
846
852
|
newSocket.onclose = () => {
|
|
847
|
-
this._closeSocket();
|
|
848
853
|
reject(new Error('Connection handshake error'));
|
|
849
854
|
};
|
|
850
855
|
newSocket.onopen = () => {
|
|
@@ -874,7 +879,6 @@ export abstract class AWSWebSocketProvider {
|
|
|
874
879
|
|
|
875
880
|
this.awsRealTimeSocket.onclose = event => {
|
|
876
881
|
this.logger.debug(`WebSocket closed ${event.reason}`);
|
|
877
|
-
this._closeSocket();
|
|
878
882
|
reject(new Error(JSON.stringify(event)));
|
|
879
883
|
};
|
|
880
884
|
|
|
@@ -938,11 +942,7 @@ export abstract class AWSWebSocketProvider {
|
|
|
938
942
|
return;
|
|
939
943
|
}
|
|
940
944
|
|
|
941
|
-
|
|
942
|
-
this.keepAliveHeartbeatIntervalId = setInterval(() => {
|
|
943
|
-
this.keepAliveHeartbeat(connectionTimeoutMs);
|
|
944
|
-
}, DEFAULT_KEEP_ALIVE_HEARTBEAT_TIMEOUT);
|
|
945
|
-
|
|
945
|
+
this.keepAliveTimeout = connectionTimeoutMs;
|
|
946
946
|
this.awsRealTimeSocket.onmessage =
|
|
947
947
|
this._handleIncomingSubscriptionMessage.bind(this);
|
|
948
948
|
|
|
@@ -953,7 +953,6 @@ export abstract class AWSWebSocketProvider {
|
|
|
953
953
|
|
|
954
954
|
this.awsRealTimeSocket.onclose = event => {
|
|
955
955
|
this.logger.debug(`WebSocket closed ${event.reason}`);
|
|
956
|
-
this._closeSocket();
|
|
957
956
|
this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
958
957
|
};
|
|
959
958
|
}
|
|
@@ -128,11 +128,6 @@ export const START_ACK_TIMEOUT = 15000;
|
|
|
128
128
|
*/
|
|
129
129
|
export const DEFAULT_KEEP_ALIVE_TIMEOUT = 5 * 60 * 1000;
|
|
130
130
|
|
|
131
|
-
/**
|
|
132
|
-
* Default Time in milleseconds between monitoring checks of keep alive status
|
|
133
|
-
*/
|
|
134
|
-
export const DEFAULT_KEEP_ALIVE_HEARTBEAT_TIMEOUT = 5 * 1000;
|
|
135
|
-
|
|
136
131
|
/**
|
|
137
132
|
* Default Time in milleseconds to alert for missed GQL_CONNECTION_KEEP_ALIVE message
|
|
138
133
|
*/
|
|
@@ -70,7 +70,7 @@ async function connect(
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
// WS publish is not enabled in the service yet. It will be a follow up feature
|
|
73
|
-
const
|
|
73
|
+
const pub = async (
|
|
74
74
|
event: DocumentType,
|
|
75
75
|
pubOptions?: EventsOptions,
|
|
76
76
|
): Promise<any> => {
|
|
@@ -94,7 +94,7 @@ async function connect(
|
|
|
94
94
|
return {
|
|
95
95
|
subscribe: sub,
|
|
96
96
|
close,
|
|
97
|
-
|
|
97
|
+
publish: pub,
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { Subscription } from 'rxjs';
|
|
5
5
|
import type { GraphQLAuthMode } from '@aws-amplify/core/internals/utils';
|
|
6
|
+
import { DocumentType } from '@aws-amplify/core/internals/utils';
|
|
6
7
|
|
|
7
8
|
export interface SubscriptionObserver<T> {
|
|
8
9
|
next(value: T): void;
|
|
@@ -51,6 +52,15 @@ export interface EventsChannel {
|
|
|
51
52
|
*
|
|
52
53
|
*/
|
|
53
54
|
close(): void;
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* @param event
|
|
58
|
+
* @param pubOptions
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* await channel.publish({ some: "data" });
|
|
62
|
+
*/
|
|
63
|
+
publish(event: DocumentType, pubOptions?: EventsOptions): Promise<any>;
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
export type ResolvedGraphQLAuthModes = Exclude<GraphQLAuthMode, 'identityPool'>;
|