@aws-amplify/api-graphql 4.0.18-unstable.b758843.0 → 4.0.18

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 (51) hide show
  1. package/dist/cjs/internals/APIClient.js +95 -34
  2. package/dist/cjs/internals/APIClient.js.map +1 -1
  3. package/dist/cjs/internals/generateClient.js +7 -0
  4. package/dist/cjs/internals/generateClient.js.map +1 -1
  5. package/dist/cjs/internals/generateCustomOperationsProperty.js +45 -0
  6. package/dist/cjs/internals/generateCustomOperationsProperty.js.map +1 -0
  7. package/dist/cjs/internals/operations/custom.js +289 -0
  8. package/dist/cjs/internals/operations/custom.js.map +1 -0
  9. package/dist/cjs/internals/operations/get.js +1 -1
  10. package/dist/cjs/internals/operations/get.js.map +1 -1
  11. package/dist/cjs/internals/operations/indexQuery.js +1 -1
  12. package/dist/cjs/internals/operations/indexQuery.js.map +1 -1
  13. package/dist/cjs/internals/operations/list.js +1 -1
  14. package/dist/cjs/internals/operations/list.js.map +1 -1
  15. package/dist/cjs/internals/operations/subscription.js +1 -1
  16. package/dist/cjs/internals/operations/subscription.js.map +1 -1
  17. package/dist/cjs/internals/server/generateClientWithAmplifyInstance.js +11 -1
  18. package/dist/cjs/internals/server/generateClientWithAmplifyInstance.js.map +1 -1
  19. package/dist/esm/internals/APIClient.d.ts +4 -4
  20. package/dist/esm/internals/APIClient.mjs +95 -34
  21. package/dist/esm/internals/APIClient.mjs.map +1 -1
  22. package/dist/esm/internals/generateClient.mjs +7 -0
  23. package/dist/esm/internals/generateClient.mjs.map +1 -1
  24. package/dist/esm/internals/generateCustomOperationsProperty.d.ts +8 -0
  25. package/dist/esm/internals/generateCustomOperationsProperty.mjs +41 -0
  26. package/dist/esm/internals/generateCustomOperationsProperty.mjs.map +1 -0
  27. package/dist/esm/internals/operations/custom.d.ts +60 -0
  28. package/dist/esm/internals/operations/custom.mjs +287 -0
  29. package/dist/esm/internals/operations/custom.mjs.map +1 -0
  30. package/dist/esm/internals/operations/get.mjs +1 -1
  31. package/dist/esm/internals/operations/get.mjs.map +1 -1
  32. package/dist/esm/internals/operations/indexQuery.mjs +1 -1
  33. package/dist/esm/internals/operations/indexQuery.mjs.map +1 -1
  34. package/dist/esm/internals/operations/list.mjs +1 -1
  35. package/dist/esm/internals/operations/list.mjs.map +1 -1
  36. package/dist/esm/internals/operations/subscription.mjs +1 -1
  37. package/dist/esm/internals/operations/subscription.mjs.map +1 -1
  38. package/dist/esm/internals/server/generateClientWithAmplifyInstance.mjs +12 -2
  39. package/dist/esm/internals/server/generateClientWithAmplifyInstance.mjs.map +1 -1
  40. package/dist/esm/types/index.d.ts +9 -1
  41. package/package.json +6 -6
  42. package/src/internals/APIClient.ts +138 -44
  43. package/src/internals/generateClient.ts +25 -1
  44. package/src/internals/generateCustomOperationsProperty.ts +89 -0
  45. package/src/internals/operations/custom.ts +382 -0
  46. package/src/internals/operations/get.ts +1 -1
  47. package/src/internals/operations/indexQuery.ts +1 -1
  48. package/src/internals/operations/list.ts +1 -1
  49. package/src/internals/operations/subscription.ts +3 -1
  50. package/src/internals/server/generateClientWithAmplifyInstance.ts +20 -1
  51. package/src/types/index.ts +11 -1
