@aws-amplify/api-graphql 4.0.1-console-preview.be08038.0 → 4.0.1-server-generate-client.1e317a1.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 (80) hide show
  1. package/lib/GraphQLAPI.d.ts +2 -2
  2. package/lib/GraphQLAPI.js +4 -6
  3. package/lib/Providers/AWSAppSyncRealTimeProvider/index.d.ts +1 -1
  4. package/lib/Providers/AWSAppSyncRealTimeProvider/index.js +36 -36
  5. package/lib/Providers/constants.js +5 -4
  6. package/lib/index.js +5 -4
  7. package/lib/internals/APIClient.d.ts +101 -0
  8. package/lib/internals/APIClient.js +443 -0
  9. package/lib/internals/InternalGraphQLAPI.d.ts +2 -2
  10. package/lib/internals/InternalGraphQLAPI.js +35 -17
  11. package/lib/internals/generateClient.d.ts +12 -0
  12. package/lib/internals/generateClient.js +30 -0
  13. package/lib/internals/generateModelsProperty.d.ts +4 -0
  14. package/lib/internals/generateModelsProperty.js +169 -0
  15. package/lib/internals/index.d.ts +1 -0
  16. package/lib/internals/index.js +8 -5
  17. package/lib/internals/types.d.ts +10 -0
  18. package/lib/internals/types.js +2 -0
  19. package/lib/internals/v6.js +1 -0
  20. package/lib/tsconfig.tsbuildinfo +1 -6619
  21. package/lib/types/PubSub.d.ts +2 -2
  22. package/lib/types/PubSub.js +3 -2
  23. package/lib/types/index.d.ts +25 -22
  24. package/lib/types/index.js +4 -3
  25. package/lib/utils/ConnectionStateMonitor.d.ts +3 -3
  26. package/lib/utils/ConnectionStateMonitor.js +7 -6
  27. package/lib/utils/ReachabilityMonitor/index.d.ts +1 -1
  28. package/lib/utils/ReachabilityMonitor/index.js +3 -1
  29. package/lib/utils/ReachabilityMonitor/index.native.d.ts +1 -1
  30. package/lib/utils/ReachabilityMonitor/index.native.js +3 -1
  31. package/lib/utils/ReconnectionMonitor.js +2 -1
  32. package/lib/utils/errors/APIError.js +1 -0
  33. package/lib/utils/errors/assertValidationError.js +1 -0
  34. package/lib/utils/errors/index.js +5 -4
  35. package/lib/utils/errors/validation.js +2 -1
  36. package/lib/utils/index.js +3 -2
  37. package/lib/utils/resolveConfig.d.ts +1 -1
  38. package/lib/utils/resolveConfig.js +4 -2
  39. package/lib/utils/resolveLibraryOptions.d.ts +1 -1
  40. package/lib/utils/resolveLibraryOptions.js +3 -1
  41. package/lib/utils/resolveOwnerFields.d.ts +12 -0
  42. package/lib/utils/resolveOwnerFields.js +63 -0
  43. package/lib-esm/GraphQLAPI.d.ts +2 -2
  44. package/lib-esm/GraphQLAPI.js +1 -5
  45. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.d.ts +1 -1
  46. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js +28 -29
  47. package/lib-esm/internals/APIClient.d.ts +101 -0
  48. package/lib-esm/internals/APIClient.js +432 -0
  49. package/lib-esm/internals/InternalGraphQLAPI.d.ts +2 -2
  50. package/lib-esm/internals/InternalGraphQLAPI.js +21 -5
  51. package/lib-esm/internals/generateClient.d.ts +12 -0
  52. package/lib-esm/internals/generateClient.js +26 -0
  53. package/lib-esm/internals/generateModelsProperty.d.ts +4 -0
  54. package/lib-esm/internals/generateModelsProperty.js +165 -0
  55. package/lib-esm/internals/index.d.ts +1 -0
  56. package/lib-esm/internals/index.js +1 -0
  57. package/lib-esm/internals/types.d.ts +10 -0
  58. package/lib-esm/internals/types.js +1 -0
  59. package/lib-esm/tsconfig.tsbuildinfo +1 -6619
  60. package/lib-esm/types/PubSub.d.ts +2 -2
  61. package/lib-esm/types/index.d.ts +25 -22
  62. package/lib-esm/utils/ConnectionStateMonitor.d.ts +3 -3
  63. package/lib-esm/utils/ConnectionStateMonitor.js +2 -2
  64. package/lib-esm/utils/ReachabilityMonitor/index.d.ts +1 -1
  65. package/lib-esm/utils/ReachabilityMonitor/index.native.d.ts +1 -1
  66. package/lib-esm/utils/resolveConfig.d.ts +1 -1
  67. package/lib-esm/utils/resolveLibraryOptions.d.ts +1 -1
  68. package/lib-esm/utils/resolveOwnerFields.d.ts +12 -0
  69. package/lib-esm/utils/resolveOwnerFields.js +59 -0
  70. package/package.json +12 -6
  71. package/src/GraphQLAPI.ts +2 -6
  72. package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +2 -3
  73. package/src/internals/APIClient.ts +583 -0
  74. package/src/internals/InternalGraphQLAPI.ts +35 -9
  75. package/src/internals/generateClient.ts +30 -0
  76. package/src/internals/generateModelsProperty.ts +192 -0
  77. package/src/internals/index.ts +1 -0
  78. package/src/internals/types.ts +15 -0
  79. package/src/types/index.ts +7 -1
  80. package/src/utils/resolveOwnerFields.ts +65 -0
