@aws-amplify/api-graphql 4.0.23 → 4.0.24-unstable.873a6f4.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.
Files changed (50) hide show
  1. package/dist/cjs/internals/APIClient.js +13 -0
  2. package/dist/cjs/internals/APIClient.js.map +1 -1
  3. package/dist/cjs/internals/InternalGraphQLAPI.js +36 -38
  4. package/dist/cjs/internals/InternalGraphQLAPI.js.map +1 -1
  5. package/dist/cjs/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.js +15 -0
  6. package/dist/cjs/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.js.map +1 -0
  7. package/dist/cjs/utils/errors/constants.js +47 -0
  8. package/dist/cjs/utils/errors/constants.js.map +1 -0
  9. package/dist/cjs/utils/errors/createGraphQLResultWithError.js +15 -0
  10. package/dist/cjs/utils/errors/createGraphQLResultWithError.js.map +1 -0
  11. package/dist/cjs/utils/errors/index.js +3 -1
  12. package/dist/cjs/utils/errors/index.js.map +1 -1
  13. package/dist/cjs/utils/errors/repackageAuthError.js +6 -6
  14. package/dist/cjs/utils/errors/repackageAuthError.js.map +1 -1
  15. package/dist/cjs/utils/resolveOwnerFields.js +4 -1
  16. package/dist/cjs/utils/resolveOwnerFields.js.map +1 -1
  17. package/dist/esm/internals/APIClient.mjs +13 -0
  18. package/dist/esm/internals/APIClient.mjs.map +1 -1
  19. package/dist/esm/internals/InternalGraphQLAPI.mjs +40 -40
  20. package/dist/esm/internals/InternalGraphQLAPI.mjs.map +1 -1
  21. package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.d.ts +2 -0
  22. package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.mjs +12 -0
  23. package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.mjs.map +1 -0
  24. package/dist/esm/types/index.d.ts +2 -1
  25. package/dist/esm/utils/errors/constants.d.ts +7 -0
  26. package/dist/esm/utils/errors/constants.mjs +46 -0
  27. package/dist/esm/utils/errors/constants.mjs.map +1 -0
  28. package/dist/esm/utils/errors/createGraphQLResultWithError.d.ts +2 -0
  29. package/dist/esm/utils/errors/createGraphQLResultWithError.mjs +13 -0
  30. package/dist/esm/utils/errors/createGraphQLResultWithError.mjs.map +1 -0
  31. package/dist/esm/utils/errors/index.d.ts +1 -0
  32. package/dist/esm/utils/errors/index.mjs +1 -0
  33. package/dist/esm/utils/errors/index.mjs.map +1 -1
  34. package/dist/esm/utils/errors/repackageAuthError.d.ts +3 -7
  35. package/dist/esm/utils/errors/repackageAuthError.mjs +5 -5
  36. package/dist/esm/utils/errors/repackageAuthError.mjs.map +1 -1
  37. package/dist/esm/utils/resolveConfig.mjs +1 -0
  38. package/dist/esm/utils/resolveConfig.mjs.map +1 -1
  39. package/dist/esm/utils/resolveOwnerFields.mjs +4 -1
  40. package/dist/esm/utils/resolveOwnerFields.mjs.map +1 -1
  41. package/package.json +5 -5
  42. package/src/internals/APIClient.ts +19 -0
  43. package/src/internals/InternalGraphQLAPI.ts +50 -56
  44. package/src/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.ts +15 -0
  45. package/src/types/index.ts +2 -0
  46. package/src/utils/errors/constants.ts +56 -0
  47. package/src/utils/errors/createGraphQLResultWithError.ts +15 -0
  48. package/src/utils/errors/index.ts +1 -0
  49. package/src/utils/errors/repackageAuthError.ts +8 -10
  50. package/src/utils/resolveOwnerFields.ts +4 -1
