@aws-amplify/api-graphql 3.3.2-unstable.f47d472.1 → 3.4.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/src/GraphQLAPI.ts CHANGED
@@ -1,213 +1,26 @@
1
- // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
- // SPDX-License-Identifier: Apache-2.0
3
- import {
4
- DocumentNode,
5
- OperationDefinitionNode,
6
- print,
7
- parse,
8
- GraphQLError,
9
- OperationTypeNode,
10
- } from 'graphql';
1
+ import { Amplify } from '@aws-amplify/core';
2
+ import { GraphQLOptions, GraphQLResult } from './types';
3
+ import { InternalGraphQLAPIClass } from './internals';
11
4
  import Observable from 'zen-observable-ts';
12
- import {
13
- Amplify,
14
- ConsoleLogger as Logger,
15
- Credentials,
16
- getAmplifyUserAgent,
17
- INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER,
18
- } from '@aws-amplify/core';
19
- import { PubSub } from '@aws-amplify/pubsub';
20
- import { Auth } from '@aws-amplify/auth';
21
- import { Cache } from '@aws-amplify/cache';
22
- import {
23
- GraphQLAuthError,
24
- GraphQLOptions,
25
- GraphQLResult,
26
- GraphQLOperation,
27
- } from './types';
28
- import { RestClient } from '@aws-amplify/api-rest';
29
- const USER_AGENT_HEADER = 'x-amz-user-agent';
30
-
31
- const logger = new Logger('GraphQLAPI');
32
5
 
33
6
  export const graphqlOperation = (
34
7
  query,
35
8
  variables = {},
36
- authToken?: string,
37
- userAgentSuffix?: string
9
+ authToken?: string
38
10
  ) => ({
39
11
  query,
40
12
  variables,
41
13
  authToken,
42
- userAgentSuffix,
43
14
  });
44
15
 
45
16
  /**
46
17
  * Export Cloud Logic APIs
47
18
  */
