@aws-amplify/data-schema 1.2.2 → 1.2.4
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/runtime/internals/APIClient.js +117 -26
- package/dist/cjs/runtime/internals/APIClient.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/custom.js +10 -2
- package/dist/cjs/runtime/internals/operations/custom.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/get.js +6 -3
- package/dist/cjs/runtime/internals/operations/get.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/indexQuery.js +5 -4
- package/dist/cjs/runtime/internals/operations/indexQuery.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/list.js +2 -2
- package/dist/cjs/runtime/internals/operations/list.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/subscription.js +1 -0
- package/dist/cjs/runtime/internals/operations/subscription.js.map +1 -1
- package/dist/esm/runtime/internals/APIClient.d.ts +41 -3
- package/dist/esm/runtime/internals/APIClient.mjs +117 -26
- package/dist/esm/runtime/internals/APIClient.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/custom.mjs +11 -3
- package/dist/esm/runtime/internals/operations/custom.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/get.mjs +6 -3
- package/dist/esm/runtime/internals/operations/get.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/indexQuery.mjs +5 -4
- package/dist/esm/runtime/internals/operations/indexQuery.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/list.mjs +2 -2
- package/dist/esm/runtime/internals/operations/list.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/subscription.mjs +1 -0
- package/dist/esm/runtime/internals/operations/subscription.mjs.map +1 -1
- package/dist/meta/cjs.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/runtime/internals/APIClient.ts +151 -48
- package/src/runtime/internals/operations/custom.ts +12 -3
- package/src/runtime/internals/operations/get.ts +5 -3
- package/src/runtime/internals/operations/indexQuery.ts +12 -2
- package/src/runtime/internals/operations/list.ts +8 -2
- package/src/runtime/internals/operations/subscription.ts +1 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
GraphQLAuthMode,
|
|
12
12
|
ClientInternalsGetter,
|
|
13
13
|
ListArgs,
|
|
14
|
+
Field,
|
|
14
15
|
ModelFieldType,
|
|
15
16
|
ModelIntrospectionSchema,
|
|
16
17
|
NonModelFieldType,
|
|
@@ -72,32 +73,117 @@ const resolvedSkName = (sk: string[]): string => {
|
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
/**
|
|
76
|
+
* Crawls a model tree, starting with a given **individual** model instance record, looking
|
|
77
|
+
* for related hasMany children to extract from their `items` containers.
|
|
75
78
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
79
|
+
* E.g., if we have a record like this:
|
|
80
|
+
*
|
|
81
|
+
* ```js
|
|
82
|
+
* {
|
|
83
|
+
* id: 'some-id',
|
|
84
|
+
* children: {
|
|
85
|
+
* items: [
|
|
86
|
+
* { name: 'a' }
|
|
87
|
+
* { name: 'b' }
|
|
88
|
+
* { name: 'c' }
|
|
89
|
+
* ]
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* And if `children` refers to *an array of another model* (as opposed to a custom type),
|
|
95
|
+
* the `items` will be extracted. We do this because `items` is just the mechanism for nesting
|
|
96
|
+
* child records -- we don't want customers to have to dig the items out in application code.
|
|
97
|
+
* Ultimately, we return this "flattened" structure:
|
|
98
|
+
*
|
|
99
|
+
* ```js
|
|
100
|
+
* {
|
|
101
|
+
* id: 'some-id',
|
|
102
|
+
* children: [
|
|
103
|
+
* { name: 'a' }
|
|
104
|
+
* { name: 'b' }
|
|
105
|
+
* { name: 'c' }
|
|
106
|
+
* ]
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* Notably, an identical record could be the result of a nested custom type that contains an
|
|
111
|
+
* `items` property. This will *not* be flattened, because in that case the `items` property is
|
|
112
|
+
* actually part of the customer's schema. Similarly if a model contains an explicit `items` field.
|
|
113
|
+
*
|
|
114
|
+
* @param modelIntrospection Top-level model introspection schema.
|
|
115
|
+
* @param modelName The name of the model. Can be `undefined`. E.g., for customOperation return types.
|
|
116
|
+
* @param modelRecord The individual "model instance record" to normalize.
|
|
78
117
|
*/
|
|
79
|
-
export const flattenItems = (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
export const flattenItems = (
|
|
119
|
+
modelIntrospection: ModelIntrospectionSchema,
|
|
120
|
+
modelName: string | undefined,
|
|
121
|
+
modelRecord: Record<string, any>,
|
|
122
|
+
): Record<string, any> | null => {
|
|
123
|
+
if (!modelRecord) return null;
|
|
124
|
+
|
|
125
|
+
const mapped = {} as Record<string, any>;
|
|
126
|
+
for (const [fieldName, value] of Object.entries(modelRecord)) {
|
|
127
|
+
const fieldDef = modelName
|
|
128
|
+
? modelIntrospection.models[modelName]?.fields[fieldName]
|
|
129
|
+
: undefined;
|
|
130
|
+
const dvPair = { fieldDef, value };
|
|
131
|
+
if (isRelatedModelItemsArrayPair(dvPair)) {
|
|
132
|
+
mapped[fieldName] = dvPair.value.items.map((itemValue) =>
|
|
133
|
+
flattenItems(modelIntrospection, dvPair.fieldDef.type.model, itemValue),
|
|
134
|
+
);
|
|
135
|
+
} else if (isRelatedModelProperty(fieldDef)) {
|
|
136
|
+
mapped[fieldName] = flattenItems(
|
|
137
|
+
modelIntrospection,
|
|
138
|
+
fieldDef.type.model,
|
|
139
|
+
value,
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
mapped[fieldName] = value;
|
|
94
143
|
}
|
|
144
|
+
}
|
|
145
|
+
return mapped;
|
|
146
|
+
};
|
|
95
147
|
|
|
96
|
-
|
|
97
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Determines whether the given field definition and associated result value
|
|
150
|
+
* represent a related model array from a HasMany-type relationship.
|
|
151
|
+
*
|
|
152
|
+
* @param dv Pair of field definition and associated result value
|
|
153
|
+
* @returns
|
|
154
|
+
*/
|
|
155
|
+
function isRelatedModelItemsArrayPair(dv: {
|
|
156
|
+
fieldDef: Field | undefined;
|
|
157
|
+
value: any;
|
|
158
|
+
}): dv is {
|
|
159
|
+
fieldDef: Field & { type: ModelFieldType };
|
|
160
|
+
value: { items: Record<string, any>[] };
|
|
161
|
+
} {
|
|
162
|
+
return (
|
|
163
|
+
typeof dv.fieldDef?.type === 'object' &&
|
|
164
|
+
'model' in dv.fieldDef.type &&
|
|
165
|
+
typeof dv.fieldDef.type.model === 'string' &&
|
|
166
|
+
dv.fieldDef.isArray &&
|
|
167
|
+
Array.isArray(dv.value?.items)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
98
170
|
|
|
99
|
-
|
|
100
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Determines whether the given field definition represents a relationship
|
|
173
|
+
* to another model.
|
|
174
|
+
*
|
|
175
|
+
* @param fieldDef
|
|
176
|
+
* @returns
|
|
177
|
+
*/
|
|
178
|
+
function isRelatedModelProperty(
|
|
179
|
+
fieldDef: Field | undefined,
|
|
180
|
+
): fieldDef is Field & { type: ModelFieldType } {
|
|
181
|
+
return (
|
|
182
|
+
typeof fieldDef?.type === 'object' &&
|
|
183
|
+
'model' in fieldDef.type &&
|
|
184
|
+
typeof fieldDef.type.model === 'string'
|
|
185
|
+
);
|
|
186
|
+
}
|
|
101
187
|
|
|
102
188
|
// TODO: this should accept single result to support CRUD methods; create helper for array/list
|
|
103
189
|
export function initializeModel(
|
|
@@ -148,7 +234,6 @@ export function initializeModel(
|
|
|
148
234
|
}
|
|
149
235
|
|
|
150
236
|
switch (relationType) {
|
|
151
|
-
case connectionType.HAS_ONE:
|
|
152
237
|
case connectionType.BELONGS_TO: {
|
|
153
238
|
const sortKeyValues = relatedModelSKFieldNames.reduce(
|
|
154
239
|
// TODO(Eslint): is this implementation correct?
|
|
@@ -180,7 +265,7 @@ export function initializeModel(
|
|
|
180
265
|
);
|
|
181
266
|
}
|
|
182
267
|
|
|
183
|
-
return
|
|
268
|
+
return { data: null };
|
|
184
269
|
};
|
|
185
270
|
} else {
|
|
186
271
|
initializedRelationalFields[fieldName] = (
|
|
@@ -199,13 +284,29 @@ export function initializeModel(
|
|
|
199
284
|
);
|
|
200
285
|
}
|
|
201
286
|
|
|
202
|
-
return
|
|
287
|
+
return { data: null };
|
|
203
288
|
};
|
|
204
289
|
}
|
|
205
290
|
|
|
206
291
|
break;
|
|
207
292
|
}
|
|
293
|
+
case connectionType.HAS_ONE:
|
|
208
294
|
case connectionType.HAS_MANY: {
|
|
295
|
+
/**
|
|
296
|
+
* If the loader is a HAS_ONE, we just need to attempt to grab the first item
|
|
297
|
+
* from the result.
|
|
298
|
+
*/
|
|
299
|
+
const mapResult =
|
|
300
|
+
relationType === connectionType.HAS_ONE
|
|
301
|
+
? (result: Record<string, any>) => {
|
|
302
|
+
return {
|
|
303
|
+
data: result?.data.shift() || null,
|
|
304
|
+
errors: result.errors,
|
|
305
|
+
extensions: result.extensions,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
: (result: Record<string, any>) => result;
|
|
309
|
+
|
|
209
310
|
const parentPk = introModel.primaryKeyInfo.primaryKeyFieldName;
|
|
210
311
|
const parentSK = introModel.primaryKeyInfo.sortKeyFieldNames;
|
|
211
312
|
|
|
@@ -238,16 +339,15 @@ export function initializeModel(
|
|
|
238
339
|
options?: LazyLoadOptions,
|
|
239
340
|
) => {
|
|
240
341
|
if (record[parentPk]) {
|
|
241
|
-
return (client as any).models[relatedModelName]
|
|
242
|
-
contextSpec,
|
|
243
|
-
{
|
|
342
|
+
return (client as any).models[relatedModelName]
|
|
343
|
+
.list(contextSpec, {
|
|
244
344
|
filter: { and: hasManyFilter },
|
|
245
345
|
limit: options?.limit,
|
|
246
346
|
nextToken: options?.nextToken,
|
|
247
347
|
authMode: options?.authMode || authMode,
|
|
248
348
|
authToken: options?.authToken || authToken,
|
|
249
|
-
}
|
|
250
|
-
|
|
349
|
+
})
|
|
350
|
+
.then(mapResult);
|
|
251
351
|
}
|
|
252
352
|
|
|
253
353
|
return [];
|
|
@@ -257,13 +357,15 @@ export function initializeModel(
|
|
|
257
357
|
options?: LazyLoadOptions,
|
|
258
358
|
) => {
|
|
259
359
|
if (record[parentPk]) {
|
|
260
|
-
return (client as any).models[relatedModelName]
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
360
|
+
return (client as any).models[relatedModelName]
|
|
361
|
+
.list({
|
|
362
|
+
filter: { and: hasManyFilter },
|
|
363
|
+
limit: options?.limit,
|
|
364
|
+
nextToken: options?.nextToken,
|
|
365
|
+
authMode: options?.authMode || authMode,
|
|
366
|
+
authToken: options?.authToken || authToken,
|
|
367
|
+
})
|
|
368
|
+
.then(mapResult);
|
|
267
369
|
}
|
|
268
370
|
|
|
269
371
|
return [];
|
|
@@ -289,16 +391,15 @@ export function initializeModel(
|
|
|
289
391
|
options?: LazyLoadOptions,
|
|
290
392
|
) => {
|
|
291
393
|
if (record[parentPk]) {
|
|
292
|
-
return (client as any).models[relatedModelName]
|
|
293
|
-
contextSpec,
|
|
294
|
-
{
|
|
394
|
+
return (client as any).models[relatedModelName]
|
|
395
|
+
.list(contextSpec, {
|
|
295
396
|
filter: { and: hasManyFilter },
|
|
296
397
|
limit: options?.limit,
|
|
297
398
|
nextToken: options?.nextToken,
|
|
298
399
|
authMode: options?.authMode || authMode,
|
|
299
400
|
authToken: options?.authToken || authToken,
|
|
300
|
-
}
|
|
301
|
-
|
|
401
|
+
})
|
|
402
|
+
.then(mapResult);
|
|
302
403
|
}
|
|
303
404
|
|
|
304
405
|
return [];
|
|
@@ -308,13 +409,15 @@ export function initializeModel(
|
|
|
308
409
|
options?: LazyLoadOptions,
|
|
309
410
|
) => {
|
|
310
411
|
if (record[parentPk]) {
|
|
311
|
-
return (client as any).models[relatedModelName]
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
412
|
+
return (client as any).models[relatedModelName]
|
|
413
|
+
.list({
|
|
414
|
+
filter: { and: hasManyFilter },
|
|
415
|
+
limit: options?.limit,
|
|
416
|
+
nextToken: options?.nextToken,
|
|
417
|
+
authMode: options?.authMode || authMode,
|
|
418
|
+
authToken: options?.authToken || authToken,
|
|
419
|
+
})
|
|
420
|
+
.then(mapResult);
|
|
318
421
|
}
|
|
319
422
|
|
|
320
423
|
return [];
|
|
@@ -20,7 +20,6 @@ import { map } from 'rxjs';
|
|
|
20
20
|
import {
|
|
21
21
|
authModeParams,
|
|
22
22
|
getDefaultSelectionSetForNonModelWithIR,
|
|
23
|
-
flattenItems,
|
|
24
23
|
generateSelectionSet,
|
|
25
24
|
getCustomHeaders,
|
|
26
25
|
initializeModel,
|
|
@@ -398,7 +397,12 @@ async function _op(
|
|
|
398
397
|
// flatten response
|
|
399
398
|
if (data) {
|
|
400
399
|
const [key] = Object.keys(data);
|
|
401
|
-
|
|
400
|
+
|
|
401
|
+
// TODO: when adding support for custom selection set, flattening will need
|
|
402
|
+
// to occur recursively. For now, it's expected that related models are not
|
|
403
|
+
// present in the result. Only FK's are present. Any related model properties
|
|
404
|
+
// should be replaced with lazy loaders under the current implementation.
|
|
405
|
+
const flattenedResult = data[key];
|
|
402
406
|
|
|
403
407
|
// TODO: custom selection set. current selection set is default selection set only
|
|
404
408
|
// custom selection set requires data-schema-type + runtime updates above.
|
|
@@ -433,7 +437,12 @@ async function _op(
|
|
|
433
437
|
*/
|
|
434
438
|
if (data && Object.keys(data).length !== 0 && errors) {
|
|
435
439
|
const [key] = Object.keys(data);
|
|
436
|
-
|
|
440
|
+
|
|
441
|
+
// TODO: when adding support for custom selection set, flattening will need
|
|
442
|
+
// to occur recursively. For now, it's expected that related models are not
|
|
443
|
+
// present in the result. Only FK's are present. Any related model properties
|
|
444
|
+
// should be replaced with lazy loaders under the current implementation.
|
|
445
|
+
const flattenedResult = data[key];
|
|
437
446
|
|
|
438
447
|
/**
|
|
439
448
|
* `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
|
|
@@ -119,9 +119,11 @@ async function _get(
|
|
|
119
119
|
// flatten response
|
|
120
120
|
if (data) {
|
|
121
121
|
const [key] = Object.keys(data);
|
|
122
|
-
const flattenedResult = flattenItems(data
|
|
122
|
+
const flattenedResult = flattenItems(modelIntrospection, name, data[key]);
|
|
123
123
|
|
|
124
|
-
if (
|
|
124
|
+
if (flattenedResult === null) {
|
|
125
|
+
return { data: null, extensions };
|
|
126
|
+
} else if (options?.selectionSet) {
|
|
125
127
|
return { data: flattenedResult, extensions };
|
|
126
128
|
} else {
|
|
127
129
|
// TODO: refactor to avoid destructuring here
|
|
@@ -155,7 +157,7 @@ async function _get(
|
|
|
155
157
|
*/
|
|
156
158
|
if (data && Object.keys(data).length !== 0 && errors) {
|
|
157
159
|
const [key] = Object.keys(data);
|
|
158
|
-
const flattenedResult = flattenItems(data
|
|
160
|
+
const flattenedResult = flattenItems(modelIntrospection, name, data[key]);
|
|
159
161
|
|
|
160
162
|
/**
|
|
161
163
|
* `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
|
|
@@ -74,6 +74,8 @@ export function indexQueryFactory(
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function processGraphQlResponse(
|
|
77
|
+
modelIntroSchema: ModelIntrospectionSchema,
|
|
78
|
+
modelName: string,
|
|
77
79
|
result: GraphQLResult,
|
|
78
80
|
selectionSet: undefined | string[],
|
|
79
81
|
modelInitializer: (flattenedResult: any[]) => any[],
|
|
@@ -83,7 +85,9 @@ function processGraphQlResponse(
|
|
|
83
85
|
const [key] = Object.keys(data);
|
|
84
86
|
|
|
85
87
|
if (data[key].items) {
|
|
86
|
-
const flattenedResult =
|
|
88
|
+
const flattenedResult = data[key].items.map((value: Record<string, any>) =>
|
|
89
|
+
flattenItems(modelIntroSchema, modelName, value),
|
|
90
|
+
);
|
|
87
91
|
|
|
88
92
|
return {
|
|
89
93
|
data: selectionSet ? flattenedResult : modelInitializer(flattenedResult),
|
|
@@ -92,6 +96,7 @@ function processGraphQlResponse(
|
|
|
92
96
|
};
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
// Index queries are always list queries. No `items`? No flattening needed.
|
|
95
100
|
return {
|
|
96
101
|
data: data[key],
|
|
97
102
|
nextToken: data[key].nextToken,
|
|
@@ -159,6 +164,8 @@ async function _indexQuery(
|
|
|
159
164
|
|
|
160
165
|
if (response.data !== undefined) {
|
|
161
166
|
return processGraphQlResponse(
|
|
167
|
+
modelIntrospection,
|
|
168
|
+
name,
|
|
162
169
|
response,
|
|
163
170
|
args?.selectionSet,
|
|
164
171
|
modelInitializer,
|
|
@@ -181,7 +188,10 @@ async function _indexQuery(
|
|
|
181
188
|
const [key] = Object.keys(data);
|
|
182
189
|
|
|
183
190
|
if (data[key]?.items) {
|
|
184
|
-
const flattenedResult =
|
|
191
|
+
const flattenedResult = data[key]?.items.map(
|
|
192
|
+
(value: Record<string, any>) =>
|
|
193
|
+
flattenItems(modelIntrospection, name, value),
|
|
194
|
+
);
|
|
185
195
|
|
|
186
196
|
/**
|
|
187
197
|
* Check exists since `flattenedResult` could be `null`.
|
|
@@ -99,7 +99,10 @@ async function _list(
|
|
|
99
99
|
const [key] = Object.keys(data);
|
|
100
100
|
|
|
101
101
|
if (data[key].items) {
|
|
102
|
-
const flattenedResult =
|
|
102
|
+
const flattenedResult = data[key].items.map(
|
|
103
|
+
(value: Record<string, any>) =>
|
|
104
|
+
flattenItems(modelIntrospection, name, value),
|
|
105
|
+
);
|
|
103
106
|
|
|
104
107
|
// don't init if custom selection set
|
|
105
108
|
if (args?.selectionSet) {
|
|
@@ -153,7 +156,10 @@ async function _list(
|
|
|
153
156
|
const [key] = Object.keys(data);
|
|
154
157
|
|
|
155
158
|
if (data[key]?.items) {
|
|
156
|
-
const flattenedResult =
|
|
159
|
+
const flattenedResult = data[key].items.map(
|
|
160
|
+
(value: Record<string, any>) =>
|
|
161
|
+
flattenItems(modelIntrospection, name, value),
|
|
162
|
+
);
|
|
157
163
|
|
|
158
164
|
/**
|
|
159
165
|
* Check exists since `flattenedResult` could be `null`.
|
|
@@ -59,6 +59,7 @@ export function subscriptionFactory(
|
|
|
59
59
|
return observable.pipe(
|
|
60
60
|
map((value) => {
|
|
61
61
|
const [key] = Object.keys(value.data);
|
|
62
|
+
// Will need flattening here if/when custom selection set support is added:
|
|
62
63
|
const data = (value.data as any)[key];
|
|
63
64
|
const [initialized] = initializeModel(
|
|
64
65
|
client,
|