@@ -0,0 +1,583 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { resolveOwnerFields } from '../utils/resolveOwnerFields';
4
+
5
+ type ListArgs = { selectionSet?: string[]; filter?: {} };
6
+
7
+ const connectionType = {
8
+ HAS_ONE: 'HAS_ONE',
9
+ HAS_MANY: 'HAS_MANY',
10
+ BELONGS_TO: 'BELONGS_TO',
11
+ };
12
+
13
+ /**
14
+ *
15
+ * @param GraphQL response object
16
+ * @returns response object with `items` properties flattened
17
+ */
18
+ export const flattenItems = (obj: Record<string, any>): Record<string, any> => {
19
+ const res: Record<string, any> = {};
20
+
21
+ Object.entries(obj).forEach(([prop, value]) => {
22
+ if (typeof value === 'object' && value !== null) {
23
+ if (value.items !== undefined) {
24
+ res[prop] = value.items.map((item: Record<string, any>) =>
25
+ flattenItems(item)
26
+ );
27
+ return;
28
+ }
29
+ res[prop] = flattenItems(value);
30
+ return;
31
+ }
32
+
33
+ res[prop] = value;
34
+ });
35
+
36
+ return res;
37
+ };
38
+
39
+ // TODO: this should accept single result to support CRUD methods; create helper for array/list
40
+ export function initializeModel(
41
+ client: any,
42
+ modelName: string,
43
+ result: any[],
44
+ modelIntrospection: any
45
+ ): any[] {
46
+ const introModel = modelIntrospection.models[modelName];
47
+ const introModelFields = introModel.fields;
48
+
49
+ const modelFields: string[] = Object.entries(introModelFields)
50
+ .filter(([_, field]: [string, any]) => field?.type?.model !== undefined)
51
+ .map(([fieldName]) => fieldName);
52
+
53
+ return result.map(record => {
54
+ const initializedRelationalFields = {};
55
+
56
+ for (const field of modelFields) {
57
+ const relatedModelName = introModelFields[field].type.model;
58
+
59
+ const relatedModel = modelIntrospection.models[relatedModelName];
60
+
61
+ const relatedModelPKFieldName =
62
+ relatedModel.primaryKeyInfo.primaryKeyFieldName;
63
+
64
+ const relatedModelSKFieldNames =
65
+ relatedModel.primaryKeyInfo.sortKeyFieldNames;
66
+
67
+ const relationType = introModelFields[field].association.connectionType;
68
+ const connectionFields =
69
+ introModelFields[field].association.associatedWith;
70
+
71
+ const targetNames =
72
+ introModelFields[field].association?.targetNames || [];
73
+
74
+ switch (relationType) {
75
+ case connectionType.HAS_ONE:
76
+ case connectionType.BELONGS_TO:
77
+ const sortKeyValues = relatedModelSKFieldNames.reduce(
78
+ (acc, curVal) => {
79
+ if (record[curVal]) {
80
+ return (acc[curVal] = record[curVal]);
81
+ }
82
+ },
83
+ {}
84
+ );
85
+
86
+ initializedRelationalFields[field] = () => {
87
+ if (record[targetNames[0]]) {
88
+ return client.models[relatedModelName].get({
89
+ [relatedModelPKFieldName]: record[targetNames[0]],
90
+ ...sortKeyValues,
91
+ });
92
+ }
93
+ return undefined;
94
+ };
95
+
96
+ break;
97
+ case connectionType.HAS_MANY:
98
+ const parentPk = introModel.primaryKeyInfo.primaryKeyFieldName;
99
+ const parentSK = introModel.primaryKeyInfo.sortKeyFieldNames;
100
+
101
+ // M:N check - TODO: refactor
102
+ if (relatedModel.fields[connectionFields[0]]?.type.model) {
103
+ const relatedTargetNames =
104
+ relatedModel.fields[connectionFields[0]].association.targetNames;
105
+
106
+ const hasManyFilter = relatedTargetNames.map((field, idx) => {
107
+ if (idx === 0) {
108
+ return { [field]: { eq: record[parentPk] } };
109
+ }
110
+
111
+ return { [field]: { eq: record[parentSK[idx - 1]] } };
112
+ });
113
+
114
+ initializedRelationalFields[field] = () => {
115
+ if (record[parentPk]) {
116
+ return client.models[relatedModelName].list({
117
+ filter: { and: hasManyFilter },
118
+ });
119
+ }
120
+ return [];
121
+ };
122
+ break;
123
+ }
124
+
125
+ const hasManyFilter = connectionFields.map((field, idx) => {
126
+ if (idx === 0) {
127
+ return { [field]: { eq: record[parentPk] } };
128
+ }
129
+
130
+ return { [field]: { eq: record[parentSK[idx - 1]] } };
131
+ });
132
+
133
+ initializedRelationalFields[field] = () => {
134
+ if (record[parentPk]) {
135
+ return client.models[relatedModelName].list({
136
+ filter: { and: hasManyFilter },
137
+ });
138
+ }
139
+ return [];
140
+ };
141
+ break;
142
+ default:
143
+ break;
144
+ }
145
+ }
146
+
147
+ return { ...record, ...initializedRelationalFields };
148
+ });
149
+ }
150
+
151
+ export const graphQLOperationsInfo = {
152
+ CREATE: { operationPrefix: 'create' as const, usePlural: false },
153
+ READ: { operationPrefix: 'get' as const, usePlural: false },
154
+ UPDATE: { operationPrefix: 'update' as const, usePlural: false },
155
+ DELETE: { operationPrefix: 'delete' as const, usePlural: false },
156
+ LIST: { operationPrefix: 'list' as const, usePlural: true },
157
+ ONCREATE: { operationPrefix: 'onCreate' as const, usePlural: false },
158
+ ONUPDATE: { operationPrefix: 'onUpdate' as const, usePlural: false },
159
+ ONDELETE: { operationPrefix: 'onDelete' as const, usePlural: false },
160
+ };
161
+ export type ModelOperation = keyof typeof graphQLOperationsInfo;
162
+
163
+ type OperationPrefix =
164
+ (typeof graphQLOperationsInfo)[ModelOperation]['operationPrefix'];
165
+
166
+ const graphQLDocumentsCache = new Map<string, Map<ModelOperation, string>>();
167
+ const SELECTION_SET_WILDCARD = '*';
168
+
169
+ function defaultSelectionSetForModel(modelDefinition: any): string[] {
170
+ // fields that are explicitly part of the graphql schema; not
171
+ // inferred from owner auth rules.
172
+ const { fields } = modelDefinition;
173
+ const explicitFields = Object.values<any>(fields)
174
+ .map(({ type, name }) => typeof type === 'string' && name) // Default selection set omits model fields
175
+ .filter(Boolean);
176
+
177
+ // fields used for owner auth rules that may or may not also
178
+ // be explicit on the model.
179
+ const ownerFields = resolveOwnerFields(modelDefinition);
180
+
181
+ return Array.from(new Set(explicitFields.concat(ownerFields)));
182
+ }
183
+
184
+ const FIELD_IR = '';
185
+
186
+ /**
187
+ * Generates nested Custom Selection Set IR from path
188
+ *
189
+ * @param modelIntrospection
190
+ * @param modelName
191
+ * @param selectionSet - array of object paths
192
+ * @example
193
+ * ### Given
194
+ * `selectionSet = ['id', 'comments.post.id']`
195
+ * ### Returns
196
+ * ```ts
197
+ * {
198
+ * id: '',
199
+ * comments: {
200
+ * items: { post: { id: '' } }
201
+ * }
202
+ * }
203
+ * ```
204
+ */
205
+ export function customSelectionSetToIR(
206
+ modelIntrospection: any,
207
+ modelName: string,
208
+ selectionSet: string[]
209
+ ): Record<string, string | object> {
210
+ const modelDefinition = modelIntrospection[modelName];
211
+ const { fields: modelFields } = modelDefinition;
212
+
213
+ return selectionSet.reduce((resultObj, path) => {
214
+ const [fieldName, nested, ...rest] = path.split('.');
215
+
216
+ if (nested) {
217
+ const relatedModel = modelFields[fieldName]?.type?.model;
218
+
219
+ if (!relatedModel) {
220
+ // TODO: may need to change this to support custom types
221
+ throw Error(`${fieldName} is not a model field`);
222
+ }
223
+
224
+ const relatedModelDefinition = modelIntrospection[relatedModel];
225
+
226
+ const selectionSet =
227
+ nested === SELECTION_SET_WILDCARD
228
+ ? defaultSelectionSetIR(relatedModelDefinition)
229
+ : // if we have a path like 'field.anotherField' recursively build up selection set IR
230
+ customSelectionSetToIR(modelIntrospection, relatedModel, [
231
+ [nested, ...rest].join('.'),
232
+ ]);
233
+
234
+ if (modelFields[fieldName]?.isArray) {
235
+ const existing = resultObj[fieldName] || {
236
+ items: {},
237
+ };
238
+ const merged = { ...existing.items, ...selectionSet };
239
+
240
+ resultObj[fieldName] = { items: merged };
241
+ return resultObj;
242
+ }
243
+
244
+ const existingItems = resultObj[fieldName] || {};
245
+ const merged = { ...existingItems, ...selectionSet };
246
+
247
+ resultObj[fieldName] = merged;
248
+ return resultObj;
249
+ }
250
+
251
+ const exists = Boolean(modelFields[fieldName]);
252
+
253
+ if (!exists) {
254
+ throw Error(`${fieldName} is not a field of model ${modelName}`);
255
+ }
256
+
257
+ resultObj[fieldName] = FIELD_IR;
258
+ return resultObj;
259
+ }, {});
260
+ }
261
+
262
+ const defaultSelectionSetIR = relatedModelDefinition => {
263
+ const defaultSelectionSet = defaultSelectionSetForModel(
264
+ relatedModelDefinition
265
+ );
266
+
267
+ const reduced = defaultSelectionSet.reduce((acc, curVal) => {
268
+ acc[curVal] = FIELD_IR;
269
+ return acc;
270
+ }, {});
271
+
272
+ return reduced;
273
+ };
274
+
275
+ /**
276
+ * Stringifies selection set IR
277
+ * * @example
278
+ * ### Given
279
+ * ```ts
280
+ * {
281
+ * id: '',
282
+ * comments: {
283
+ * items: { post: { id: '' } }
284
+ * }
285
+ * }
286
+ * ```
287
+ * ### Returns
288
+ * `'id comments { items { post { id } } }'`
289
+ */
290
+ export function selectionSetIRToString(
291
+ obj: Record<string, string | any>
292
+ ): string {
293
+ const res: string[] = [];
294
+
295
+ Object.entries(obj).forEach(([fieldName, value]) => {
296
+ if (value === FIELD_IR) {
297
+ res.push(fieldName);
298
+ } else if (typeof value === 'object' && value !== null) {
299
+ if (value?.items) {
300
+ res.push(
301
+ fieldName,
302
+ '{',
303
+ 'items',
304
+ '{',
305
+ selectionSetIRToString(value.items),
306
+ '}',
307
+ '}'
308
+ );
309
+ } else {
310
+ res.push(fieldName, '{', selectionSetIRToString(value), '}');
311
+ }
312
+ }
313
+ });
314
+
315
+ return res.join(' ');
316
+ }
317
+
318
+ export function generateSelectionSet(
319
+ modelIntrospection: any,
320
+ modelName: string,
321
+ selectionSet?: string[]
322
+ ) {
323
+ const modelDefinition = modelIntrospection[modelName];
324
+
325
+ if (!selectionSet) {
326
+ return defaultSelectionSetForModel(modelDefinition).join(' ');
327
+ }
328
+
329
+ const selSetIr = customSelectionSetToIR(
330
+ modelIntrospection,
331
+ modelName,
332
+ selectionSet
333
+ );
334
+ const selSetString = selectionSetIRToString(selSetIr);
335
+
336
+ return selSetString;
337
+ }
338
+
339
+ export function generateGraphQLDocument(
340
+ modelIntrospection: any,
341
+ modelName: string,
342
+ modelOperation: ModelOperation,
343
+ listArgs?: ListArgs
344
+ ): string {
345
+ const modelDefinition = modelIntrospection[modelName];
346
+
347
+ const {
348
+ name,
349
+ pluralName,
350
+ fields,
351
+ primaryKeyInfo: {
352
+ isCustomPrimaryKey,
353
+ primaryKeyFieldName,
354
+ sortKeyFieldNames,
355
+ },
356
+ } = modelDefinition;
357
+
358
+ const { operationPrefix, usePlural } = graphQLOperationsInfo[modelOperation];
359
+
360
+ const { selectionSet } = listArgs || {};
361
+
362
+ const fromCache = graphQLDocumentsCache.get(name)?.get(modelOperation);
363
+
364
+ if (fromCache !== undefined) {
365
+ return fromCache;
366
+ }
367
+
368
+ if (!graphQLDocumentsCache.has(name)) {
369
+ graphQLDocumentsCache.set(name, new Map());
370
+ }
371
+
372
+ const graphQLFieldName = `${operationPrefix}${usePlural ? pluralName : name}`;
373
+ let graphQLOperationType: 'mutation' | 'query' | 'subscription' | undefined;
374
+ let graphQLSelectionSet: string | undefined;
375
+ let graphQLArguments: Record<string, any> | undefined;
376
+
377
+ const selectionSetFields = generateSelectionSet(
378
+ modelIntrospection,
379
+ modelName,
380
+ selectionSet
381
+ );
382
+
383
+ switch (modelOperation) {
384
+ case 'CREATE':
385
+ case 'UPDATE':
386
+ case 'DELETE':
387
+ graphQLArguments ??
388
+ (graphQLArguments = {
389
+ input: `${
390
+ operationPrefix.charAt(0).toLocaleUpperCase() +
391
+ operationPrefix.slice(1)
392
+ }${name}Input!`,
393
+ });
394
+ graphQLOperationType ?? (graphQLOperationType = 'mutation');
395
+ case 'READ':
396
+ graphQLArguments ??
397
+ (graphQLArguments = isCustomPrimaryKey
398
+ ? [primaryKeyFieldName, ...sortKeyFieldNames].reduce(
399
+ (acc, fieldName) => {
400
+ acc[fieldName] = fields[fieldName].type;
401
+
402
+ return acc;
403
+ },
404
+ {}
405
+ )
406
+ : {
407
+ [primaryKeyFieldName]: `${fields[primaryKeyFieldName].type}!`,
408
+ });
409
+ graphQLSelectionSet ?? (graphQLSelectionSet = selectionSetFields);
410
+ case 'LIST':
411
+ graphQLArguments ??
412
+ (graphQLArguments = {
413
+ filter: `Model${name}FilterInput`,
414
+ });
415
+ graphQLOperationType ?? (graphQLOperationType = 'query');
416
+ graphQLSelectionSet ??
417
+ (graphQLSelectionSet = `items { ${selectionSetFields} }`);
418
+ case 'ONCREATE':
419
+ case 'ONUPDATE':
420
+ case 'ONDELETE':
421
+ graphQLArguments ??
422
+ (graphQLArguments = {
423
+ filter: `ModelSubscription${name}FilterInput`,
424
+ });
425
+ graphQLOperationType ?? (graphQLOperationType = 'subscription');
426
+ graphQLSelectionSet ?? (graphQLSelectionSet = selectionSetFields);
427
+ }
428
+
429
+ const graphQLDocument = `${graphQLOperationType}${
430
+ graphQLArguments
431
+ ? `(${Object.entries(graphQLArguments).map(
432
+ ([fieldName, type]) => `\$${fieldName}: ${type}`
433
+ )})`
434
+ : ''
435
+ } { ${graphQLFieldName}${
436
+ graphQLArguments
437
+ ? `(${Object.keys(graphQLArguments).map(
438
+ fieldName => `${fieldName}: \$${fieldName}`
439
+ )})`
440
+ : ''
441
+ } { ${graphQLSelectionSet} } }`;
442
+
443
+ graphQLDocumentsCache.get(name)?.set(modelOperation, graphQLDocument);
444
+
445
+ return graphQLDocument;
446
+ }
447
+
448
+ export function buildGraphQLVariables(
449
+ modelDefinition: any,
450
+ operation: ModelOperation,
451
+ arg: any,
452
+ modelIntrospection
453
+ ): object {
454
+ const {
455
+ fields,
456
+ primaryKeyInfo: {
457
+ isCustomPrimaryKey,
458
+ primaryKeyFieldName,
459
+ sortKeyFieldNames,
460
+ },
461
+ } = modelDefinition;
462
+
463
+ let variables = {};
464
+
465
+ // TODO: process input
466
+ switch (operation) {
467
+ case 'CREATE':
468
+ variables = {
469
+ input: normalizeMutationInput(arg, modelDefinition, modelIntrospection),
470
+ };
471
+ break;
472
+ case 'UPDATE':
473
+ // readonly fields are not updated
474
+ variables = {
475
+ input: Object.fromEntries(
476
+ Object.entries(
477
+ normalizeMutationInput(arg, modelDefinition, modelIntrospection)
478
+ ).filter(([fieldName]) => {
479
+ const { isReadOnly } = fields[fieldName];
480
+
481
+ return !isReadOnly;
482
+ })
483
+ ),
484
+ };
485
+ break;
486
+ case 'READ':
487
+ case 'DELETE':
488
+ // only identifiers are sent
489
+ variables = isCustomPrimaryKey
490
+ ? [primaryKeyFieldName, ...sortKeyFieldNames].reduce(
491
+ (acc, fieldName) => {
492
+ acc[fieldName] = arg[fieldName];
493
+
494
+ return acc;
495
+ },
496
+ {}
497
+ )
498
+ : { [primaryKeyFieldName]: arg[primaryKeyFieldName] };
499
+
500
+ if (operation === 'DELETE') {
501
+ variables = { input: variables };
502
+ }
503
+ break;
504
+ case 'LIST':
505
+ case 'ONCREATE':
506
+ case 'ONUPDATE':
507
+ case 'ONDELETE':
508
+ if (arg?.filter) {
509
+ variables = { filter: arg.filter };
510
+ }
511
+ break;
512
+ default:
513
+ const exhaustiveCheck: never = operation;
514
+ throw new Error(`Unhandled operation case: ${exhaustiveCheck}`);
515
+ }
516
+
517
+ return variables;
518
+ }
519
+
520
+ /**
521
+ * Iterates over mutation input values and resolves any model inputs to their corresponding join fields/values
522
+ *
523
+ * @example
524
+ * ### Usage
525
+ * ```ts
526
+ * const result = normalizeMutationInput({ post: post }, model, modelDefinition);
527
+ * ```
528
+ * ### Result
529
+ * ```ts
530
+ * { postId: "abc123" }
531
+ * ```
532
+ *
533
+ */
534
+ export function normalizeMutationInput(
535
+ mutationInput: any,
536
+ model: any,
537
+ modelDefinition: any
538
+ ): Record<string, unknown> {
539
+ const { fields } = model;
540
+
541
+ const normalized = {};
542
+
543
+ Object.entries(mutationInput).forEach(([inputFieldName, inputValue]) => {
544
+ const relatedModelName: string | undefined =
545
+ fields[inputFieldName]?.type?.model;
546
+
547
+ if (relatedModelName) {
548
+ const association = fields[inputFieldName]?.association;
549
+ const relatedModelDef = modelDefinition.models[relatedModelName];
550
+ const relatedModelPkInfo = relatedModelDef.primaryKeyInfo;
551
+
552
+ if (association.connectionType === connectionType.HAS_ONE) {
553
+ association.targetNames.forEach((targetName, idx) => {
554
+ const associatedFieldName = association.associatedWith[idx];
555
+ normalized[targetName] = (inputValue as Record<string, unknown>)[
556
+ associatedFieldName
557
+ ];
558
+ });
559
+ }
560
+
561
+ if (association.connectionType === connectionType.BELONGS_TO) {
562
+ association.targetNames.forEach((targetName, idx) => {
563
+ if (idx === 0) {
564
+ const associatedFieldName = relatedModelPkInfo.primaryKeyFieldName;
565
+ normalized[targetName] = (inputValue as Record<string, unknown>)[
566
+ associatedFieldName
567
+ ];
568
+ } else {
569
+ const associatedFieldName =
570
+ relatedModelPkInfo.sortKeyFieldNames[idx - 1];
571
+ normalized[targetName] = (inputValue as Record<string, unknown>)[
572
+ associatedFieldName
573
+ ];
574
+ }
575
+ });
576
+ }
577
+ } else {
578
+ normalized[inputFieldName] = inputValue;
579
+ }
580
+ });
581
+
582
+ return normalized;
583
+ }
@@ -45,6 +45,13 @@ export const graphqlOperation = (
45
45
  authToken,
46
46
  });
