@aws-amplify/api-graphql 4.0.1-console-preview.b5a42af.0 → 4.0.1-console-preview.9607e4a.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.
@@ -11,6 +11,7 @@ import {
11
11
  import { Observable } from 'rxjs';
12
12
  import { Amplify, Cache, fetchAuthSession } from '@aws-amplify/core';
13
13
  import {
14
+ APIAuthMode,
14
15
  CustomUserAgentDetails,
15
16
  ConsoleLogger as Logger,
16
17
  getAmplifyUserAgent,
@@ -21,7 +22,12 @@ import {
21
22
  GraphQLOperation,
22
23
  GraphQLOptions,
23
24
  } from '../types';
24
- import { post } from '@aws-amplify/api-rest/internals';
25
+ import { isCancelError as isCancelErrorREST } from '@aws-amplify/api-rest';
26
+ import {
27
+ post,
28
+ cancel as cancelREST,
29
+ updateRequestToBeCancellable,
30
+ } from '@aws-amplify/api-rest/internals';
25
31
  import { AWSAppSyncRealTimeProvider } from '../Providers/AWSAppSyncRealTimeProvider';
26
32
 
27
33
  const USER_AGENT_HEADER = 'x-amz-user-agent';
@@ -49,7 +55,7 @@ export class InternalGraphQLAPIClass {
49
55
  private appSyncRealTime: AWSAppSyncRealTimeProvider | null;
50
56
 
51
57
  Cache = Cache;
52
- private _api = { post };
58
+ private _api = { post, updateRequestToBeCancellable };
53
59
 
54
60
  /**
55
61
  * Initialize GraphQL API with AWS configuration
@@ -65,9 +71,8 @@ export class InternalGraphQLAPIClass {
65
71
  }
66
72
 
67
73
  private async _headerBasedAuth(
68
- defaultAuthenticationType?,
69
- additionalHeaders: { [key: string]: string } = {},
70
- customUserAgentDetails?: CustomUserAgentDetails
74
+ authMode: APIAuthMode,
75
+ additionalHeaders: { [key: string]: string } = {}
71
76
  ) {
72
77
  const config = Amplify.getConfig();
73
78
  const {
@@ -75,11 +80,12 @@ export class InternalGraphQLAPIClass {
75
80
  endpoint: appSyncGraphqlEndpoint,
76
81
  apiKey,
77
82
  defaultAuthMode,
78
- } = config.API.GraphQL;
83
+ } = config.API?.GraphQL || {};
79
84
 
85
+ const authenticationType = authMode || defaultAuthMode || 'iam';
80
86
  let headers = {};
81
87
 
82
- switch (defaultAuthMode) {
88
+ switch (authenticationType) {
83
89
  case 'apiKey':
84
90
  if (!apiKey) {
85
91
  throw new Error(GraphQLAuthError.NO_API_KEY);
@@ -177,11 +183,17 @@ export class InternalGraphQLAPIClass {
177
183
  switch (operationType) {
178
184
  case 'query':
179
185
  case 'mutation':
186
+ const abortController = new AbortController();
180
187
  const responsePromise = this._graphql<T>(
181
188
  { query, variables, authMode },
182
189
  headers,
190
+ abortController,
183
191
  customUserAgentDetails
184
192
  );
193
+ this._api.updateRequestToBeCancellable(
194
+ responsePromise,
195
+ abortController
196
+ );
185
197
  return responsePromise;
186
198
  case 'subscription':
187
199
  return this._graphqlSubscribe(
@@ -197,31 +209,26 @@ export class InternalGraphQLAPIClass {
197
209
  private async _graphql<T = any>(
198
210
  { query, variables, authMode }: GraphQLOptions,
199
211
  additionalHeaders = {},
212
+ abortController: AbortController,
200
213
  customUserAgentDetails?: CustomUserAgentDetails
201
214
  ): Promise<GraphQLResult<T>> {
202
215
  const config = Amplify.getConfig();
203
216
 
204
217
  const { region: region, endpoint: appSyncGraphqlEndpoint } =
205
- config.API.GraphQL;
218
+ config.API?.GraphQL || {};
206
219
 
207
220
  const customGraphqlEndpoint = null;
208
221
  const customEndpointRegion = null;
209
222
 
223
+ // TODO: Figure what we need to do to remove `!`'s.
210
224
  const headers = {
211
225
  ...(!customGraphqlEndpoint &&
212
- (await this._headerBasedAuth(
213
- authMode,
214
- additionalHeaders,
215
- customUserAgentDetails
216
- ))),
217
- ...(customGraphqlEndpoint &&
226
+ (await this._headerBasedAuth(authMode!, additionalHeaders))),
227
+ ...((customGraphqlEndpoint &&
218
228
  (customEndpointRegion
219
- ? await this._headerBasedAuth(
220
- authMode,
221
- additionalHeaders,
222
- customUserAgentDetails
223
- )
224
- : { Authorization: null })),
229
+ ? await this._headerBasedAuth(authMode!, additionalHeaders)
230
+ : { Authorization: null })) ||
231
+ {}),
225
232
  ...additionalHeaders,
226
233
  ...(!customGraphqlEndpoint && {
227
234
  [USER_AGENT_HEADER]: getAmplifyUserAgent(customUserAgentDetails),
@@ -230,7 +237,7 @@ export class InternalGraphQLAPIClass {
230
237
 
231
238
  const body = {
232
239
  query: print(query as DocumentNode),
233
- variables,
240
+ variables: variables || null,
234
241
  };
235
242
 
236
243
  const endpoint = customGraphqlEndpoint || appSyncGraphqlEndpoint;
@@ -246,7 +253,7 @@ export class InternalGraphQLAPIClass {
246
253
 
247
254
  let response;
248
255
  try {
249
- const { body: responsePayload } = await this._api.post({
256
+ const { body: responseBody } = await this._api.post({
250
257
  url: new URL(endpoint),
251
258
  options: {
252
259
  headers,
@@ -256,9 +263,20 @@ export class InternalGraphQLAPIClass {
256
263
  region,
257
264
  },
258
265
  },
266
+ abortController,
259
267
  });
260
- response = await responsePayload.json();
268
+
269
+ const result = await responseBody.json();
270
+
271
+ response = result;
261
272
  } catch (err) {
273
+ // If the exception is because user intentionally
274
+ // cancelled the request, do not modify the exception
275
+ // so that clients can identify the exception correctly.
276
+ if (isCancelErrorREST(err)) {
277
+ throw err;
278
+ }
279
+
262
280
  response = {
263
281
  data: {},
264
282
  errors: [new GraphQLError(err.message, null, null, null, null, err)],
@@ -274,13 +292,26 @@ export class InternalGraphQLAPIClass {
274
292
  return response;
275
293
  }
276
294
 
295
+ /**
296
+ * Checks to see if an error thrown is from an api request cancellation
297
+ * @param {any} error - Any error
298
+ * @return {boolean} - A boolean indicating if the error was from an api request cancellation
299
+ */
300
+ isCancelError(error: any): boolean {
301
+ return isCancelErrorREST(error);
302
+ }
303
+
304
+ /**
305
+ * Cancels an inflight request. Only applicable for graphql queries and mutations
306
+ * @param {any} request - request to cancel
307
+ * @returns - A boolean indicating if the request was cancelled
308
+ */
309
+ cancel(request: Promise<any>, message?: string): boolean {
310
+ return cancelREST(request, message);
311
+ }
312
+
277
313
  private _graphqlSubscribe(
278
- {
279
- query,
280
- variables,
281
- authMode: defaultAuthenticationType,
282
- authToken,
283
- }: GraphQLOptions,
314
+ { query, variables, authMode }: GraphQLOptions,
284
315
  additionalHeaders = {},
285
316
  customUserAgentDetails?: CustomUserAgentDetails
286
317
  ): Observable<any> {
@@ -288,14 +319,18 @@ export class InternalGraphQLAPIClass {
288
319
  if (!this.appSyncRealTime) {
289
320
  this.appSyncRealTime = new AWSAppSyncRealTimeProvider();
290
321
  }
291
- return this.appSyncRealTime.subscribe({
292
- query: print(query as DocumentNode),
293
- variables,
294
- appSyncGraphqlEndpoint: GraphQL?.endpoint,
295
- region: GraphQL?.region,
296
- authenticationType: GraphQL?.defaultAuthMode,
297
- apiKey: GraphQL?.apiKey,
298
- });
322
+ return this.appSyncRealTime.subscribe(
323
+ {
324
+ query: print(query as DocumentNode),
325
+ variables,
326
+ appSyncGraphqlEndpoint: GraphQL?.endpoint,
327
+ region: GraphQL?.region,
328
+ authenticationType: authMode ?? GraphQL?.defaultAuthMode,
329
+ apiKey: GraphQL?.apiKey,
330
+ additionalHeaders,
331
+ },
332
+ customUserAgentDetails
333
+ );
299
334
  }
300
335
  }
301
336
 
@@ -5,4 +5,4 @@ export {
5
5
  InternalGraphQLAPIClass,
6
6
  } from './InternalGraphQLAPI';
7
7
 
8
- export { graphql } from './v6';
8
+ export { graphql, cancel, isCancelError } from './v6';
@@ -103,4 +103,22 @@ export function graphql<
103
103
  return result as any;
104
104
  }
105
105
 
106
+ /**
107
+ * Cancels an inflight request. Only applicable for graphql queries and mutations
108
+ * @param {any} request - request to cancel
109
+ * @returns - A boolean indicating if the request was cancelled
110
+ */
111
+ export function cancel(promise: Promise<any>, message?: string): boolean {
112
+ return GraphQLAPI.cancel(promise, message);
113
+ }
114
+
115
+ /**
116
+ * Checks to see if an error thrown is from an api request cancellation
117
+ * @param {any} error - Any error
118
+ * @returns - A boolean indicating if the error was from an api request cancellation
119
+ */
120
+ export function isCancelError(error: any): boolean {
121
+ return GraphQLAPI.isCancelError(error);
122
+ }
123
+
106
124
  export { GraphQLOptionsV6, GraphQLResponseV6 };
@@ -52,5 +52,5 @@ export enum ConnectionState {
52
52
  ConnectedPendingKeepAlive = 'ConnectedPendingKeepAlive',
53
53
  }
54
54
 
55
- export type PubSubContent = Record<string, unknown> | string;
55
+ export type PubSubContent = Record<string, unknown>;
56
56
  export type PubSubContentObserver = Observer<PubSubContent>;
@@ -12,7 +12,7 @@ export { CONTROL_MSG, ConnectionState } from './PubSub';
12
12
  export interface GraphQLOptions {
13
13
  query: string | DocumentNode;
14
14
  variables?: Record<string, DocumentType>;
15
- authMode?: string;
15
+ authMode?: APIAuthMode;
16
16
  authToken?: string;
17
17
  /**
18
18
  * @deprecated This property should not be used
@@ -21,7 +21,7 @@ export interface GraphQLOptions {
21
21
  }
22
22
 
23
23
  export interface GraphQLResult<T = object> {
24
- data?: T;
24
+ data: T;
25
25
  errors?: GraphQLError[];
26
26
  extensions?: {
27
27
  [key: string]: any;
@@ -36,6 +36,82 @@ export type GraphQLSubscription<T> = T & {
36
36
  readonly [queryType]: 'subscription';
37
37
  };
38
38
 
39
+ /**
40
+ * Accepts a code generated model type and returns a supertype that
41
+ * can accept return values from the relevant graphql operations.
42
+ *
43
+ * For example:
44
+ *
45
+ * ```typescript
46
+ * import { GraphQLReturnType } from 'aws-amplify/api';
47
+ * import { MyModel } from './API';
48
+ * import { getMyModel } from './graphql/queries';
49
+ *
50
+ * const [item, setItem] = useState<GraphQLReturnType<MyModel>>();
51
+ * setItem(await client.graphql({ query: getMyModel }).data.getMyModel!)
52
+ * ```
53
+ *
54
+ * Trying to assign the result of a `getMyModel` operation directly to a
55
+ * `MyModel` variables won't necessarily work, because normally-required related
56
+ * models might not be part of the selection set.
57
+ *
58
+ * This util simply makes related model properties optional recursively.
59
+ */
60
+ export type GraphQLReturnType<T> = T extends {}
61
+ ? {
62
+ [K in keyof T]?: GraphQLReturnType<T[K]>;
63
+ }
64
+ : T;
65
+
66
+ /**
67
+ * Describes a paged list result from AppSync, which can either
68
+ * live at the top query or property (e.g., related model) level.
69
+ */
70
+ type PagedList<T, TYPENAME> = {
71
+ __typename: TYPENAME;
72
+ nextToken?: string | null | undefined;
73
+ items: Array<T>;
74
+ };
75
+
76
+ /**
77
+ * Recursively looks through a result type and removes nulls and
78
+ * and undefined from `PagedList` types.
79
+ *
80
+ * Although a graphql response might contain empty values in an
81
+ * array, this will only be the case when we also have errors,
82
+ * which will then be *thrown*.
83
+ */
84
+ type WithListsFixed<T> = T extends PagedList<infer IT, infer NAME>
85
+ ? PagedList<Exclude<IT, null | undefined>, NAME>
86
+ : T extends {}
87
+ ? {
88
+ [K in keyof T]: WithListsFixed<T[K]>;
89
+ }
90
+ : T;
91
+
92
+ /**
93
+ * Returns an updated response type to always return a value.
94
+ */
95
+ type NeverEmpty<T> = {
96
+ [K in keyof T]-?: Exclude<WithListsFixed<T[K]>, undefined | null>;
97
+ };
98
+
99
+ /**
100
+ * Replaces all list result types in a query result with types to exclude
101
+ * nulls and undefined from list member unions.
102
+ *
103
+ * If empty members are present, there will also be errors present,
104
+ * and the response will instead be *thrown*.
105
+ */
106
+ type FixedQueryResult<T> = Exclude<
107
+ T[keyof T],
108
+ null | undefined
109
+ > extends PagedList<any, any>
110
+ ? {
111
+ [K in keyof T]-?: WithListsFixed<Exclude<T[K], null | undefined>>;
112
+ }
113
+ : T;
114
+
39
115
  /**
40
116
  * The return value from a `graphql({query})` call when `query` is a subscription.
41
117
  *
@@ -77,7 +153,7 @@ export type GraphqlSubscriptionResult<T> = Observable<
77
153
  * ```
78
154
  */
79
155
  export type GraphqlSubscriptionMessage<T> = {
80
- data?: T;
156
+ data: T;
81
157
  };
82
158
 
83
159
  export interface AWSAppSyncRealTimeProviderOptions {
@@ -165,11 +241,11 @@ export type GraphQLResponseV6<
165
241
  FALLBACK_TYPE = unknown,
166
242
  TYPED_GQL_STRING extends string = string
167
243
  > = TYPED_GQL_STRING extends GeneratedQuery<infer IN, infer QUERY_OUT>
168
- ? Promise<GraphQLResult<QUERY_OUT>>
244
+ ? Promise<GraphQLResult<FixedQueryResult<QUERY_OUT>>>
169
245
  : TYPED_GQL_STRING extends GeneratedMutation<infer IN, infer MUTATION_OUT>
170
- ? Promise<GraphQLResult<MUTATION_OUT>>
246
+ ? Promise<GraphQLResult<NeverEmpty<MUTATION_OUT>>>
171
247
  : TYPED_GQL_STRING extends GeneratedSubscription<infer IN, infer SUB_OUT>
172
- ? GraphqlSubscriptionResult<SUB_OUT>
248
+ ? GraphqlSubscriptionResult<NeverEmpty<SUB_OUT>>
173
249
  : FALLBACK_TYPE extends GraphQLQuery<infer T>
174
250
  ? Promise<GraphQLResult<FALLBACK_TYPE>>
175
251
  : FALLBACK_TYPE extends GraphQLSubscription<infer T>