@aws-amplify/api-graphql 4.4.1 → 4.4.2-events.538dd9f.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 +98 -0
- package/dist/cjs/Providers/AWSAppSyncEventsProvider/index.js.map +1 -0
- package/dist/cjs/Providers/AWSAppSyncRealTimeProvider/index.js +34 -731
- package/dist/cjs/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -1
- package/dist/cjs/Providers/AWSWebSocketProvider/appsyncUrl.js +112 -0
- package/dist/cjs/Providers/AWSWebSocketProvider/appsyncUrl.js.map +1 -0
- package/dist/cjs/Providers/AWSWebSocketProvider/authHeaders.js +99 -0
- package/dist/cjs/Providers/AWSWebSocketProvider/authHeaders.js.map +1 -0
- package/dist/cjs/Providers/AWSWebSocketProvider/index.js +624 -0
- package/dist/cjs/Providers/AWSWebSocketProvider/index.js.map +1 -0
- package/dist/cjs/Providers/constants.js +32 -2
- package/dist/cjs/Providers/constants.js.map +1 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internals/InternalGraphQLAPI.js +8 -67
- package/dist/cjs/internals/InternalGraphQLAPI.js.map +1 -1
- package/dist/cjs/internals/events/appsyncRequest.js +97 -0
- package/dist/cjs/internals/events/appsyncRequest.js.map +1 -0
- package/dist/cjs/internals/events/index.js +73 -0
- package/dist/cjs/internals/events/index.js.map +1 -0
- package/dist/cjs/internals/events/types.js +6 -0
- package/dist/cjs/internals/events/types.js.map +1 -0
- package/dist/cjs/internals/events/utils.js +57 -0
- package/dist/cjs/internals/events/utils.js.map +1 -0
- package/dist/cjs/internals/graphqlAuth.js +64 -0
- package/dist/cjs/internals/graphqlAuth.js.map +1 -0
- package/dist/cjs/internals/graphqlRequest.js +20 -0
- package/dist/cjs/internals/graphqlRequest.js.map +1 -0
- package/dist/cjs/utils/ReconnectionMonitor.js +1 -1
- package/dist/cjs/utils/ReconnectionMonitor.js.map +1 -1
- package/dist/esm/Providers/AWSAppSyncEventsProvider/index.d.ts +43 -0
- package/dist/esm/Providers/AWSAppSyncEventsProvider/index.mjs +97 -0
- package/dist/esm/Providers/AWSAppSyncEventsProvider/index.mjs.map +1 -0
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.d.ts +24 -71
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.mjs +34 -731
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.mjs.map +1 -1
- package/dist/esm/Providers/AWSWebSocketProvider/appsyncUrl.d.ts +21 -0
- package/dist/esm/Providers/AWSWebSocketProvider/appsyncUrl.mjs +106 -0
- package/dist/esm/Providers/AWSWebSocketProvider/appsyncUrl.mjs.map +1 -0
- package/dist/esm/Providers/AWSWebSocketProvider/authHeaders.d.ts +8 -0
- package/dist/esm/Providers/AWSWebSocketProvider/authHeaders.mjs +97 -0
- package/dist/esm/Providers/AWSWebSocketProvider/authHeaders.mjs.map +1 -0
- package/dist/esm/Providers/AWSWebSocketProvider/index.d.ts +89 -0
- package/dist/esm/Providers/AWSWebSocketProvider/index.mjs +622 -0
- package/dist/esm/Providers/AWSWebSocketProvider/index.mjs.map +1 -0
- package/dist/esm/Providers/constants.d.ts +33 -3
- package/dist/esm/Providers/constants.mjs +32 -2
- package/dist/esm/Providers/constants.mjs.map +1 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/internals/InternalGraphQLAPI.d.ts +0 -1
- package/dist/esm/internals/InternalGraphQLAPI.mjs +9 -68
- package/dist/esm/internals/InternalGraphQLAPI.mjs.map +1 -1
- package/dist/esm/internals/events/appsyncRequest.d.ts +14 -0
- package/dist/esm/internals/events/appsyncRequest.mjs +96 -0
- package/dist/esm/internals/events/appsyncRequest.mjs.map +1 -0
- package/dist/esm/internals/events/index.d.ts +17 -0
- package/dist/esm/internals/events/index.mjs +69 -0
- package/dist/esm/internals/events/index.mjs.map +1 -0
- package/dist/esm/internals/events/types.d.ts +23 -0
- package/dist/esm/internals/events/types.mjs +2 -0
- package/dist/esm/internals/events/types.mjs.map +1 -0
- package/dist/esm/internals/events/utils.d.ts +16 -0
- package/dist/esm/internals/events/utils.mjs +53 -0
- package/dist/esm/internals/events/utils.mjs.map +1 -0
- package/dist/esm/internals/graphqlAuth.d.ts +3 -0
- package/dist/esm/internals/graphqlAuth.mjs +64 -0
- package/dist/esm/internals/graphqlAuth.mjs.map +1 -0
- package/dist/esm/internals/graphqlRequest.d.ts +3 -0
- package/dist/esm/internals/graphqlRequest.mjs +18 -0
- package/dist/esm/internals/graphqlRequest.mjs.map +1 -0
- package/dist/esm/utils/ConnectionStateMonitor.d.ts +29 -1
- package/dist/esm/utils/ReconnectionMonitor.d.ts +1 -1
- package/dist/esm/utils/ReconnectionMonitor.mjs +1 -1
- package/dist/esm/utils/ReconnectionMonitor.mjs.map +1 -1
- package/package.json +4 -4
- package/src/Providers/AWSAppSyncEventsProvider/index.ts +192 -0
- package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +56 -1049
- package/src/Providers/AWSWebSocketProvider/appsyncUrl.ts +151 -0
- package/src/Providers/AWSWebSocketProvider/authHeaders.ts +146 -0
- package/src/Providers/AWSWebSocketProvider/index.ts +932 -0
- package/src/Providers/constants.ts +32 -2
- package/src/index.ts +5 -0
- package/src/internals/InternalGraphQLAPI.ts +15 -102
- package/src/internals/events/appsyncRequest.ts +169 -0
- package/src/internals/events/index.ts +138 -0
- package/src/internals/events/types.ts +35 -0
- package/src/internals/events/utils.ts +77 -0
- package/src/internals/graphqlAuth.ts +87 -0
- package/src/internals/graphqlRequest.ts +30 -0
- package/src/utils/ConnectionStateMonitor.ts +2 -13
- package/src/utils/ReconnectionMonitor.ts +1 -1
|
@@ -36,9 +36,9 @@ export enum MESSAGE_TYPES {
|
|
|
36
36
|
GQL_START_ACK = 'start_ack',
|
|
37
37
|
/**
|
|
38
38
|
* Server -> Client message.
|
|
39
|
-
* This message type is for subscription message from AWS AppSync RealTime
|
|
39
|
+
* This message type is for subscription message from AWS AppSync RealTime or Events
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
DATA = 'data',
|
|
42
42
|
/**
|
|
43
43
|
* Server -> Client message.
|
|
44
44
|
* This message type helps the client to know is still receiving messages from AWS AppSync RealTime
|
|
@@ -59,6 +59,36 @@ export enum MESSAGE_TYPES {
|
|
|
59
59
|
* This message type is for sending error messages from AWS AppSync RealTime to the client
|
|
60
60
|
*/
|
|
61
61
|
GQL_ERROR = 'error', // Server -> Client
|
|
62
|
+
/**
|
|
63
|
+
* Client -> Server message.
|
|
64
|
+
* This message type is for registering subscriptions with Events
|
|
65
|
+
*/
|
|
66
|
+
EVENT_SUBSCRIBE = 'subscribe',
|
|
67
|
+
/**
|
|
68
|
+
* Client -> Server message.
|
|
69
|
+
* This message type is for publishing a message with Events
|
|
70
|
+
*/
|
|
71
|
+
EVENT_PUBLISH = 'publish',
|
|
72
|
+
/**
|
|
73
|
+
* Server -> Client message.
|
|
74
|
+
* Server acknowledges successful subscription
|
|
75
|
+
*/
|
|
76
|
+
EVENT_SUBSCRIBE_ACK = 'subscribe_success',
|
|
77
|
+
/**
|
|
78
|
+
* Server -> Client message.
|
|
79
|
+
* Server acknowledges successful publish
|
|
80
|
+
*/
|
|
81
|
+
EVENT_PUBLISH_ACK = 'publish_success',
|
|
82
|
+
/**
|
|
83
|
+
* Client -> Server message.
|
|
84
|
+
* This message type is for unregister subscriptions with AWS AppSync RealTime
|
|
85
|
+
*/
|
|
86
|
+
EVENT_STOP = 'unsubscribe',
|
|
87
|
+
/**
|
|
88
|
+
* Server -> Client message.
|
|
89
|
+
* This is the ack response from AWS AppSync Events to EVENT_STOP message
|
|
90
|
+
*/
|
|
91
|
+
EVENT_COMPLETE = 'unsubscribe_success',
|
|
62
92
|
}
|
|
63
93
|
|
|
64
94
|
export enum SUBSCRIPTION_STATUS {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import * as events from './internals/events';
|
|
5
|
+
|
|
6
|
+
export { events };
|
|
7
|
+
|
|
4
8
|
export { GraphQLAPI, GraphQLAPIClass, graphqlOperation } from './GraphQLAPI';
|
|
5
9
|
export * from './types';
|
|
6
10
|
|
|
7
11
|
export { CONNECTION_STATE_CHANGE } from './Providers/constants';
|
|
12
|
+
export * from './internals/events/types';
|
|
@@ -12,7 +12,6 @@ import { AmplifyClassV6 } from '@aws-amplify/core';
|
|
|
12
12
|
import {
|
|
13
13
|
AmplifyUrl,
|
|
14
14
|
CustomUserAgentDetails,
|
|
15
|
-
GraphQLAuthMode,
|
|
16
15
|
getAmplifyUserAgent,
|
|
17
16
|
} from '@aws-amplify/core/internals/utils';
|
|
18
17
|
import { isCancelError as isCancelErrorREST } from '@aws-amplify/api-rest';
|
|
@@ -30,17 +29,11 @@ import { AWSAppSyncRealTimeProvider } from '../Providers/AWSAppSyncRealTimeProvi
|
|
|
30
29
|
import { GraphQLOperation, GraphQLOptions, GraphQLResult } from '../types';
|
|
31
30
|
import { resolveConfig, resolveLibraryOptions } from '../utils';
|
|
32
31
|
import { repackageUnauthorizedError } from '../utils/errors/repackageAuthError';
|
|
33
|
-
import {
|
|
34
|
-
NO_API_KEY,
|
|
35
|
-
NO_AUTH_TOKEN_HEADER,
|
|
36
|
-
NO_ENDPOINT,
|
|
37
|
-
NO_SIGNED_IN_USER,
|
|
38
|
-
NO_VALID_AUTH_TOKEN,
|
|
39
|
-
NO_VALID_CREDENTIALS,
|
|
40
|
-
} from '../utils/errors/constants';
|
|
32
|
+
import { NO_ENDPOINT } from '../utils/errors/constants';
|
|
41
33
|
import { GraphQLApiError, createGraphQLResultWithError } from '../utils/errors';
|
|
42
34
|
|
|
43
35
|
import { isGraphQLResponseWithErrors } from './utils/runtimeTypeGuards/isGraphQLResponseWithErrors';
|
|
36
|
+
import { headerBasedAuth } from './graphqlAuth';
|
|
44
37
|
|
|
45
38
|
const USER_AGENT_HEADER = 'x-amz-user-agent';
|
|
46
39
|
|
|
@@ -72,80 +65,6 @@ export class InternalGraphQLAPIClass {
|
|
|
72
65
|
return 'InternalGraphQLAPI';
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
private async _headerBasedAuth(
|
|
76
|
-
amplify: AmplifyClassV6,
|
|
77
|
-
authMode: GraphQLAuthMode,
|
|
78
|
-
additionalHeaders: Record<string, string> = {},
|
|
79
|
-
) {
|
|
80
|
-
const { apiKey } = resolveConfig(amplify);
|
|
81
|
-
|
|
82
|
-
let headers = {};
|
|
83
|
-
|
|
84
|
-
switch (authMode) {
|
|
85
|
-
case 'apiKey':
|
|
86
|
-
if (!apiKey) {
|
|
87
|
-
throw new GraphQLApiError(NO_API_KEY);
|
|
88
|
-
}
|
|
89
|
-
headers = {
|
|
90
|
-
'X-Api-Key': apiKey,
|
|
91
|
-
};
|
|
92
|
-
break;
|
|
93
|
-
case 'iam': {
|
|
94
|
-
const session = await amplify.Auth.fetchAuthSession();
|
|
95
|
-
if (session.credentials === undefined) {
|
|
96
|
-
throw new GraphQLApiError(NO_VALID_CREDENTIALS);
|
|
97
|
-
}
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
case 'oidc':
|
|
101
|
-
case 'userPool': {
|
|
102
|
-
let token: string | undefined;
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
token = (
|
|
106
|
-
await amplify.Auth.fetchAuthSession()
|
|
107
|
-
).tokens?.accessToken.toString();
|
|
108
|
-
} catch (e) {
|
|
109
|
-
// fetchAuthSession failed
|
|
110
|
-
throw new GraphQLApiError({
|
|
111
|
-
...NO_SIGNED_IN_USER,
|
|
112
|
-
underlyingError: e,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// `fetchAuthSession()` succeeded but didn't return `tokens`.
|
|
117
|
-
// This may happen when unauthenticated access is enabled and there is
|
|
118
|
-
// no user signed in.
|
|
119
|
-
if (!token) {
|
|
120
|
-
throw new GraphQLApiError(NO_VALID_AUTH_TOKEN);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
headers = {
|
|
124
|
-
Authorization: token,
|
|
125
|
-
};
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
case 'lambda':
|
|
129
|
-
if (
|
|
130
|
-
typeof additionalHeaders === 'object' &&
|
|
131
|
-
!additionalHeaders.Authorization
|
|
132
|
-
) {
|
|
133
|
-
throw new GraphQLApiError(NO_AUTH_TOKEN_HEADER);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
headers = {
|
|
137
|
-
Authorization: additionalHeaders.Authorization,
|
|
138
|
-
};
|
|
139
|
-
break;
|
|
140
|
-
case 'none':
|
|
141
|
-
break;
|
|
142
|
-
default:
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return headers;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
68
|
/**
|
|
150
69
|
* to get the operation type
|
|
151
70
|
* @param operation
|
|
@@ -252,6 +171,7 @@ export class InternalGraphQLAPIClass {
|
|
|
252
171
|
authToken?: string,
|
|
253
172
|
): Promise<GraphQLResult<T>> {
|
|
254
173
|
const {
|
|
174
|
+
apiKey,
|
|
255
175
|
region,
|
|
256
176
|
endpoint: appSyncGraphqlEndpoint,
|
|
257
177
|
customEndpoint,
|
|
@@ -303,29 +223,22 @@ export class InternalGraphQLAPIClass {
|
|
|
303
223
|
};
|
|
304
224
|
}
|
|
305
225
|
|
|
306
|
-
|
|
226
|
+
const authHeaders = await headerBasedAuth(
|
|
227
|
+
amplify,
|
|
228
|
+
authMode,
|
|
229
|
+
apiKey,
|
|
230
|
+
additionalCustomHeaders,
|
|
231
|
+
);
|
|
232
|
+
|
|
307
233
|
const headers = {
|
|
308
|
-
...(!customEndpoint &&
|
|
309
|
-
(await this._headerBasedAuth(
|
|
310
|
-
amplify,
|
|
311
|
-
authMode!,
|
|
312
|
-
additionalCustomHeaders,
|
|
313
|
-
))),
|
|
234
|
+
...(!customEndpoint && authHeaders),
|
|
314
235
|
/**
|
|
315
236
|
* Custom endpoint headers.
|
|
316
237
|
* If there is both a custom endpoint and custom region present, we get the headers.
|
|
317
238
|
* If there is a custom endpoint but no region, we return an empty object.
|
|
318
239
|
* If neither are present, we return an empty object.
|
|
319
240
|
*/
|
|
320
|
-
...((customEndpoint &&
|
|
321
|
-
(customEndpointRegion
|
|
322
|
-
? await this._headerBasedAuth(
|
|
323
|
-
amplify,
|
|
324
|
-
authMode!,
|
|
325
|
-
additionalCustomHeaders,
|
|
326
|
-
)
|
|
327
|
-
: {})) ||
|
|
328
|
-
{}),
|
|
241
|
+
...((customEndpoint && (customEndpointRegion ? authHeaders : {})) || {}),
|
|
329
242
|
// Custom headers included in Amplify configuration options:
|
|
330
243
|
...(customHeaders &&
|
|
331
244
|
(await customHeaders({
|
|
@@ -378,9 +291,9 @@ export class InternalGraphQLAPIClass {
|
|
|
378
291
|
let response: any;
|
|
379
292
|
|
|
380
293
|
try {
|
|
381
|
-
// See the inline doc of the REST `post()` API for possible errors to be thrown.
|
|
382
|
-
// As these errors are catastrophic they should be caught and handled by GraphQL
|
|
383
|
-
// API consumers.
|
|
294
|
+
// // // See the inline doc of the REST `post()` API for possible errors to be thrown.
|
|
295
|
+
// // // As these errors are catastrophic they should be caught and handled by GraphQL
|
|
296
|
+
// // // API consumers.
|
|
384
297
|
const { body: responseBody } = await this._api.post(amplify, {
|
|
385
298
|
url: new AmplifyUrl(endpoint),
|
|
386
299
|
options: {
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AmplifyClassV6 } from '@aws-amplify/core';
|
|
4
|
+
import {
|
|
5
|
+
AmplifyUrl,
|
|
6
|
+
CustomUserAgentDetails,
|
|
7
|
+
GraphQLAuthMode,
|
|
8
|
+
getAmplifyUserAgent,
|
|
9
|
+
} from '@aws-amplify/core/internals/utils';
|
|
10
|
+
import { post } from '@aws-amplify/api-rest/internals';
|
|
11
|
+
import {
|
|
12
|
+
CustomHeaders,
|
|
13
|
+
RequestOptions,
|
|
14
|
+
} from '@aws-amplify/data-schema/runtime';
|
|
15
|
+
|
|
16
|
+
import { resolveLibraryOptions } from '../../utils';
|
|
17
|
+
import { repackageUnauthorizedError } from '../../utils/errors/repackageAuthError';
|
|
18
|
+
import { headerBasedAuth } from '../graphqlAuth';
|
|
19
|
+
import { isGraphQLResponseWithErrors } from '../utils/runtimeTypeGuards/isGraphQLResponseWithErrors';
|
|
20
|
+
|
|
21
|
+
const USER_AGENT_HEADER = 'x-amz-user-agent';
|
|
22
|
+
|
|
23
|
+
interface GqlRequestOptions {
|
|
24
|
+
apiKey?: string;
|
|
25
|
+
region?: string;
|
|
26
|
+
appSyncGraphqlEndpoint: string;
|
|
27
|
+
authenticationType: GraphQLAuthMode;
|
|
28
|
+
query: string;
|
|
29
|
+
variables: string[];
|
|
30
|
+
authToken?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// This is effectively a copy of InternalGraphQLAPI.ts._graphql(...)
|
|
34
|
+
// Our existing unit tests are tightly coupled to the implementation, so i was unable to refactor
|
|
35
|
+
// and extend _graphql() without having to change a bunch of tests as well... which in turn reduces confidence
|
|
36
|
+
// that this feature will _not affect_ GQL behavior.
|
|
37
|
+
export async function appsyncRequest<T = any>(
|
|
38
|
+
amplify: AmplifyClassV6,
|
|
39
|
+
options: GqlRequestOptions,
|
|
40
|
+
additionalHeaders: CustomHeaders = {},
|
|
41
|
+
abortController: AbortController,
|
|
42
|
+
customUserAgentDetails?: CustomUserAgentDetails,
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
const {
|
|
45
|
+
region,
|
|
46
|
+
appSyncGraphqlEndpoint: endpoint,
|
|
47
|
+
authenticationType: authMode,
|
|
48
|
+
query,
|
|
49
|
+
variables,
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
if (!endpoint) {
|
|
53
|
+
throw new Error('No endpoint');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { withCredentials } = resolveLibraryOptions(amplify);
|
|
57
|
+
|
|
58
|
+
const headers = await requestHeaders(
|
|
59
|
+
amplify,
|
|
60
|
+
options,
|
|
61
|
+
additionalHeaders,
|
|
62
|
+
customUserAgentDetails,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const body = {
|
|
66
|
+
channel: query as string,
|
|
67
|
+
events: variables,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const signingServiceInfo = ['apiKey', 'none'].includes(authMode)
|
|
71
|
+
? undefined
|
|
72
|
+
: {
|
|
73
|
+
service: 'appsync',
|
|
74
|
+
region,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const { body: responseBody } = await post(amplify, {
|
|
78
|
+
url: new AmplifyUrl(endpoint),
|
|
79
|
+
options: {
|
|
80
|
+
headers,
|
|
81
|
+
body,
|
|
82
|
+
signingServiceInfo,
|
|
83
|
+
withCredentials,
|
|
84
|
+
},
|
|
85
|
+
abortController,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const response = await responseBody.json();
|
|
89
|
+
|
|
90
|
+
if (isGraphQLResponseWithErrors(response)) {
|
|
91
|
+
throw repackageUnauthorizedError(response);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return response as T;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Computes all the necessary HTTP headers for the request based on:
|
|
99
|
+
* 1. Operation-level header options
|
|
100
|
+
* 2. Amplify.configure custom headers
|
|
101
|
+
* 3. AuthZ headers for explicit auth mode specified for operation ?? default auth mode in config
|
|
102
|
+
*
|
|
103
|
+
* @returns HTTP request headers key/value
|
|
104
|
+
*/
|
|
105
|
+
async function requestHeaders(
|
|
106
|
+
amplify: AmplifyClassV6,
|
|
107
|
+
options: GqlRequestOptions,
|
|
108
|
+
additionalHeaders: CustomHeaders,
|
|
109
|
+
customUserAgentDetails?: CustomUserAgentDetails,
|
|
110
|
+
) {
|
|
111
|
+
const {
|
|
112
|
+
apiKey,
|
|
113
|
+
appSyncGraphqlEndpoint: endpoint,
|
|
114
|
+
authenticationType: authMode,
|
|
115
|
+
query,
|
|
116
|
+
variables,
|
|
117
|
+
authToken,
|
|
118
|
+
} = options;
|
|
119
|
+
|
|
120
|
+
const { headers: customHeadersFn } = resolveLibraryOptions(amplify);
|
|
121
|
+
|
|
122
|
+
let additionalCustomHeaders: Record<string, string>;
|
|
123
|
+
|
|
124
|
+
if (typeof additionalHeaders === 'function') {
|
|
125
|
+
const requestOptions: RequestOptions = {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
url: new AmplifyUrl(endpoint).toString(),
|
|
128
|
+
queryString: query,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
additionalCustomHeaders = await additionalHeaders(requestOptions);
|
|
132
|
+
} else {
|
|
133
|
+
additionalCustomHeaders = additionalHeaders;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// if an authorization header is set, have the operation-level authToken take precedence
|
|
137
|
+
if (authToken) {
|
|
138
|
+
additionalCustomHeaders = {
|
|
139
|
+
...additionalCustomHeaders,
|
|
140
|
+
Authorization: authToken,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const authHeaders = await headerBasedAuth(
|
|
145
|
+
amplify,
|
|
146
|
+
authMode,
|
|
147
|
+
apiKey,
|
|
148
|
+
additionalCustomHeaders,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const customHeaders =
|
|
152
|
+
customHeadersFn &&
|
|
153
|
+
(await customHeadersFn({
|
|
154
|
+
query,
|
|
155
|
+
variables: variables as any,
|
|
156
|
+
}));
|
|
157
|
+
|
|
158
|
+
const headers = {
|
|
159
|
+
...authHeaders,
|
|
160
|
+
// Custom headers included in Amplify configuration options:
|
|
161
|
+
...customHeaders,
|
|
162
|
+
// Custom headers from individual requests or API client configuration:
|
|
163
|
+
...additionalCustomHeaders,
|
|
164
|
+
// User agent headers:
|
|
165
|
+
[USER_AGENT_HEADER]: getAmplifyUserAgent(customUserAgentDetails),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return headers;
|
|
169
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { Subscription } from 'rxjs';
|
|
5
|
+
import { Amplify } from '@aws-amplify/core';
|
|
6
|
+
import { DocumentType } from '@aws-amplify/core/internals/utils';
|
|
7
|
+
|
|
8
|
+
import { AppSyncEventProvider as eventProvider } from '../../Providers/AWSAppSyncEventsProvider';
|
|
9
|
+
|
|
10
|
+
import { appsyncRequest } from './appsyncRequest';
|
|
11
|
+
import { configure, normalizeAuth, serializeEvents } from './utils';
|
|
12
|
+
import type {
|
|
13
|
+
EventsChannel,
|
|
14
|
+
EventsOptions,
|
|
15
|
+
PublishResponse,
|
|
16
|
+
PublishedEvent,
|
|
17
|
+
SubscriptionObserver,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Establish a WebSocket connection to an Events channel
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
async function connect(
|
|
25
|
+
channelName: string,
|
|
26
|
+
options?: EventsOptions,
|
|
27
|
+
): Promise<EventsChannel> {
|
|
28
|
+
const providerOptions = configure();
|
|
29
|
+
|
|
30
|
+
providerOptions.authenticationType = normalizeAuth(
|
|
31
|
+
options?.authMode,
|
|
32
|
+
providerOptions.authenticationType,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
await eventProvider.connect(providerOptions);
|
|
36
|
+
|
|
37
|
+
let _subscription: Subscription;
|
|
38
|
+
|
|
39
|
+
const sub = (
|
|
40
|
+
observer: SubscriptionObserver<any>,
|
|
41
|
+
subOptions?: EventsOptions,
|
|
42
|
+
): Subscription => {
|
|
43
|
+
const subscribeOptions = { ...providerOptions, query: channelName };
|
|
44
|
+
subscribeOptions.authenticationType = normalizeAuth(
|
|
45
|
+
subOptions?.authMode,
|
|
46
|
+
subscribeOptions.authenticationType,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
_subscription = eventProvider
|
|
50
|
+
.subscribe(subscribeOptions)
|
|
51
|
+
.subscribe(observer);
|
|
52
|
+
|
|
53
|
+
return _subscription;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// WS publish is not enabled in the service yet. It will be a follow up feature
|
|
57
|
+
const _pub = async (
|
|
58
|
+
event: DocumentType,
|
|
59
|
+
pubOptions?: EventsOptions,
|
|
60
|
+
): Promise<any> => {
|
|
61
|
+
const publishOptions = {
|
|
62
|
+
...providerOptions,
|
|
63
|
+
query: channelName,
|
|
64
|
+
variables: event,
|
|
65
|
+
};
|
|
66
|
+
publishOptions.authenticationType = normalizeAuth(
|
|
67
|
+
pubOptions?.authMode,
|
|
68
|
+
publishOptions.authenticationType,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return eventProvider.publish(publishOptions);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const close = () => {
|
|
75
|
+
_subscription && _subscription.unsubscribe();
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
/**
|
|
80
|
+
* Subscribe to incoming events
|
|
81
|
+
*/
|
|
82
|
+
subscribe: sub,
|
|
83
|
+
/**
|
|
84
|
+
* Close channel and any active subscriptions
|
|
85
|
+
*/
|
|
86
|
+
close,
|
|
87
|
+
// publish: pub,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Publish events to a channel via REST request
|
|
93
|
+
*
|
|
94
|
+
* @param channelName - publish destination
|
|
95
|
+
* @param event - JSON-serializable value or an array of values
|
|
96
|
+
* @param options
|
|
97
|
+
*/
|
|
98
|
+
async function post(
|
|
99
|
+
channelName: string,
|
|
100
|
+
event: DocumentType | DocumentType[],
|
|
101
|
+
options?: EventsOptions,
|
|
102
|
+
): Promise<void | PublishedEvent[]> {
|
|
103
|
+
const providerOptions = configure();
|
|
104
|
+
providerOptions.authenticationType = normalizeAuth(
|
|
105
|
+
options?.authMode,
|
|
106
|
+
providerOptions.authenticationType,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// trailing slash required in publish
|
|
110
|
+
const normalizedChannelName =
|
|
111
|
+
channelName[0] === '/' ? channelName : `/${channelName}`;
|
|
112
|
+
|
|
113
|
+
const publishOptions = {
|
|
114
|
+
...providerOptions,
|
|
115
|
+
query: normalizedChannelName,
|
|
116
|
+
variables: serializeEvents(event),
|
|
117
|
+
authToken: options?.authToken,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const abortController = new AbortController();
|
|
121
|
+
|
|
122
|
+
const res = await appsyncRequest<PublishResponse>(
|
|
123
|
+
Amplify,
|
|
124
|
+
publishOptions,
|
|
125
|
+
{},
|
|
126
|
+
abortController,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
if (res.failed?.length > 0) {
|
|
130
|
+
return res.failed;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function closeAll(): void {
|
|
135
|
+
eventProvider.close();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { connect, post, closeAll };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { Subscription } from 'rxjs';
|
|
5
|
+
import type { GraphQLAuthMode } from '@aws-amplify/core/internals/utils';
|
|
6
|
+
|
|
7
|
+
export interface SubscriptionObserver<T> {
|
|
8
|
+
next(value: T): void;
|
|
9
|
+
error(errorValue: any): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface EventsChannel {
|
|
13
|
+
subscribe(
|
|
14
|
+
observer: SubscriptionObserver<any>,
|
|
15
|
+
subOptions?: EventsOptions,
|
|
16
|
+
): Subscription;
|
|
17
|
+
close(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ResolvedGraphQLAuthModes = Exclude<GraphQLAuthMode, 'identityPool'>;
|
|
21
|
+
|
|
22
|
+
export interface EventsOptions {
|
|
23
|
+
authMode?: GraphQLAuthMode;
|
|
24
|
+
authToken?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PublishedEvent {
|
|
28
|
+
identifier: string;
|
|
29
|
+
index: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PublishResponse {
|
|
33
|
+
failed: PublishedEvent[];
|
|
34
|
+
successful: PublishedEvent[];
|
|
35
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Amplify } from '@aws-amplify/core';
|
|
4
|
+
import {
|
|
5
|
+
DocumentType,
|
|
6
|
+
GraphQLAuthMode,
|
|
7
|
+
} from '@aws-amplify/core/internals/utils';
|
|
8
|
+
|
|
9
|
+
import type { ResolvedGraphQLAuthModes } from './types';
|
|
10
|
+
|
|
11
|
+
export const normalizeAuth = (
|
|
12
|
+
explicitAuthMode: GraphQLAuthMode | undefined,
|
|
13
|
+
defaultAuthMode: ResolvedGraphQLAuthModes,
|
|
14
|
+
): ResolvedGraphQLAuthModes => {
|
|
15
|
+
if (!explicitAuthMode) {
|
|
16
|
+
return defaultAuthMode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (explicitAuthMode === 'identityPool') {
|
|
20
|
+
return 'iam';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return explicitAuthMode;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const configure = () => {
|
|
27
|
+
const config = Amplify.getConfig();
|
|
28
|
+
|
|
29
|
+
const eventsConfig = config.API?.Events;
|
|
30
|
+
|
|
31
|
+
if (!eventsConfig) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'Amplify configuration is missing. Have you called Amplify.configure()?',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const configAuthMode = normalizeAuth(eventsConfig.defaultAuthMode, 'apiKey');
|
|
38
|
+
|
|
39
|
+
const options = {
|
|
40
|
+
appSyncGraphqlEndpoint: eventsConfig.endpoint,
|
|
41
|
+
region: eventsConfig.region,
|
|
42
|
+
authenticationType: configAuthMode,
|
|
43
|
+
apiKey: eventsConfig.apiKey,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return options;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Event API expects and array of JSON strings
|
|
51
|
+
*
|
|
52
|
+
* @param events - JSON-serializable value or an array of values
|
|
53
|
+
* @returns array of JSON strings
|
|
54
|
+
*/
|
|
55
|
+
export const serializeEvents = (
|
|
56
|
+
events: DocumentType | DocumentType[],
|
|
57
|
+
): string[] => {
|
|
58
|
+
if (Array.isArray(events)) {
|
|
59
|
+
return events.map((ev, idx) => {
|
|
60
|
+
const eventJson = JSON.stringify(ev);
|
|
61
|
+
if (eventJson === undefined) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Event must be a valid JSON value. Received ${ev} at index ${idx}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return eventJson;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const eventJson = JSON.stringify(events);
|
|
72
|
+
if (eventJson === undefined) {
|
|
73
|
+
throw new Error(`Event must be a valid JSON value. Received ${events}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return [eventJson];
|
|
77
|
+
};
|