@@ -0,0 +1,382 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core';
4
+ import {
5
+ initializeModel,
6
+ flattenItems,
7
+ authModeParams,
8
+ getCustomHeaders,
9
+ } from '../APIClient';
10
+ import {
11
+ AuthModeParams,
12
+ ClientWithModels,
13
+ GraphQLOptionsV6,
14
+ GraphQLResult,
15
+ ListArgs,
16
+ QueryArgs,
17
+ V6Client,
18
+ V6ClientSSRRequest,
19
+ } from '../../types';
20
+ import {
21
+ ModelIntrospectionSchema,
22
+ SchemaModel,
23
+ SchemaNonModel,
24
+ CustomOperation,
25
+ CustomOperationArgument,
26
+ } from '@aws-amplify/core/internals/utils';
27
+
28
+ /**
29
+ * Builds an operation function, embedded with all client and context data, that
30
+ * can be attached to a client as a custom query or mutation.
31
+ *
32
+ * If we have this source schema:
33
+ *
34
+ * ```typescript
35
+ * a.schema({
36
+ * echo: a.query()
37
+ * .arguments({input: a.string().required()})
38
+ * .returns(a.string())
39
+ * })
40
+ * ```
41
+ *
42
+ * Our model intro schema will contain an entry like this:
43
+ *
44
+ * ```ts
45
+ * {
46
+ * queries: {
47
+ * echo: {
48
+ * name: "echo",
49
+ * isArray: false,
50
+ * type: 'String',
51
+ * isRequired: false,
52
+ * arguments: {
53
+ * input: {
54
+ * name: 'input',
55
+ * isArray: false,
56
+ * type: String,
57
+ * isRequired: true
58
+ * }
59
+ * }
60
+ * }
61
+ * }
62
+ * }
63
+ * ```
64
+ *
65
+ * The `echo` object is used to build the `echo' method that goes here:
66
+ *
67
+ * ```typescript
68
+ * const client = generateClent()
69
+ * const { data } = await client.queries.echo({input: 'a string'});
70
+ * // ^
71
+ * // |
72
+ * // +-- This one right here.
73
+ * //
74
+ * ```
75
+ *
76
+ *
77
+ * @param client The client to run graphql queries through.
78
+ * @param modelIntrospection The model introspection schema the op comes from.
79
+ * @param operationType The broad category of graphql operation.
80
+ * @param operation The operation definition from the introspection schema.
81
+ * @param useContext Whether the function needs to accept an SSR context.
82
+ * @returns The operation function to attach to query, mutations, etc.
83
+ */
84
+ export function customOpFactory(
85
+ client: ClientWithModels,
86
+ modelIntrospection: ModelIntrospectionSchema,
87
+ operationType: 'query' | 'mutation',
88
+ operation: CustomOperation,
89
+ useContext: boolean,
90
+ ) {
91
+ const opWithContext = async (
92
+ contextSpec: AmplifyServer.ContextSpec & GraphQLOptionsV6<unknown, string>,
93
+ arg?: QueryArgs,
94
+ options?: AuthModeParams & ListArgs,
95
+ ) => {
96
+ return _op(
97
+ client,
98
+ modelIntrospection,
99
+ operationType,
100
+ operation,
101
+ arg,
102
+ options,
103
+ contextSpec,
104
+ );
105
+ };
106
+
107
+ const op = async (arg?: QueryArgs, options?: AuthModeParams & ListArgs) => {
108
+ return _op(
109
+ client,
110
+ modelIntrospection,
111
+ operationType,
112
+ operation,
113
+ arg,
114
+ options,
115
+ );
116
+ };
117
+
118
+ return useContext ? opWithContext : op;
119
+ }
120
+
121
+ /**
122
+ * Runtime test and type guard to check whether `o[field]` is a `String`.
123
+ *
124
+ * ```typescript
125
+ * if (hasStringField(o, 'prop')) {
126
+ * const s = o.prop;
127
+ * // ^? const s: string
128
+ * }
129
+ * ```
130
+ *
131
+ * @param o Object to inspect
132
+ * @param field Field to look for
133
+ * @returns Boolean: `true` if the `o[field]` is a `string`
134
+ */
135
+ function hasStringField<Field extends string>(
136
+ o: any,
137
+ field: Field,
138
+ ): o is Record<Field, string> {
139
+ return typeof o[field] === 'string';
140
+ }
141
+
142
+ /**
143
+ * Generates "outer" arguments string for a custom operation. For example,
144
+ * in this operation:
145
+ *
146
+ * ```graphql
147
+ * query MyQuery(InputString: String!) {
148
+ * echoString(InputString: $InputString)
149
+ * }
150
+ * ```
151
+ *
152
+ * This function returns the top/outer level arguments as a string:
153
+ *
154
+ * ```json
155
+ * "InputString: String!"
156
+ * ```
157
+ *
158
+ * @param operation Operation object from model introspection schema.
159
+ * @returns "outer" arguments string
160
+ */
161
+ function outerArguments(operation: CustomOperation): string {
162
+ const args = Object.entries(operation.arguments)
163
+ .map(([k, v]) => {
164
+ const baseType = v.type + (v.isRequired ? '!' : '');
165
+ const finalType = v.isArray
166
+ ? `[${baseType}]${v.isArrayNullable ? '' : '!'}`
167
+ : baseType;
168
+ return `$${k}: ${finalType}`;
169
+ })
170
+ .join(', ');
171
+ return args.length > 0 ? `(${args})` : '';
172
+ }
173
+
174
+ /**
175
+ * Generates "inner" arguments string for a custom operation. For example,
176
+ * in this operation:
177
+ *
178
+ * ```graphql
179
+ * query MyQuery(InputString: String!) {
180
+ * echoString(InputString: $InputString)
181
+ * }
182
+ * ```
183
+ *
184
+ * This function returns the inner arguments as a string:
185
+ *
186
+ * ```json
187
+ * "InputString: $InputString"
188
+ * ```
189
+ *
190
+ * @param operation Operation object from model introspection schema.
191
+ * @returns "outer" arguments string
192
+ */
193
+ function innerArguments(operation: CustomOperation): string {
194
+ const args = Object.entries(operation.arguments)
195
+ .map(([k, v]) => `${k}: $${k}`)
196
+ .join(', ');
197
+ return args.length > 0 ? `(${args})` : '';
198
+ }
199
+
200
+ /**
201
+ * Accepts a Model or NonModel from generated introspection schema and
202
+ * generates the selection set string.
203
+ *
204
+ * @param nonModel Model or nonModel object from introspection schema.
205
+ * @returns String selection set.
206
+ */
207
+ function modelishTypeSelectionSet(
208
+ nonModel: SchemaModel | SchemaNonModel,
209
+ ): string {
210
+ return Object.values(nonModel.fields)
211
+ .filter(
212
+ field =>
213
+ hasStringField(field, 'type') ||
214
+ hasStringField(field.type, 'nonModel') ||
215
+ hasStringField(field.type, 'enum'),
216
+ )
217
+ .map(field => field.name)
218
+ .join(' ');
219
+ }
220
+
221
+ /**
222
+ * Generates the selection set string for a custom operation. This is slightly
223
+ * different than the selection set generation for models. If the custom op returns
224
+ * a primitive or enum types, it doen't require a selection set at all.
225
+ *
226
+ * E.g., the graphql might look like this:
227
+ *
228
+ * ```graphql
229
+ * query MyQuery {
230
+ * echoString(inputString: "whatever")
231
+ * }
232
+ * # ^
233
+ * # |
234
+ * # +-- no selection set
235
+ * ```
236
+ *
237
+ * Non-primitive return type selection set generation will be similar to other
238
+ * model operations.
239
+ *
240
+ * @param modelIntrospection The full code-generated introspection schema.
241
+ * @param operation The operation object from the schema.
242
+ * @returns The selection set as a string.
243
+ */
244
+ function operationSelectionSet(
245
+ modelIntrospection: ModelIntrospectionSchema,
246
+ operation: CustomOperation,
247
+ ): string {
248
+ if (
249
+ hasStringField(operation, 'type') ||
250
+ hasStringField(operation.type, 'enum')
251
+ ) {
252
+ return '';
253
+ } else if (hasStringField(operation.type, 'nonModel')) {
254
+ const model = modelIntrospection.nonModels[operation.type.nonModel];
255
+ return `{${modelishTypeSelectionSet(model)}}`;
256
+ } else if (hasStringField(operation.type, 'model')) {
257
+ const nonModel = modelIntrospection.models[operation.type.model];
258
+ return `{${modelishTypeSelectionSet(nonModel)}}`;
259
+ } else {
260
+ return '';
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Maps an arguments objec to graphql variables, removing superfluous args and
266
+ * screaming loudly when required args are missing.
267
+ *
268
+ * @param operation The operation to construct graphql request variables for.
269
+ * @param args The arguments to map variables from.
270
+ * @returns The graphql variables object.
271
+ */
272
+ function operationVariables(
273
+ operation: CustomOperation,
274
+ args: QueryArgs = {},
275
+ ): Record<string, unknown> {
276
+ const variables = {} as Record<string, unknown>;
277
+ for (const argDef of Object.values(operation.arguments)) {
278
+ if (typeof args[argDef.name] !== 'undefined') {
279
+ variables[argDef.name] = args[argDef.name];
280
+ } else if (argDef.isRequired) {
281
+ // At this point, the variable is both required and missing: We don't need
282
+ // to continue. The operation is expected to fail.
283
+ throw new Error(`${operation.name} requires arguments '${argDef.name}'`);
284
+ }
285
+ }
286
+ return variables;
287
+ }
288
+
289
+ /**
290
+ * Executes an operation from the given model intro schema against a client, returning
291
+ * a fully instantiated model when relevant.
292
+ *
293
+ * @param client The client to operate `graphql()` calls through.
294
+ * @param modelIntrospection The model intro schema to construct requests from.
295
+ * @param operationType The high level graphql operation type.
296
+ * @param operation The specific operation name, args, return type details.
297
+ * @param args The arguments to provide to the operation as variables.
298
+ * @param options Request options like headers, etc.
299
+ * @param context SSR context if relevant.
300
+ * @returns Result from the graphql request, model-instantiated when relevant.
301
+ */
302
+ async function _op(
303
+ client: ClientWithModels,
304
+ modelIntrospection: ModelIntrospectionSchema,
305
+ operationType: 'query' | 'mutation',
306
+ operation: CustomOperation,
307
+ args?: QueryArgs,
308
+ options?: AuthModeParams & ListArgs,
309
+ context?: AmplifyServer.ContextSpec,
310
+ ) {
311
+ const { name: operationName } = operation;
312
+ const auth = authModeParams(client, options);
313
+ const headers = getCustomHeaders(client, options?.headers);
314
+ const outerArgsString = outerArguments(operation);
315
+ const innerArgsString = innerArguments(operation);
316
+ const selectionSet = operationSelectionSet(modelIntrospection, operation);
317
+
318
+ const returnTypeModelName = hasStringField(operation.type, 'model')
319
+ ? operation.type.model
320
+ : undefined;
321
+
322
+ const query = `
323
+ ${operationType.toLocaleLowerCase()}${outerArgsString} {
324
+ ${operationName}${innerArgsString} ${selectionSet}
325
+ }
326
+ `;
327
+
328
+ const variables = operationVariables(operation, args);
329
+
330
+ try {
331
+ const { data, extensions } = context
332
+ ? ((await (client as V6ClientSSRRequest<Record<string, any>>).graphql(
333
+ context,
334
+ {
335
+ ...auth,
336
+ query,
337
+ variables,
338
+ },
339
+ headers,
340
+ )) as GraphQLResult<any>)
341
+ : ((await (client as V6Client<Record<string, any>>).graphql(
342
+ {
343
+ ...auth,
344
+ query,
345
+ variables,
346
+ },
347
+ headers,
348
+ )) as GraphQLResult<any>);
349
+
350
+ // flatten response
351
+ if (data) {
352
+ const [key] = Object.keys(data);
353
+ const flattenedResult = flattenItems(data)[key];
354
+
355
+ // TODO: custom selection set. current selection set is default selection set only
356
+ // custom selection set requires data-schema-type + runtime updates above.
357
+ const [initialized] = returnTypeModelName
358
+ ? initializeModel(
359
+ client,
360
+ returnTypeModelName,
361
+ [flattenedResult],
362
+ modelIntrospection,
363
+ auth.authMode,
364
+ auth.authToken,
365
+ !!context,
366
+ )
367
+ : [flattenedResult];
368
+
369
+ return { data: initialized, extensions };
370
+ } else {
371
+ return { data: null, extensions };
372
+ }
373
+ } catch (error: any) {
374
+ if (error.errors) {
375
+ // graphql errors pass through
376
+ return error as any;
377
+ } else {
378
+ // non-graphql errors re re-thrown
379
+ throw error;
380
+ }
381
+ }
382
+ }
@@ -67,7 +67,7 @@ async function _get(
67
67
  const { name } = model;
68
68
 
69
69
  const query = generateGraphQLDocument(
70
- modelIntrospection.models,
70
+ modelIntrospection,
71
71
  name,
72
72
  operation,
73
73
  options,
@@ -110,7 +110,7 @@ async function _indexQuery(
110
110
  const { name } = model;
111
111
 
112
112
  const query = generateGraphQLDocument(
113
- modelIntrospection.models,
113
+ modelIntrospection,
114
114
  name,
115
115
  'INDEX_QUERY',
116
116
  args,
@@ -52,7 +52,7 @@ async function _list(
52
52
  const { name } = model;
53
53
 
54
54
  const query = generateGraphQLDocument(
55
- modelIntrospection.models,
55
+ modelIntrospection,
56
56
  name,
57
57
  'LIST',
58
58
  args,
@@ -26,10 +26,12 @@ export function subscriptionFactory(
26
26
 
27
27
  const subscription = (args?: any) => {
28
28
  const query = generateGraphQLDocument(
29
- modelIntrospection.models,
29
+ modelIntrospection,
30
30
  name,
31
31
  operation,
32
+ args
32
33
  );
34
+
33
35
  const variables = buildGraphQLVariables(
34
36
  model,
35
37
  operation,
@@ -3,15 +3,23 @@
3
3
 
4
4
  import { graphql, cancel, isCancelError } from '..';
5
5
  import { generateModelsProperty } from './generateModelsProperty';
6
+ import { Hub } from '@aws-amplify/core';
6
7
  import {
7
8
  __amplify,
8
9
  __authMode,
9
10
  __authToken,
11
+ __headers,
10
12
  V6ClientSSRRequest,
11
13
  V6ClientSSRCookies,
12
14
  ServerClientGenerationParams,
13
15
  CommonPublicClientOptions,
14
16
  } from '../../types';
17
+ import { isApiGraphQLConfig } from '../utils/isApiGraphQLProviderConfig';
18
+ import { generateEnumsProperty } from '../utils/generateEnumsProperty';
19
+ import {
20
+ generateCustomMutationsProperty,
21
+ generateCustomQueriesProperty,
22
+ } from '../generateCustomOperationsProperty';
15
23
 
16
24
  /**
17
25
  * @private
@@ -36,12 +44,23 @@ export function generateClientWithAmplifyInstance<
36
44
  [__amplify]: params.amplify,
37
45
  [__authMode]: params.authMode,
38
46
  [__authToken]: params.authToken,
47
+ [__headers]: params.headers,
39
48
  graphql,
40
49
  cancel,
41
50
  isCancelError,
42
51
  } as any;
43
52
 
44
- client.models = generateModelsProperty<T>(client, params);
53
+ const apiGraphqlConfig = params.config?.API?.GraphQL;
54
+
55
+ if (isApiGraphQLConfig(apiGraphqlConfig)) {
56
+ client.models = generateModelsProperty<T>(client, params);
57
+ client.enums = generateEnumsProperty<T>(apiGraphqlConfig);
58
+ client.queries = generateCustomQueriesProperty<T>(client, apiGraphqlConfig);
59
+ client.mutations = generateCustomMutationsProperty<T>(
60
+ client,
61
+ apiGraphqlConfig,
62
+ );
63
+ }
45
64
 
46
65
  return client as ClientType;
47
66
  }
@@ -2,9 +2,11 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { AmplifyClassV6, ResourcesConfig } from '@aws-amplify/core';
4
4
  import {
5
+ EnumTypes,
5
6
  ModelTypes,
6
7
  CustomHeaders,
7
- EnumTypes,
8
+ CustomQueries,
9
+ CustomMutations,
8
10
  } from '@aws-amplify/data-schema-types';
9
11
  import { Source, DocumentNode, GraphQLError } from 'graphql';
10
12
  export { OperationTypeNode } from 'graphql';
@@ -388,6 +390,8 @@ export type V6Client<T extends Record<any, any> = never> = ExcludeNeverFields<{
388
390
  isCancelError: (error: any) => boolean;
389
391
  models: ModelTypes<T>;
390
392
  enums: EnumTypes<T>;
393
+ queries: CustomQueries<T>;
394
+ mutations: CustomMutations<T>;
391
395
  }>;
392
396
 
393
397
  export type V6ClientSSRRequest<T extends Record<any, any> = never> =
@@ -400,6 +404,9 @@ export type V6ClientSSRRequest<T extends Record<any, any> = never> =
400
404
  cancel: (promise: Promise<any>, message?: string) => boolean;
401
405
  isCancelError: (error: any) => boolean;
402
406
  models: ModelTypes<T, 'REQUEST'>;
407
+ enums: EnumTypes<T>;
408
+ queries: CustomQueries<T, 'REQUEST'>;
409
+ mutations: CustomMutations<T, 'REQUEST'>;
403
410
  }>;
404
411
 
405
412
  export type V6ClientSSRCookies<T extends Record<any, any> = never> =
@@ -412,6 +419,9 @@ export type V6ClientSSRCookies<T extends Record<any, any> = never> =
412
419
  cancel: (promise: Promise<any>, message?: string) => boolean;
413
420
  isCancelError: (error: any) => boolean;
414
421
  models: ModelTypes<T, 'COOKIES'>;
422
+ enums: EnumTypes<T>;
423
+ queries: CustomQueries<T, 'COOKIES'>;
424
+ mutations: CustomMutations<T, 'COOKIES'>;
415
425
  }>;
416
426
 
417
427
  export type GraphQLMethod = <