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