48
- export class GraphQLAPIClass {
49
- /**
50
- * @private
51
- */
52
- private _options;
53
- private _api = null;
54
-
55
- Auth = Auth;
56
- Cache = Cache;
57
- Credentials = Credentials;
58
-
59
- /**
60
- * Initialize GraphQL API with AWS configuration
61
- * @param {Object} options - Configuration object for API
62
- */
63
- constructor(options) {
64
- this._options = options;
65
- logger.debug('API Options', this._options);
66
- }
67
-
19
+ export class GraphQLAPIClass extends InternalGraphQLAPIClass {
68
20
  public getModuleName() {
69
21
  return 'GraphQLAPI';
70
22
  }
71
23
 
72
- /**
73
- * Configure API
74
- * @param {Object} config - Configuration of the API
75
- * @return {Object} - The current configuration
76
- */
77
- configure(options) {
78
- const { API = {}, ...otherOptions } = options || {};
79
- let opt = { ...otherOptions, ...API };
80
- logger.debug('configure GraphQL API', { opt });
81
-
82
- if (opt['aws_project_region']) {
83
- opt = Object.assign({}, opt, {
84
- region: opt['aws_project_region'],
85
- header: {},
86
- });
87
- }
88
-
89
- if (
90
- typeof opt.graphql_headers !== 'undefined' &&
91
- typeof opt.graphql_headers !== 'function'
92
- ) {
93
- logger.warn('graphql_headers should be a function');
94
- opt.graphql_headers = undefined;
95
- }
96
-
97
- this._options = Object.assign({}, this._options, opt);
98
-
99
- this.createInstance();
100
-
101
- return this._options;
102
- }
103
-
104
- /**
105
- * Create an instance of API for the library
106
- * @return - A promise of true if Success
107
- */
108
- createInstance() {
109
- logger.debug('create Rest instance');
110
- if (this._options) {
111
- this._api = new RestClient(this._options);
112
- // Share instance Credentials with client for SSR
113
- this._api.Credentials = this.Credentials;
114
-
115
- return true;
116
- } else {
117
- return Promise.reject('API not configured');
118
- }
119
- }
120
-
121
- private async _headerBasedAuth(
122
- defaultAuthenticationType?,
123
- additionalHeaders: { [key: string]: string } = {}
124
- ) {
125
- const { aws_appsync_authenticationType, aws_appsync_apiKey: apiKey } =
126
- this._options;
127
- const authenticationType =
128
- defaultAuthenticationType || aws_appsync_authenticationType || 'AWS_IAM';
129
- let headers = {};
130
-
131
- switch (authenticationType) {
132
- case 'API_KEY':
133
- if (!apiKey) {
134
- throw new Error(GraphQLAuthError.NO_API_KEY);
135
- }
136
- headers = {
137
- Authorization: null,
138
- 'X-Api-Key': apiKey,
139
- };
140
- break;
141
- case 'AWS_IAM':
142
- const credentialsOK = await this._ensureCredentials();
143
- if (!credentialsOK) {
144
- throw new Error(GraphQLAuthError.NO_CREDENTIALS);
145
- }
146
- break;
147
- case 'OPENID_CONNECT':
148
- try {
149
- let token;
150
- // backwards compatibility
151
- const federatedInfo = await Cache.getItem('federatedInfo');
152
- if (federatedInfo) {
153
- token = federatedInfo.token;
154
- } else {
155
- const currentUser = await Auth.currentAuthenticatedUser();
156
- if (currentUser) {
157
- token = currentUser.token;
158
- }
159
- }
160
- if (!token) {
161
- throw new Error(GraphQLAuthError.NO_FEDERATED_JWT);
162
- }
163
- headers = {
164
- Authorization: token,
165
- };
166
- } catch (e) {
167
- throw new Error(GraphQLAuthError.NO_CURRENT_USER);
168
- }
169
- break;
170
- case 'AMAZON_COGNITO_USER_POOLS':
171
- try {
172
- const session = await this.Auth.currentSession();
173
- headers = {
174
- Authorization: session.getAccessToken().getJwtToken(),
175
- };
176
- } catch (e) {
177
- throw new Error(GraphQLAuthError.NO_CURRENT_USER);
178
- }
179
- break;
180
- case 'AWS_LAMBDA':
181
- if (!additionalHeaders.Authorization) {
182
- throw new Error(GraphQLAuthError.NO_AUTH_TOKEN);
183
- }
184
- headers = {
185
- Authorization: additionalHeaders.Authorization,
186
- };
187
- break;
188
- default:
189
- headers = {
190
- Authorization: null,
191
- };
192
- break;
193
- }
194
-
195
- return headers;
196
- }
197
-
198
- /**
199
- * to get the operation type
200
- * @param operation
201
- */
202
- getGraphqlOperationType(operation: GraphQLOperation): OperationTypeNode {
203
- const doc = parse(operation);
204
- const definitions =
205
- doc.definitions as ReadonlyArray<OperationDefinitionNode>;
206
- const [{ operation: operationType }] = definitions;
207
-
208
- return operationType;
209
- }
210
-
211
24
  /**
212
25
  * Executes a GraphQL operation
213
26
  *
@@ -216,227 +29,10 @@ export class GraphQLAPIClass {
216
29
  * @returns An Observable if the query is a subscription query, else a promise of the graphql result.
217
30
  */
218
31
  graphql<T = any>(
219
- {
220
- query: paramQuery,
221
- variables = {},
222
- authMode,
223
- authToken,
224
- userAgentSuffix,
225
- }: GraphQLOptions,
32
+ options: GraphQLOptions,
226
33
  additionalHeaders?: { [key: string]: string }
227
34
  ): Observable<GraphQLResult<T>> | Promise<GraphQLResult<T>> {
228
- const query =
229
- typeof paramQuery === 'string'
230
- ? parse(paramQuery)
231
- : parse(print(paramQuery));
232
-
233
- const [operationDef = {}] = query.definitions.filter(
234
- def => def.kind === 'OperationDefinition'
235
- );
236
- const { operation: operationType } =
237
- operationDef as OperationDefinitionNode;
238
-
239
- const headers = additionalHeaders || {};
240
-
241
- // if an authorization header is set, have the explicit authToken take precedence
242
- if (authToken) {
243
- headers.Authorization = authToken;
244
- }
245
-
246
- switch (operationType) {
247
- case 'query':
248
- case 'mutation':
249
- this.createInstanceIfNotCreated();
250
- const cancellableToken = this._api.getCancellableToken();
251
- const initParams = {
252
- cancellableToken,
253
- withCredentials: this._options.withCredentials,
254
- };
255
- const responsePromise = this._graphql<T>(
256
- { query, variables, authMode, userAgentSuffix },
257
- headers,
258
- initParams
259
- );
260
- this._api.updateRequestToBeCancellable(
261
- responsePromise,
262
- cancellableToken
263
- );
264
- return responsePromise;
265
- case 'subscription':
266
- return this._graphqlSubscribe({ query, variables, authMode }, headers);
267
- default:
268
- throw new Error(`invalid operation type: ${operationType}`);
269
- }
270
- }
271
-
272
- private async _graphql<T = any>(
273
- { query, variables, authMode, userAgentSuffix }: GraphQLOptions,
274
- additionalHeaders = {},
275
- initParams = {}
276
- ): Promise<GraphQLResult<T>> {
277
- this.createInstanceIfNotCreated();
278
- const {
279
- aws_appsync_region: region,
280
- aws_appsync_graphqlEndpoint: appSyncGraphqlEndpoint,
281
- graphql_headers = () => ({}),
282
- graphql_endpoint: customGraphqlEndpoint,
283
- graphql_endpoint_iam_region: customEndpointRegion,
284
- } = this._options;
285
-
286
- const headers = {
287
- ...(!customGraphqlEndpoint &&
288
- (await this._headerBasedAuth(authMode, additionalHeaders))),
289
- ...(customGraphqlEndpoint &&
290
- (customEndpointRegion
291
- ? await this._headerBasedAuth(authMode, additionalHeaders)
292
- : { Authorization: null })),
293
- ...(await graphql_headers({ query, variables })),
294
- ...additionalHeaders,
295
- ...(!customGraphqlEndpoint && {
296
- [USER_AGENT_HEADER]: getAmplifyUserAgent(userAgentSuffix),
297
- }),
298
- };
299
-
300
- const body = {
301
- query: print(query as DocumentNode),
302
- variables,
303
- };
304
-
305
- const init = Object.assign(
306
- {
307
- headers,
308
- body,
309
- signerServiceInfo: {
310
- service: !customGraphqlEndpoint ? 'appsync' : 'execute-api',
311
- region: !customGraphqlEndpoint ? region : customEndpointRegion,
312
- },
313
- },
314
- initParams
315
- );
316
-
317
- const endpoint = customGraphqlEndpoint || appSyncGraphqlEndpoint;
318
-
319
- if (!endpoint) {
320
- const error = new GraphQLError('No graphql endpoint provided.');
321
-
322
- throw {
323
- data: {},
324
- errors: [error],
325
- };
326
- }
327
-
328
- let response;
329
- try {
330
- response = await this._api.post(endpoint, init);
331
- } catch (err) {
332
- // If the exception is because user intentionally
333
- // cancelled the request, do not modify the exception
334
- // so that clients can identify the exception correctly.
335
- if (this._api.isCancel(err)) {
336
- throw err;
337
- }
338
- response = {
339
- data: {},
340
- errors: [new GraphQLError(err.message, null, null, null, null, err)],
341
- };
342
- }
343
-
344
- const { errors } = response;
345
-
346
- if (errors && errors.length) {
347
- throw response;
348
- }
349
-
350
- return response;
351
- }
352
-
353
- async createInstanceIfNotCreated() {
354
- if (!this._api) {
355
- await this.createInstance();
356
- }
357
- }
358
-
359
- /**
360
- * Checks to see if an error thrown is from an api request cancellation
361
- * @param {any} error - Any error
362
- * @return {boolean} - A boolean indicating if the error was from an api request cancellation
363
- */
364
- isCancel(error) {
365
- return this._api.isCancel(error);
366
- }
367
-
368
- /**
369
- * Cancels an inflight request. Only applicable for graphql queries and mutations
370
- * @param {any} request - request to cancel
371
- * @return {boolean} - A boolean indicating if the request was cancelled
372
- */
373
- cancel(request: Promise<any>, message?: string) {
374
- return this._api.cancel(request, message);
375
- }
376
-
377
- /**
378
- * Check if the request has a corresponding cancel token in the WeakMap.
379
- * @params request - The request promise
380
- * @return if the request has a corresponding cancel token.
381
- */
382
- hasCancelToken(request: Promise<any>) {
383
- return this._api.hasCancelToken(request);
384
- }
385
-
386
- private _graphqlSubscribe(
387
- {
388
- query,
389
- variables,
390
- authMode: defaultAuthenticationType,
391
- authToken,
392
- }: GraphQLOptions,
393
- additionalHeaders = {}
394
- ): Observable<any> {
395
- const {
396
- aws_appsync_region: region,
397
- aws_appsync_graphqlEndpoint: appSyncGraphqlEndpoint,
398
- aws_appsync_authenticationType,
399
- aws_appsync_apiKey: apiKey,
400
- graphql_headers = () => ({}),
401
- } = this._options;
402
- const authenticationType =
403
- defaultAuthenticationType || aws_appsync_authenticationType || 'AWS_IAM';
404
-
405
- if (PubSub && typeof PubSub.subscribe === 'function') {
406
- return PubSub.subscribe('', {
407
- provider: INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER,
408
- appSyncGraphqlEndpoint,
409
- authenticationType,
410
- apiKey,
411
- query: print(query as DocumentNode),
412
- region,
413
- variables,
414
- graphql_headers,
415
- additionalHeaders,
416
- authToken,
417
- });
418
- } else {
419
- logger.debug('No pubsub module applied for subscription');
420
- throw new Error('No pubsub module applied for subscription');
421
- }
422
- }
423
-
424
- /**
425
- * @private
426
- */
427
- _ensureCredentials() {
428
- return this.Credentials.get()
429
- .then(credentials => {
430
- if (!credentials) return false;
431
- const cred = this.Credentials.shear(credentials);
432
- logger.debug('set credentials for api', cred);
433
-
434
- return true;
435
- })
436
- .catch(err => {
437
- logger.warn('ensure credentials error', err);
438
- return false;
439
- });
35
+ return super.graphql(options, additionalHeaders);
440
36
  }
441
37
  }
442
38