@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.
- package/dist/cjs/internals/APIClient.js +13 -0
- package/dist/cjs/internals/APIClient.js.map +1 -1
- package/dist/cjs/internals/InternalGraphQLAPI.js +36 -38
- package/dist/cjs/internals/InternalGraphQLAPI.js.map +1 -1
- package/dist/cjs/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.js +15 -0
- package/dist/cjs/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.js.map +1 -0
- package/dist/cjs/utils/errors/constants.js +47 -0
- package/dist/cjs/utils/errors/constants.js.map +1 -0
- package/dist/cjs/utils/errors/createGraphQLResultWithError.js +15 -0
- package/dist/cjs/utils/errors/createGraphQLResultWithError.js.map +1 -0
- package/dist/cjs/utils/errors/index.js +3 -1
- package/dist/cjs/utils/errors/index.js.map +1 -1
- package/dist/cjs/utils/errors/repackageAuthError.js +6 -6
- package/dist/cjs/utils/errors/repackageAuthError.js.map +1 -1
- package/dist/cjs/utils/resolveOwnerFields.js +4 -1
- package/dist/cjs/utils/resolveOwnerFields.js.map +1 -1
- package/dist/esm/internals/APIClient.mjs +13 -0
- package/dist/esm/internals/APIClient.mjs.map +1 -1
- package/dist/esm/internals/InternalGraphQLAPI.mjs +40 -40
- package/dist/esm/internals/InternalGraphQLAPI.mjs.map +1 -1
- package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.d.ts +2 -0
- package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.mjs +12 -0
- package/dist/esm/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.mjs.map +1 -0
- package/dist/esm/types/index.d.ts +2 -1
- package/dist/esm/utils/errors/constants.d.ts +7 -0
- package/dist/esm/utils/errors/constants.mjs +46 -0
- package/dist/esm/utils/errors/constants.mjs.map +1 -0
- package/dist/esm/utils/errors/createGraphQLResultWithError.d.ts +2 -0
- package/dist/esm/utils/errors/createGraphQLResultWithError.mjs +13 -0
- package/dist/esm/utils/errors/createGraphQLResultWithError.mjs.map +1 -0
- package/dist/esm/utils/errors/index.d.ts +1 -0
- package/dist/esm/utils/errors/index.mjs +1 -0
- package/dist/esm/utils/errors/index.mjs.map +1 -1
- package/dist/esm/utils/errors/repackageAuthError.d.ts +3 -7
- package/dist/esm/utils/errors/repackageAuthError.mjs +5 -5
- package/dist/esm/utils/errors/repackageAuthError.mjs.map +1 -1
- package/dist/esm/utils/resolveConfig.mjs +1 -0
- package/dist/esm/utils/resolveConfig.mjs.map +1 -1
- package/dist/esm/utils/resolveOwnerFields.mjs +4 -1
- package/dist/esm/utils/resolveOwnerFields.mjs.map +1 -1
- package/package.json +5 -5
- package/src/internals/APIClient.ts +19 -0
- package/src/internals/InternalGraphQLAPI.ts +50 -56
- package/src/internals/utils/runtimeTypeGuards/isGraphQLResponseWithErrors.ts +15 -0
- package/src/types/index.ts +2 -0
- package/src/utils/errors/constants.ts +56 -0
- package/src/utils/errors/createGraphQLResultWithError.ts +15 -0
- package/src/utils/errors/index.ts +1 -0
- package/src/utils/errors/repackageAuthError.ts +8 -10
- 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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
*
|
|
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
|
|
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 (
|
|
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
|
|
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
|
}
|