@@ -2,7 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import {
4
4
  DocumentNode,
5
- GraphQLError,
6
5
  OperationDefinitionNode,
7
6
  OperationTypeNode,
8
7
  parse,
@@ -25,14 +24,20 @@ import {
25
24
  import { CustomHeaders, RequestOptions } from '@aws-amplify/data-schema-types';
26
25
 
27
26
  import { AWSAppSyncRealTimeProvider } from '../Providers/AWSAppSyncRealTimeProvider';
28
- import {
29
- GraphQLAuthError,
30
- GraphQLOperation,
31
- GraphQLOptions,
32
- GraphQLResult,
33
- } from '../types';
27
+ import { GraphQLOperation, GraphQLOptions, GraphQLResult } from '../types';
34
28
  import { resolveConfig, resolveLibraryOptions } from '../utils';
35
- import { repackageUnauthError } from '../utils/errors/repackageAuthError';
29
+ import { repackageUnauthorizedError } from '../utils/errors/repackageAuthError';
30
+ import {
31
+ NO_API_KEY,
32
+ NO_AUTH_TOKEN_HEADER,
33
+ NO_ENDPOINT,
34
+ NO_SIGNED_IN_USER,
35
+ NO_VALID_AUTH_TOKEN,
36
+ NO_VALID_CREDENTIALS,
37
+ } from '../utils/errors/constants';
38
+ import { GraphQLApiError, createGraphQLResultWithError } from '../utils/errors';
39
+
40
+ import { isGraphQLResponseWithErrors } from './utils/runtimeTypeGuards/isGraphQLResponseWithErrors';
36
41
 
37
42
  const USER_AGENT_HEADER = 'x-amz-user-agent';
38
43
 
@@ -76,7 +81,7 @@ export class InternalGraphQLAPIClass {
76
81
  switch (authMode) {
77
82
  case 'apiKey':
78
83
  if (!apiKey) {
79
- throw new Error(GraphQLAuthError.NO_API_KEY);
84
+ throw new GraphQLApiError(NO_API_KEY);
80
85
  }
81
86
  headers = {
82
87
  'X-Api-Key': apiKey,
@@ -85,33 +90,44 @@ export class InternalGraphQLAPIClass {
85
90
  case 'iam': {
86
91
  const session = await amplify.Auth.fetchAuthSession();
87
92
  if (session.credentials === undefined) {
88
- throw new Error(GraphQLAuthError.NO_CREDENTIALS);
93
+ throw new GraphQLApiError(NO_VALID_CREDENTIALS);
89
94
  }
90
95
  break;
91
96
  }
92
97
  case 'oidc':
93
- case 'userPool':
98
+ case 'userPool': {
99
+ let token: string | undefined;
100
+
94
101
  try {
95
- const token = (
102
+ token = (
96
103
  await amplify.Auth.fetchAuthSession()
97
104
  ).tokens?.accessToken.toString();
98
-
99
- if (!token) {
100
- throw new Error(GraphQLAuthError.NO_FEDERATED_JWT);
101
- }
102
- headers = {
103
- Authorization: token,
104
- };
105
105
  } catch (e) {
106
- throw new Error(GraphQLAuthError.NO_CURRENT_USER);
106
+ // fetchAuthSession failed
107
+ throw new GraphQLApiError({
108
+ ...NO_SIGNED_IN_USER,
109
+ underlyingError: e,
110
+ });
111
+ }
112
+
113
+ // `fetchAuthSession()` succeeded but didn't return `tokens`.
114
+ // This may happen when unauthenticated access is enabled and there is
115
+ // no user signed in.
116
+ if (!token) {
117
+ throw new GraphQLApiError(NO_VALID_AUTH_TOKEN);
107
118
  }
119
+
120
+ headers = {
121
+ Authorization: token,
122
+ };
108
123
  break;
124
+ }
109
125
  case 'lambda':
110
126
  if (
111
127
  typeof additionalHeaders === 'object' &&
112
128
  !additionalHeaders.Authorization
113
129
  ) {
114
- throw new Error(GraphQLAuthError.NO_AUTH_TOKEN);
130
+ throw new GraphQLApiError(NO_AUTH_TOKEN_HEADER);
115
131
  }
116
132
 
117
133
  headers = {
@@ -350,18 +366,15 @@ export class InternalGraphQLAPIClass {
350
366
  const endpoint = customEndpoint || appSyncGraphqlEndpoint;
351
367
 
352
368
  if (!endpoint) {
353
- const error = new GraphQLError('No graphql endpoint provided.');
354
- // TODO(Eslint): refactor this to throw an Error instead of a plain object
355
- // eslint-disable-next-line no-throw-literal
356
- throw {
357
- data: {},
358
- errors: [error],
359
- };
369
+ throw createGraphQLResultWithError<T>(new GraphQLApiError(NO_ENDPOINT));
360
370
  }
361
371
 
362
372
  let response: any;
363
373
 
364
374
  try {
375
+ // See the inline doc of the REST `post()` API for possible errors to be thrown.
376
+ // As these errors are catastrophic they should be caught and handled by GraphQL
377
+ // API consumers.
365
378
  const { body: responseBody } = await this._api.post(amplify, {
366
379
  url: new AmplifyUrl(endpoint),
367
380
  options: {
@@ -373,39 +386,20 @@ export class InternalGraphQLAPIClass {
373
386
  abortController,
374
387
  });
375
388
 
376
- const result = await responseBody.json();
377
-
378
- response = result;
379
- } catch (err) {
380
- // If the exception is because user intentionally
381
- // cancelled the request, do not modify the exception
382
- // so that clients can identify the exception correctly.
383
- if (this.isCancelError(err)) {
384
- throw err;
389
+ response = await responseBody.json();
390
+ } catch (error) {
391
+ if (this.isCancelError(error)) {
392
+ throw error;
385
393
  }
386
394
 
387
- response = {
388
- data: {},
389
- errors: [
390
- new GraphQLError(
391
- (err as any).message,
392
- null,
393
- null,
394
- null,
395
- null,
396
- err as any,
397
- ),
398
- ],
399
- };
395
+ response = createGraphQLResultWithError<T>(error as any);
400
396
  }
401
397
 
402
- const { errors } = response;
403
-
404
- if (errors && errors.length) {
405
- throw repackageUnauthError(response);
398
+ if (isGraphQLResponseWithErrors(response)) {
399
+ throw repackageUnauthorizedError(response);
406
400
  }
407
401
 
408
- return response;
402
+ return response as unknown as GraphQLResult<T>;
409
403
  }
410
404
 
411
405
  /**
@@ -463,7 +457,7 @@ export class InternalGraphQLAPIClass {
463
457
  .pipe(
464
458
  catchError(e => {
465
459
  if (e.errors) {
466
- throw repackageUnauthError(e);
460
+ throw repackageUnauthorizedError(e);
467
461
  }
468
462
  throw e;
469
463
  }),
@@ -0,0 +1,15 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { GraphQLResult } from '../../../types';
5
+
6
+ export function isGraphQLResponseWithErrors(
7
+ response: unknown,
8
+ ): response is GraphQLResult {
9
+ if (!response) {
10
+ return false;
11
+ }
12
+ const r = response as GraphQLResult;
13
+
14
+ return Array.isArray(r.errors) && r.errors.length > 0;
15
+ }
@@ -7,6 +7,7 @@ import {
7
7
  CustomQueries,
8
8
  CustomSubscriptions,
9
9
  EnumTypes,
10
+ ModelSortDirection,
10
11
  ModelTypes,
11
12
  } from '@aws-amplify/data-schema-types';
12
13
  import { DocumentNode, GraphQLError, Source } from 'graphql';
@@ -464,6 +465,7 @@ export type QueryArgs = Record<string, unknown>;
464
465
  export interface ListArgs extends Record<string, unknown> {
465
466
  selectionSet?: string[];
466
467
  filter?: Record<string, unknown>;
468
+ sortDirection?: ModelSortDirection;
467
469
  headers?: CustomHeaders;
468
470
  }
469
471
 
@@ -0,0 +1,56 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { AmplifyErrorParams } from '@aws-amplify/core/internals/utils';
5
+
6
+ import { GraphQLAuthError } from '../../types';
7
+
8
+ export const NO_API_KEY: AmplifyErrorParams = {
9
+ name: 'NoApiKey',
10
+ // ideal: No API key configured.
11
+ message: GraphQLAuthError.NO_API_KEY,
12
+ recoverySuggestion:
13
+ 'The API request was made with `authMode: "apiKey"` but no API Key was passed into `Amplify.configure()`. Review if your API key is passed into the `Amplify.configure()` function.',
14
+ };
15
+
16
+ export const NO_VALID_CREDENTIALS: AmplifyErrorParams = {
17
+ name: 'NoCredentials',
18
+ // ideal: No auth credentials available.
19
+ message: GraphQLAuthError.NO_CREDENTIALS,
20
+ recoverySuggestion: `The API request was made with \`authMode: "iam"\` but no authentication credentials are available.
21
+
22
+ If you intended to make a request using an authenticated role, review if your user is signed in before making the request.
23
+
24
+ If you intend to make a request using an unauthenticated role or also known as "guest access", verify if "Auth.Cognito.allowGuestAccess" is set to "true" in the \`Amplify.configure()\` function.`,
25
+ };
26
+
27
+ export const NO_VALID_AUTH_TOKEN: AmplifyErrorParams = {
28
+ name: 'NoValidAuthTokens',
29
+ // ideal: No valid JWT auth token provided to make the API request..
30
+ message: GraphQLAuthError.NO_FEDERATED_JWT,
31
+ recoverySuggestion:
32
+ 'If you intended to make an authenticated API request, review if the current user is signed in.',
33
+ };
34
+
35
+ export const NO_SIGNED_IN_USER: AmplifyErrorParams = {
36
+ name: 'NoSignedUser',
37
+ // ideal: Couldn't retrieve authentication credentials to make the API request.
38
+ message: GraphQLAuthError.NO_CURRENT_USER,
39
+ recoverySuggestion:
40
+ 'Review the underlying exception field for more details. If you intended to make an authenticated API request, review if the current user is signed in.',
41
+ };
42
+
43
+ export const NO_AUTH_TOKEN_HEADER: AmplifyErrorParams = {
44
+ name: 'NoAuthorizationHeader',
45
+ // ideal: Authorization header not specified.
46
+ message: GraphQLAuthError.NO_AUTH_TOKEN,
47
+ recoverySuggestion:
48
+ 'The API request was made with `authMode: "lambda"` but no `authToken` is set. Review if a valid authToken is passed into the request options or in the `Amplify.configure()` function.',
49
+ };
50
+
51
+ export const NO_ENDPOINT: AmplifyErrorParams = {
52
+ name: 'NoEndpoint',
53
+ message: 'No GraphQL endpoint configured in `Amplify.configure()`.',
54
+ recoverySuggestion:
55
+ 'Review if the GraphQL API endpoint is set in the `Amplify.configure()` function.',
56
+ };
@@ -0,0 +1,15 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { GraphQLError } from 'graphql';
5
+
6
+ import { GraphQLResult } from '../../types';
7
+
8
+ export const createGraphQLResultWithError = <T>(
9
+ error: Error,
10
+ ): GraphQLResult<T> => {
11
+ return {
12
+ data: {} as T,
13
+ errors: [new GraphQLError(error.message, null, null, null, null, error)],
14
+ };
15
+ };
@@ -4,3 +4,4 @@
4
4
  export { GraphQLApiError } from './GraphQLApiError';
5
5
  export { assertValidationError } from './assertValidationError';
6
6
  export { APIValidationErrorCode, validationErrorMap } from './validation';
7
+ export { createGraphQLResultWithError } from './createGraphQLResultWithError';
@@ -1,23 +1,21 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { AmplifyErrorParams } from '@aws-amplify/core/internals/utils';
5
-
6
- interface ErrorObject {
7
- errors: AmplifyErrorParams[];
8
- }
4
+ import { GraphQLResult } from '../../types';
9
5
 
10
6
  /**
11
7
  * Checks to see if the given response or subscription message contains an
12
- * unauth error. If it does, it changes the error message to include instructions
8
+ * Unauthorized error. If it does, it changes the error message to include instructions
13
9
  * for the app developer.
14
10
  */
15
- export function repackageUnauthError<T extends ErrorObject>(content: T): T {
11
+ export function repackageUnauthorizedError<T extends GraphQLResult<any>>(
12
+ content: T,
13
+ ): T {
16
14
  if (content.errors && Array.isArray(content.errors)) {
17
15
  content.errors.forEach(e => {
18
- if (isUnauthError(e)) {
16
+ if (isUnauthorizedError(e)) {
19
17
  e.message = 'Unauthorized';
20
- e.recoverySuggestion =
18
+ (e as any).recoverySuggestion =
21
19
  `If you're calling an Amplify-generated API, make sure ` +
22
20
  `to set the "authMode" in generateClient({ authMode: '...' }) to the backend authorization ` +
23
21
  `rule's auth provider ('apiKey', 'userPool', 'iam', 'oidc', 'lambda')`;
@@ -28,7 +26,7 @@ export function repackageUnauthError<T extends ErrorObject>(content: T): T {
28
26
  return content;
29
27
  }
30
28
 
31
- function isUnauthError(error: any): boolean {
29
+ function isUnauthorizedError(error: any): boolean {
32
30
  // Error pattern corresponding to appsync calls
33
31
  if (error?.originalError?.name?.startsWith('UnauthorizedException')) {
34
32
  return true;
@@ -42,7 +42,10 @@ export function resolveOwnerFields(model: Model): string[] {
42
42
  for (const rule of attr.properties.rules) {
43
43
  if (rule.allow === 'owner') {
44
44
  ownerFields.add(rule.ownerField || 'owner');
45
- } else if (rule.allow === 'groups') {
45
+ } else if (rule.allow === 'groups' && rule.groupsField !== undefined) {
46
+ // only valid for dynamic group(s)
47
+ // static group auth will have an array of predefined groups in the attribute, groups: string[]
48
+ // but `groupsField` will be undefined
46
49
  ownerFields.add(rule.groupsField);
47
50
  }
48
51
  }