47
47
 
48
+ const isAmplifyInstance = (amplify): amplify is AmplifyClassV6 => {
49
+ if (typeof amplify !== 'function') {
50
+ return true;
51
+ }
52
+ return false;
53
+ };
54
+
48
55
  /**
49
56
  * Export Cloud Logic APIs
50
57
  */
@@ -163,7 +170,9 @@ export class InternalGraphQLAPIClass {
163
170
  * @returns An Observable if the query is a subscription query, else a promise of the graphql result.
164
171
  */
165
172
  graphql<T = any>(
166
- amplify: AmplifyClassV6,
173
+ amplify:
174
+ | AmplifyClassV6
175
+ | ((fn: (amplify: any) => Promise<any>) => Promise<AmplifyClassV6>),
167
176
  { query: paramQuery, variables = {}, authMode, authToken }: GraphQLOptions,
168
177
  additionalHeaders?: { [key: string]: string },
169
178
  customUserAgentDetails?: CustomUserAgentDetails
@@ -190,13 +199,30 @@ export class InternalGraphQLAPIClass {
190
199
  case 'query':
191
200
  case 'mutation':
192
201
  const abortController = new AbortController();
193
- const responsePromise = this._graphql<T>(
194
- amplify,
195
- { query, variables, authMode },
196
- headers,
197
- abortController,
198
- customUserAgentDetails
199
- );
202
+
203
+ let responsePromise;
204
+
205
+ if (isAmplifyInstance(amplify)) {
206
+ responsePromise = this._graphql<T>(
207
+ amplify,
208
+ { query, variables, authMode },
209
+ headers,
210
+ abortController,
211
+ customUserAgentDetails
212
+ );
213
+ } else {
214
+ const wrapper = amplifyInstance =>
215
+ this._graphql<T>(
216
+ amplifyInstance,
217
+ { query, variables, authMode },
218
+ headers,
219
+ abortController,
220
+ customUserAgentDetails
221
+ );
222
+
223
+ responsePromise = amplify(wrapper);
224
+ }
225
+
200
226
  this._api.updateRequestToBeCancellable(
201
227
  responsePromise,
202
228
  abortController
@@ -204,7 +230,7 @@ export class InternalGraphQLAPIClass {
204
230
  return responsePromise;
205
231
  case 'subscription':
206
232
  return this._graphqlSubscribe(
207
- amplify,
233
+ amplify as AmplifyClassV6,
208
234
  { query, variables, authMode },
209
235
  headers,
210
236
  customUserAgentDetails
@@ -0,0 +1,30 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { graphql, cancel, isCancelError } from './v6';
4
+ import { generateModelsProperty } from './generateModelsProperty';
5
+ import { V6Client, __amplify } from '../types';
6
+ import { ClientGenerationParams } from './types';
7
+
8
+ /**
9
+ * @private
10
+ *
11
+ * Creates a client that can be used to make GraphQL requests, using a provided `AmplifyClassV6`
12
+ * compatible context object for config and auth fetching.
13
+ *
14
+ * @param params
15
+ * @returns
16
+ */
17
+ export function generateClient<T extends Record<any, any> = never>(
18
+ params: ClientGenerationParams
19
+ ): V6Client<T> {
20
+ const client = {
21
+ [__amplify]: params.amplify,
22
+ graphql,
23
+ cancel,
24
+ isCancelError,
25
+ models: {},
26
+ } as any;
27
+ client.models = generateModelsProperty<T>(client, params);
28
+
29
+ return client as V6Client<T>;
30
+ }