@aws-amplify/data-schema 0.16.2 → 0.17.1
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/SchemaProcessor.js +6 -6
- package/dist/cjs/SchemaProcessor.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/custom.js +33 -5
- package/dist/cjs/runtime/internals/operations/custom.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/get.js +36 -6
- package/dist/cjs/runtime/internals/operations/get.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/indexQuery.js +39 -11
- package/dist/cjs/runtime/internals/operations/indexQuery.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/list.js +50 -6
- package/dist/cjs/runtime/internals/operations/list.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/utils.js +42 -0
- package/dist/cjs/runtime/internals/operations/utils.js.map +1 -0
- package/dist/esm/Authorization.d.ts +2 -2
- package/dist/esm/MappedTypes/MapSecondaryIndexes.d.ts +2 -2
- package/dist/esm/ModelRelationalField.d.ts +1 -1
- package/dist/esm/SchemaProcessor.mjs +6 -6
- package/dist/esm/SchemaProcessor.mjs.map +1 -1
- package/dist/esm/runtime/client/index.d.ts +20 -21
- package/dist/esm/runtime/internals/operations/custom.mjs +33 -5
- package/dist/esm/runtime/internals/operations/custom.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/get.mjs +36 -6
- package/dist/esm/runtime/internals/operations/get.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/indexQuery.mjs +39 -11
- package/dist/esm/runtime/internals/operations/indexQuery.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/list.mjs +50 -6
- package/dist/esm/runtime/internals/operations/list.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/utils.d.ts +8 -0
- package/dist/esm/runtime/internals/operations/utils.mjs +38 -0
- package/dist/esm/runtime/internals/operations/utils.mjs.map +1 -0
- package/dist/meta/cjs.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/MappedTypes/MapSecondaryIndexes.ts +22 -26
- package/src/SchemaProcessor.ts +6 -1
- package/src/runtime/client/index.ts +34 -11
- package/src/runtime/internals/operations/custom.ts +44 -5
- package/src/runtime/internals/operations/get.ts +47 -7
- package/src/runtime/internals/operations/indexQuery.ts +43 -11
- package/src/runtime/internals/operations/list.ts +63 -7
- package/src/runtime/internals/operations/utils.ts +35 -0
package/package.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { IsEmptyStringOrNever } from '@aws-amplify/data-schema-types';
|
|
2
1
|
import { ModelIndexType } from '../ModelIndex';
|
|
3
2
|
|
|
4
3
|
type ModelIndexTypeShape = ModelIndexType<any, any, any, any, any>;
|
|
@@ -34,31 +33,28 @@ export type SecondaryIndexToIR<
|
|
|
34
33
|
*
|
|
35
34
|
* @remarks - the IR type alias is defined as SecondaryIndexIrShape in data-schema-types
|
|
36
35
|
*/
|
|
37
|
-
type SingleIndexIrFromType<
|
|
38
|
-
Idx extends
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: ResolvedSortKeyFields<SK, ResolvedFields>;
|
|
60
|
-
}
|
|
61
|
-
: never;
|
|
36
|
+
type SingleIndexIrFromType<Idx extends ModelIndexTypeShape, ResolvedFields> =
|
|
37
|
+
Idx extends ModelIndexType<
|
|
38
|
+
any,
|
|
39
|
+
infer PK extends string,
|
|
40
|
+
infer SK,
|
|
41
|
+
infer QueryField extends string | never,
|
|
42
|
+
any
|
|
43
|
+
>
|
|
44
|
+
? {
|
|
45
|
+
defaultQueryFieldSuffix: `${QueryFieldLabelFromTuple<SK, Capitalize<PK>>}`;
|
|
46
|
+
queryField: QueryField;
|
|
47
|
+
pk: PK extends keyof ResolvedFields
|
|
48
|
+
? {
|
|
49
|
+
[Key in PK]: Exclude<ResolvedFields[PK], null>;
|
|
50
|
+
}
|
|
51
|
+
: never;
|
|
52
|
+
// distribute ResolvedFields over SK
|
|
53
|
+
sk: unknown extends SK
|
|
54
|
+
? never
|
|
55
|
+
: ResolvedSortKeyFields<SK, ResolvedFields>;
|
|
56
|
+
}
|
|
57
|
+
: never;
|
|
62
58
|
|
|
63
59
|
/**
|
|
64
60
|
* @typeParam SK - tuple of SortKey field names, e.g. ['viewCount', 'createdAt']
|
package/src/SchemaProcessor.ts
CHANGED
|
@@ -810,12 +810,13 @@ type TransformedSecondaryIndexes = {
|
|
|
810
810
|
* @returns default query field name
|
|
811
811
|
*/
|
|
812
812
|
const secondaryIndexDefaultQueryField = (
|
|
813
|
+
modelName: string,
|
|
813
814
|
pk: string,
|
|
814
815
|
sk?: readonly string[],
|
|
815
816
|
): string => {
|
|
816
817
|
const skName = sk?.length ? 'And' + sk?.map(capitalize).join('And') : '';
|
|
817
818
|
|
|
818
|
-
const queryField = `
|
|
819
|
+
const queryField = `list${capitalize(modelName)}By${capitalize(pk)}${skName}`;
|
|
819
820
|
|
|
820
821
|
return queryField;
|
|
821
822
|
};
|
|
@@ -825,6 +826,7 @@ const secondaryIndexDefaultQueryField = (
|
|
|
825
826
|
* and the value is an array of transformed Amplify @index directives with all supplied attributes
|
|
826
827
|
*/
|
|
827
828
|
const transformedSecondaryIndexesForModel = (
|
|
829
|
+
modelName: string,
|
|
828
830
|
secondaryIndexes: readonly InternalModelIndexType[],
|
|
829
831
|
): TransformedSecondaryIndexes => {
|
|
830
832
|
const indexDirectiveWithAttributes = (
|
|
@@ -835,6 +837,7 @@ const transformedSecondaryIndexesForModel = (
|
|
|
835
837
|
): string => {
|
|
836
838
|
if (!sortKeys.length && !indexName && !queryField) {
|
|
837
839
|
return `@index(queryField: "${secondaryIndexDefaultQueryField(
|
|
840
|
+
modelName,
|
|
838
841
|
partitionKey,
|
|
839
842
|
)}")`;
|
|
840
843
|
}
|
|
@@ -856,6 +859,7 @@ const transformedSecondaryIndexesForModel = (
|
|
|
856
859
|
} else {
|
|
857
860
|
attributes.push(
|
|
858
861
|
`queryField: "${secondaryIndexDefaultQueryField(
|
|
862
|
+
modelName,
|
|
859
863
|
partitionKey,
|
|
860
864
|
sortKeys,
|
|
861
865
|
)}"`,
|
|
@@ -1164,6 +1168,7 @@ const schemaPreprocessor = (
|
|
|
1164
1168
|
const [partitionKey] = identifier;
|
|
1165
1169
|
|
|
1166
1170
|
const transformedSecondaryIndexes = transformedSecondaryIndexesForModel(
|
|
1171
|
+
typeName,
|
|
1167
1172
|
typeDef.data.secondaryIndexes,
|
|
1168
1173
|
);
|
|
1169
1174
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Equal,
|
|
9
9
|
__modelMeta__,
|
|
10
10
|
ExtractModelMeta,
|
|
11
|
+
IsEmptyStringOrNever,
|
|
11
12
|
} from '@aws-amplify/data-schema-types';
|
|
12
13
|
import type { Observable } from 'rxjs';
|
|
13
14
|
|
|
@@ -494,10 +495,11 @@ type ModelMetaShape = {
|
|
|
494
495
|
|
|
495
496
|
// TODO: remove export. added for debugging.
|
|
496
497
|
export type ModelTypesClient<
|
|
498
|
+
ModelName extends string,
|
|
497
499
|
Model extends Record<string, unknown>,
|
|
498
500
|
ModelMeta extends ModelMetaShape,
|
|
499
501
|
FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
|
|
500
|
-
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], Model> & {
|
|
502
|
+
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
|
|
501
503
|
create: (
|
|
502
504
|
model: Prettify<CreateModelInput<Model, ModelMeta>>,
|
|
503
505
|
options?: {
|
|
@@ -589,10 +591,11 @@ export type ModelTypesClient<
|
|
|
589
591
|
};
|
|
590
592
|
|
|
591
593
|
type ModelTypesSSRCookies<
|
|
594
|
+
ModelName extends string,
|
|
592
595
|
Model extends Record<string, unknown>,
|
|
593
596
|
ModelMeta extends ModelMetaShape,
|
|
594
597
|
FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
|
|
595
|
-
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], Model> & {
|
|
598
|
+
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
|
|
596
599
|
create: (
|
|
597
600
|
model: Prettify<CreateModelInput<Model, ModelMeta>>,
|
|
598
601
|
options?: {
|
|
@@ -641,10 +644,11 @@ type ModelTypesSSRCookies<
|
|
|
641
644
|
};
|
|
642
645
|
|
|
643
646
|
type ModelTypesSSRRequest<
|
|
647
|
+
ModelName extends string,
|
|
644
648
|
Model extends Record<string, unknown>,
|
|
645
649
|
ModelMeta extends ModelMetaShape,
|
|
646
650
|
FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
|
|
647
|
-
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], Model> & {
|
|
651
|
+
> = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
|
|
648
652
|
create: (
|
|
649
653
|
// TODO: actual type
|
|
650
654
|
contextSpec: any,
|
|
@@ -716,11 +720,19 @@ export type ModelTypes<
|
|
|
716
720
|
>]: ModelName extends string
|
|
717
721
|
? Schema[ModelName] extends Record<string, unknown>
|
|
718
722
|
? Context extends 'CLIENT'
|
|
719
|
-
? ModelTypesClient<Schema[ModelName], ModelMeta[ModelName]>
|
|
723
|
+
? ModelTypesClient<ModelName, Schema[ModelName], ModelMeta[ModelName]>
|
|
720
724
|
: Context extends 'COOKIES'
|
|
721
|
-
? ModelTypesSSRCookies<
|
|
725
|
+
? ModelTypesSSRCookies<
|
|
726
|
+
ModelName,
|
|
727
|
+
Schema[ModelName],
|
|
728
|
+
ModelMeta[ModelName]
|
|
729
|
+
>
|
|
722
730
|
: Context extends 'REQUEST'
|
|
723
|
-
? ModelTypesSSRRequest<
|
|
731
|
+
? ModelTypesSSRRequest<
|
|
732
|
+
ModelName,
|
|
733
|
+
Schema[ModelName],
|
|
734
|
+
ModelMeta[ModelName]
|
|
735
|
+
>
|
|
724
736
|
: never
|
|
725
737
|
: never
|
|
726
738
|
: never;
|
|
@@ -847,6 +859,7 @@ export type CustomHeaders =
|
|
|
847
859
|
* SecondaryIndex index types and query methods
|
|
848
860
|
*/
|
|
849
861
|
export type SecondaryIndexIrShape = {
|
|
862
|
+
defaultQueryFieldSuffix: string;
|
|
850
863
|
queryField: string;
|
|
851
864
|
pk: { [key: string]: string | number };
|
|
852
865
|
sk: { [key: string]: string | number };
|
|
@@ -854,20 +867,30 @@ export type SecondaryIndexIrShape = {
|
|
|
854
867
|
|
|
855
868
|
type IndexQueryMethodsFromIR<
|
|
856
869
|
SecondaryIdxTuple extends SecondaryIndexIrShape[],
|
|
870
|
+
ModelName extends string,
|
|
857
871
|
Model extends Record<string, unknown>,
|
|
858
872
|
Res = unknown, // defaulting `unknown` because it gets absorbed in an intersection, e.g. `{a: 1} & unknown` => `{a: 1}`
|
|
859
873
|
> = SecondaryIdxTuple extends [
|
|
860
874
|
infer A extends SecondaryIndexIrShape,
|
|
861
875
|
...infer B extends SecondaryIndexIrShape[],
|
|
862
876
|
]
|
|
863
|
-
? IndexQueryMethodsFromIR<
|
|
877
|
+
? IndexQueryMethodsFromIR<
|
|
878
|
+
B,
|
|
879
|
+
ModelName,
|
|
880
|
+
Model,
|
|
881
|
+
IndexQueryMethodSignature<A, ModelName, Model> & Res
|
|
882
|
+
>
|
|
864
883
|
: Res;
|
|
865
884
|
|
|
866
885
|
type IndexQueryMethodSignature<
|
|
867
886
|
Idx extends SecondaryIndexIrShape,
|
|
887
|
+
ModelName extends string,
|
|
868
888
|
Model extends Record<string, unknown>,
|
|
869
|
-
> =
|
|
870
|
-
|
|
889
|
+
> = Record<
|
|
890
|
+
IsEmptyStringOrNever<Idx['queryField']> extends false
|
|
891
|
+
? Idx['queryField']
|
|
892
|
+
: `list${ModelName}By${Idx['defaultQueryFieldSuffix']}`,
|
|
893
|
+
<
|
|
871
894
|
FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
|
|
872
895
|
SelectionSet extends ReadonlyArray<ModelPath<FlatModel>> = never[],
|
|
873
896
|
>(
|
|
@@ -886,8 +909,8 @@ type IndexQueryMethodSignature<
|
|
|
886
909
|
authToken?: string;
|
|
887
910
|
headers?: CustomHeaders;
|
|
888
911
|
},
|
|
889
|
-
) => ListReturnValue<Prettify<ReturnValue<Model, FlatModel, SelectionSet
|
|
890
|
-
|
|
912
|
+
) => ListReturnValue<Prettify<ReturnValue<Model, FlatModel, SelectionSet>>>
|
|
913
|
+
>;
|
|
891
914
|
|
|
892
915
|
type FilteredKeys<T> = {
|
|
893
916
|
[P in keyof T]: T[P] extends never ? never : P;
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
selectionSetIRToString,
|
|
28
28
|
} from '../APIClient';
|
|
29
29
|
|
|
30
|
+
import { handleSingularGraphQlError } from './utils';
|
|
31
|
+
|
|
30
32
|
type CustomOperationOptions = AuthModeParams & ListArgs;
|
|
31
33
|
|
|
32
34
|
// these are the 4 possible sets of arguments custom operations methods can receive
|
|
@@ -417,12 +419,49 @@ async function _op(
|
|
|
417
419
|
return { data: null, extensions };
|
|
418
420
|
}
|
|
419
421
|
} catch (error: any) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
422
|
+
/**
|
|
423
|
+
* The `data` type returned by `error` here could be:
|
|
424
|
+
* 1) `null`
|
|
425
|
+
* 2) an empty object
|
|
426
|
+
* 3) "populated" but with a `null` value `{ getPost: null }`
|
|
427
|
+
* 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
|
|
428
|
+
*/
|
|
429
|
+
const { data, errors } = error;
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* `data` is not `null`, and is not an empty object:
|
|
433
|
+
*/
|
|
434
|
+
if (data && Object.keys(data).length !== 0 && errors) {
|
|
435
|
+
const [key] = Object.keys(data);
|
|
436
|
+
const flattenedResult = flattenItems(data)[key];
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
|
|
440
|
+
* if `flattenedResult`, result is an actual record:
|
|
441
|
+
*/
|
|
442
|
+
if (flattenedResult) {
|
|
443
|
+
// TODO: custom selection set. current selection set is default selection set only
|
|
444
|
+
// custom selection set requires data-schema-type + runtime updates above.
|
|
445
|
+
const [initialized] = returnTypeModelName
|
|
446
|
+
? initializeModel(
|
|
447
|
+
client,
|
|
448
|
+
returnTypeModelName,
|
|
449
|
+
[flattenedResult],
|
|
450
|
+
modelIntrospection,
|
|
451
|
+
auth.authMode,
|
|
452
|
+
auth.authToken,
|
|
453
|
+
!!context,
|
|
454
|
+
)
|
|
455
|
+
: [flattenedResult];
|
|
456
|
+
|
|
457
|
+
return { data: initialized, errors };
|
|
458
|
+
} else {
|
|
459
|
+
// was `data: { getPost: null }`)
|
|
460
|
+
return handleSingularGraphQlError(error);
|
|
461
|
+
}
|
|
423
462
|
} else {
|
|
424
|
-
//
|
|
425
|
-
|
|
463
|
+
// `data` is `null`:
|
|
464
|
+
return handleSingularGraphQlError(error);
|
|
426
465
|
}
|
|
427
466
|
}
|
|
428
467
|
}
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
initializeModel,
|
|
26
26
|
} from '../APIClient';
|
|
27
27
|
|
|
28
|
+
import { handleSingularGraphQlError } from './utils';
|
|
29
|
+
|
|
28
30
|
export function getFactory(
|
|
29
31
|
client: BaseClient,
|
|
30
32
|
modelIntrospection: ModelIntrospectionSchema,
|
|
@@ -90,9 +92,9 @@ async function _get(
|
|
|
90
92
|
modelIntrospection,
|
|
91
93
|
);
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
const auth = authModeParams(client, getInternals, options);
|
|
95
|
+
const auth = authModeParams(client, getInternals, options);
|
|
95
96
|
|
|
97
|
+
try {
|
|
96
98
|
const headers = getCustomHeaders(client, getInternals, options?.headers);
|
|
97
99
|
|
|
98
100
|
const { data, extensions } = context
|
|
@@ -139,12 +141,50 @@ async function _get(
|
|
|
139
141
|
return { data: null, extensions };
|
|
140
142
|
}
|
|
141
143
|
} catch (error: any) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
/**
|
|
145
|
+
* The `data` type returned by `error` here could be:
|
|
146
|
+
* 1) `null`
|
|
147
|
+
* 2) an empty object
|
|
148
|
+
* 3) "populated" but with a `null` value `{ getPost: null }`
|
|
149
|
+
* 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
|
|
150
|
+
*/
|
|
151
|
+
const { data, errors } = error;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* `data` is not `null`, and is not an empty object:
|
|
155
|
+
*/
|
|
156
|
+
if (data && Object.keys(data).length !== 0 && errors) {
|
|
157
|
+
const [key] = Object.keys(data);
|
|
158
|
+
const flattenedResult = flattenItems(data)[key];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
|
|
162
|
+
* if `flattenedResult`, result is an actual record:
|
|
163
|
+
*/
|
|
164
|
+
if (flattenedResult) {
|
|
165
|
+
if (options?.selectionSet) {
|
|
166
|
+
return { data: flattenedResult, errors };
|
|
167
|
+
} else {
|
|
168
|
+
// TODO: refactor to avoid destructuring here
|
|
169
|
+
const [initialized] = initializeModel(
|
|
170
|
+
client,
|
|
171
|
+
name,
|
|
172
|
+
[flattenedResult],
|
|
173
|
+
modelIntrospection,
|
|
174
|
+
auth.authMode,
|
|
175
|
+
auth.authToken,
|
|
176
|
+
!!context,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
return { data: initialized, errors };
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// was `data: { getPost: null }`)
|
|
183
|
+
return handleSingularGraphQlError(error);
|
|
184
|
+
}
|
|
145
185
|
} else {
|
|
146
|
-
//
|
|
147
|
-
|
|
186
|
+
// `data` is `null`:
|
|
187
|
+
return handleSingularGraphQlError(error);
|
|
148
188
|
}
|
|
149
189
|
}
|
|
150
190
|
}
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
initializeModel,
|
|
22
22
|
} from '../APIClient';
|
|
23
23
|
|
|
24
|
+
import { handleListGraphQlError } from './utils';
|
|
25
|
+
|
|
24
26
|
export interface IndexMeta {
|
|
25
27
|
queryField: string;
|
|
26
28
|
pk: string;
|
|
@@ -97,16 +99,6 @@ function processGraphQlResponse(
|
|
|
97
99
|
};
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
function handleGraphQlError(error: any) {
|
|
101
|
-
if (error.errors) {
|
|
102
|
-
// graphql errors pass through
|
|
103
|
-
return error as any;
|
|
104
|
-
} else {
|
|
105
|
-
// non-graphql errors re re-thrown
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
102
|
async function _indexQuery(
|
|
111
103
|
client: BaseClient,
|
|
112
104
|
modelIntrospection: ModelIntrospectionSchema,
|
|
@@ -173,6 +165,46 @@ async function _indexQuery(
|
|
|
173
165
|
);
|
|
174
166
|
}
|
|
175
167
|
} catch (error: any) {
|
|
176
|
-
|
|
168
|
+
/**
|
|
169
|
+
* The `data` type returned by `error` here could be:
|
|
170
|
+
* 1) `null`
|
|
171
|
+
* 2) an empty object
|
|
172
|
+
* 3) "populated" but with a `null` value:
|
|
173
|
+
* `data: { listByExampleId: null }`
|
|
174
|
+
* 4) an actual record:
|
|
175
|
+
* `data: { listByExampleId: items: [{ id: '1', ...etc } }]`
|
|
176
|
+
*/
|
|
177
|
+
const { data, errors } = error;
|
|
178
|
+
|
|
179
|
+
// `data` is not `null`, and is not an empty object:
|
|
180
|
+
if (data !== undefined && Object.keys(data).length !== 0 && errors) {
|
|
181
|
+
const [key] = Object.keys(data);
|
|
182
|
+
|
|
183
|
+
if (data[key]?.items) {
|
|
184
|
+
const flattenedResult = flattenItems(data)[key];
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check exists since `flattenedResult` could be `null`.
|
|
188
|
+
* if `flattenedResult` exists, result is an actual record.
|
|
189
|
+
*/
|
|
190
|
+
if (flattenedResult) {
|
|
191
|
+
return {
|
|
192
|
+
data: args?.selectionSet
|
|
193
|
+
? flattenedResult
|
|
194
|
+
: modelInitializer(flattenedResult),
|
|
195
|
+
nextToken: data[key]?.nextToken,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// response is of type `data: { listByExampleId: null }`
|
|
201
|
+
return {
|
|
202
|
+
data: data[key],
|
|
203
|
+
nextToken: data[key]?.nextToken,
|
|
204
|
+
};
|
|
205
|
+
} else {
|
|
206
|
+
// `data` is `null` or an empty object:
|
|
207
|
+
return handleListGraphQlError(error);
|
|
208
|
+
}
|
|
177
209
|
}
|
|
178
210
|
}
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
initializeModel,
|
|
23
23
|
} from '../APIClient';
|
|
24
24
|
|
|
25
|
+
import { handleListGraphQlError } from './utils';
|
|
26
|
+
|
|
25
27
|
export function listFactory(
|
|
26
28
|
client: BaseClient,
|
|
27
29
|
modelIntrospection: ModelIntrospectionSchema,
|
|
@@ -68,9 +70,9 @@ async function _list(
|
|
|
68
70
|
modelIntrospection,
|
|
69
71
|
);
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
const auth = authModeParams(client, getInternals, args);
|
|
73
|
+
const auth = authModeParams(client, getInternals, args);
|
|
73
74
|
|
|
75
|
+
try {
|
|
74
76
|
const headers = getCustomHeaders(client, getInternals, args?.headers);
|
|
75
77
|
|
|
76
78
|
const { data, extensions } = contextSpec
|
|
@@ -132,12 +134,66 @@ async function _list(
|
|
|
132
134
|
};
|
|
133
135
|
}
|
|
134
136
|
} catch (error: any) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
/**
|
|
138
|
+
* The `data` type returned by `error` here could be:
|
|
139
|
+
* 1) `null`
|
|
140
|
+
* 2) an empty object
|
|
141
|
+
* 3) "populated" but with a `null` value `data: { listPosts: null }`
|
|
142
|
+
* 4) actual records `data: { listPosts: items: [{ id: '1', ...etc }] }`
|
|
143
|
+
*/
|
|
144
|
+
const { data, errors } = error;
|
|
145
|
+
|
|
146
|
+
// `data` is not `null`, and is not an empty object:
|
|
147
|
+
if (data !== undefined && Object.keys(data).length !== 0 && errors) {
|
|
148
|
+
const [key] = Object.keys(data);
|
|
149
|
+
|
|
150
|
+
if (data[key]?.items) {
|
|
151
|
+
const flattenedResult = flattenItems(data)[key];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check exists since `flattenedResult` could be `null`.
|
|
155
|
+
* if `flattenedResult` exists, result is an actual record.
|
|
156
|
+
*/
|
|
157
|
+
if (flattenedResult) {
|
|
158
|
+
// don't init if custom selection set
|
|
159
|
+
if (args?.selectionSet) {
|
|
160
|
+
return {
|
|
161
|
+
data: flattenedResult,
|
|
162
|
+
nextToken: data[key]?.nextToken,
|
|
163
|
+
errors,
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
const initialized = initializeModel(
|
|
167
|
+
client,
|
|
168
|
+
name,
|
|
169
|
+
flattenedResult,
|
|
170
|
+
modelIntrospection,
|
|
171
|
+
auth.authMode,
|
|
172
|
+
auth.authToken,
|
|
173
|
+
!!contextSpec,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// data is full record w/out selection set:
|
|
177
|
+
return {
|
|
178
|
+
data: initialized,
|
|
179
|
+
nextToken: data[key]?.nextToken,
|
|
180
|
+
errors,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
data: data[key],
|
|
187
|
+
nextToken: data[key]?.nextToken,
|
|
188
|
+
errors,
|
|
189
|
+
};
|
|
190
|
+
} else {
|
|
191
|
+
// response is of type `data: { getPost: null }`)
|
|
192
|
+
return handleListGraphQlError(error);
|
|
193
|
+
}
|
|
138
194
|
} else {
|
|
139
|
-
//
|
|
140
|
-
|
|
195
|
+
// `data` is `null` or an empty object:
|
|
196
|
+
return handleListGraphQlError(error);
|
|
141
197
|
}
|
|
142
198
|
}
|
|
143
199
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// import { GraphQLFormattedError } from '@aws-amplify/data-schema-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handle errors for list return types (list and index query operations)
|
|
7
|
+
*/
|
|
8
|
+
export function handleListGraphQlError(error: any) {
|
|
9
|
+
if (error?.errors) {
|
|
10
|
+
// graphql errors pass through
|
|
11
|
+
return {
|
|
12
|
+
...error,
|
|
13
|
+
data: [],
|
|
14
|
+
} as any;
|
|
15
|
+
} else {
|
|
16
|
+
// non-graphql errors are re-thrown
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Handle errors for singular return types (create, get, update, delete operations)
|
|
23
|
+
*/
|
|
24
|
+
export function handleSingularGraphQlError(error: any) {
|
|
25
|
+
if (error.errors) {
|
|
26
|
+
// graphql errors pass through
|
|
27
|
+
return {
|
|
28
|
+
...error,
|
|
29
|
+
data: null,
|
|
30
|
+
} as any;
|
|
31
|
+
} else {
|
|
32
|
+
// non-graphql errors are re-thrown
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|