@aws-amplify/data-schema 1.2.0 → 1.2.2
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 +132 -22
- package/dist/cjs/runtime/internals/APIClient.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/list.js +4 -1
- package/dist/cjs/runtime/internals/operations/list.js.map +1 -1
- package/dist/cjs/runtime/utils/index.js +5 -1
- package/dist/cjs/runtime/utils/index.js.map +1 -1
- package/dist/cjs/runtime/utils/stringTransformation.js +13 -0
- package/dist/cjs/runtime/utils/stringTransformation.js.map +1 -0
- package/dist/esm/MappedTypes/MapIndexes.d.ts +13 -3
- package/dist/esm/ModelType.d.ts +1 -0
- package/dist/esm/runtime/client/index.d.ts +57 -8
- package/dist/esm/runtime/internals/APIClient.mjs +129 -18
- package/dist/esm/runtime/internals/APIClient.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/list.mjs +4 -1
- package/dist/esm/runtime/internals/operations/list.mjs.map +1 -1
- package/dist/esm/runtime/utils/index.d.ts +2 -0
- package/dist/esm/runtime/utils/index.mjs +2 -0
- package/dist/esm/runtime/utils/index.mjs.map +1 -1
- package/dist/esm/runtime/utils/stringTransformation.d.ts +5 -0
- package/dist/esm/runtime/utils/stringTransformation.mjs +10 -0
- package/dist/esm/runtime/utils/stringTransformation.mjs.map +1 -0
- package/dist/meta/cjs.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/MappedTypes/MapIndexes.ts +26 -3
- package/src/ModelType.ts +1 -1
- package/src/runtime/client/index.ts +70 -15
- package/src/runtime/internals/APIClient.ts +147 -27
- package/src/runtime/internals/operations/list.ts +6 -1
- package/src/runtime/utils/index.ts +2 -0
- package/src/runtime/utils/stringTransformation.ts +7 -0
package/src/ModelType.ts
CHANGED
|
@@ -363,7 +363,7 @@ export function model<T extends ModelFields>(
|
|
|
363
363
|
fields: T,
|
|
364
364
|
): ModelType<{
|
|
365
365
|
fields: T;
|
|
366
|
-
identifier: { pk: { id: string }; sk: never };
|
|
366
|
+
identifier: { pk: { id: string }; sk: never; compositeSk: never };
|
|
367
367
|
secondaryIndexes: [];
|
|
368
368
|
authorization: [];
|
|
369
369
|
}> {
|
|
@@ -449,7 +449,7 @@ type SizeFilter = {
|
|
|
449
449
|
/**
|
|
450
450
|
* Filters options that can be used on string-like fields.
|
|
451
451
|
*/
|
|
452
|
-
type StringFilter<T extends string = string> = {
|
|
452
|
+
export type StringFilter<T extends string = string> = {
|
|
453
453
|
attributeExists?: boolean;
|
|
454
454
|
beginsWith?: string;
|
|
455
455
|
between?: [string, string];
|
|
@@ -464,7 +464,7 @@ type StringFilter<T extends string = string> = {
|
|
|
464
464
|
size?: SizeFilter;
|
|
465
465
|
};
|
|
466
466
|
|
|
467
|
-
type NumericFilter = {
|
|
467
|
+
export type NumericFilter = {
|
|
468
468
|
attributeExists?: boolean;
|
|
469
469
|
between?: [number, number];
|
|
470
470
|
eq?: number;
|
|
@@ -481,12 +481,59 @@ type BooleanFilters = {
|
|
|
481
481
|
ne?: boolean;
|
|
482
482
|
};
|
|
483
483
|
|
|
484
|
+
/**
|
|
485
|
+
* A composite SK (in an identifier or secondaryIndex) resolves to this type for
|
|
486
|
+
* list queries and index queries
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* Given
|
|
490
|
+
* ```ts
|
|
491
|
+
* MyModel: a
|
|
492
|
+
.model({
|
|
493
|
+
pk: a.string().required(),
|
|
494
|
+
sk1: a.string().required(),
|
|
495
|
+
sk2: a.integer().required(),
|
|
496
|
+
})
|
|
497
|
+
.identifier(['pk', 'sk1', 'sk2']),
|
|
498
|
+
* ```
|
|
499
|
+
* Expected list options:
|
|
500
|
+
* ```ts
|
|
501
|
+
* {
|
|
502
|
+
* pk?: string
|
|
503
|
+
* sk1Sk2?: ModelPrimaryCompositeKeyConditionInput
|
|
504
|
+
* }
|
|
505
|
+
* ```
|
|
506
|
+
* Where ModelPrimaryCompositeKeyConditionInput resolves to:
|
|
507
|
+
* ```ts
|
|
508
|
+
* {
|
|
509
|
+
* eq: {sk1: string; sk2: number};
|
|
510
|
+
* le: {sk1: string; sk2: number};
|
|
511
|
+
* lt: {sk1: string; sk2: number};
|
|
512
|
+
* ge: {sk1: string; sk2: number};
|
|
513
|
+
* gt: {sk1: string; sk2: number};
|
|
514
|
+
* between: [ {sk1: string; sk2: number} ];
|
|
515
|
+
* beginsWith: {sk1: string; sk2: number};
|
|
516
|
+
* }
|
|
517
|
+
* ```
|
|
518
|
+
* */
|
|
519
|
+
export type ModelPrimaryCompositeKeyInput<
|
|
520
|
+
SkIr extends Record<string, string | number>,
|
|
521
|
+
> = {
|
|
522
|
+
eq?: SkIr;
|
|
523
|
+
le?: SkIr;
|
|
524
|
+
lt?: SkIr;
|
|
525
|
+
ge?: SkIr;
|
|
526
|
+
gt?: SkIr;
|
|
527
|
+
between?: [SkIr];
|
|
528
|
+
beginsWith?: SkIr;
|
|
529
|
+
};
|
|
530
|
+
|
|
484
531
|
type ModelFilter<Model extends Record<any, any>> = LogicalFilters<Model> & {
|
|
485
532
|
[K in keyof Model as Model[K] extends LazyLoader<any, any>
|
|
486
533
|
? never
|
|
487
|
-
: K]?: Model[K]
|
|
534
|
+
: K]?: boolean extends Model[K]
|
|
488
535
|
? BooleanFilters
|
|
489
|
-
: Model[K]
|
|
536
|
+
: number extends Model[K]
|
|
490
537
|
? NumericFilter
|
|
491
538
|
: StringFilter;
|
|
492
539
|
};
|
|
@@ -498,7 +545,6 @@ type ModelMetaShape = {
|
|
|
498
545
|
identifier: PrimaryIndexIrShape;
|
|
499
546
|
};
|
|
500
547
|
|
|
501
|
-
// TODO: remove export. added for debugging.
|
|
502
548
|
export type ModelTypesClient<
|
|
503
549
|
ModelName extends string,
|
|
504
550
|
Model extends Record<string, unknown>,
|
|
@@ -890,6 +936,7 @@ export type CustomHeaders =
|
|
|
890
936
|
export interface PrimaryIndexIrShape {
|
|
891
937
|
pk: { [key: string]: string | number };
|
|
892
938
|
sk: { [key: string]: string | number } | never;
|
|
939
|
+
compositeSk: never | string;
|
|
893
940
|
}
|
|
894
941
|
|
|
895
942
|
/**
|
|
@@ -900,7 +947,7 @@ export interface SecondaryIndexIrShape extends PrimaryIndexIrShape {
|
|
|
900
947
|
queryField: string;
|
|
901
948
|
}
|
|
902
949
|
|
|
903
|
-
type IndexQueryMethodsFromIR<
|
|
950
|
+
export type IndexQueryMethodsFromIR<
|
|
904
951
|
SecondaryIdxTuple extends SecondaryIndexIrShape[],
|
|
905
952
|
ModelName extends string,
|
|
906
953
|
Model extends Record<string, unknown>,
|
|
@@ -919,7 +966,7 @@ type IndexQueryMethodsFromIR<
|
|
|
919
966
|
>
|
|
920
967
|
: Res;
|
|
921
968
|
|
|
922
|
-
type ListPkOptions<
|
|
969
|
+
export type ListPkOptions<
|
|
923
970
|
ModelMeta extends ModelMetaShape,
|
|
924
971
|
Enums extends Record<string, string>,
|
|
925
972
|
> = ModelMeta['identifier']['sk'] extends never
|
|
@@ -933,20 +980,28 @@ type ListPkOptions<
|
|
|
933
980
|
/**
|
|
934
981
|
* Accepts a PrimaryIndexIr or SecondaryIndexIr and returns resolved parameters
|
|
935
982
|
*/
|
|
936
|
-
type IndexQueryInput<
|
|
983
|
+
export type IndexQueryInput<
|
|
937
984
|
Idx extends PrimaryIndexIrShape,
|
|
938
985
|
Enums extends Record<string, string>,
|
|
939
986
|
> = {
|
|
940
987
|
[PKField in keyof Idx['pk']]: Idx['pk'][PKField] extends `${deferredRefResolvingPrefix}${infer R}`
|
|
941
988
|
? Enums[R]
|
|
942
989
|
: Idx['pk'][PKField];
|
|
943
|
-
} &
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
990
|
+
} & (Idx['compositeSk'] extends never
|
|
991
|
+
? {
|
|
992
|
+
[SKField in keyof Idx['sk']]+?: number extends Idx['sk'][SKField]
|
|
993
|
+
? NumericFilter
|
|
994
|
+
: Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer R}`
|
|
995
|
+
? StringFilter<Enums[R]>
|
|
996
|
+
: StringFilter<Idx['sk'][SKField] & string>;
|
|
997
|
+
}
|
|
998
|
+
: {
|
|
999
|
+
[CompositeSk in Idx['compositeSk']]+?: ModelPrimaryCompositeKeyInput<{
|
|
1000
|
+
[SKField in keyof Idx['sk']]: Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer _R}`
|
|
1001
|
+
? string
|
|
1002
|
+
: Idx['sk'][SKField];
|
|
1003
|
+
}>;
|
|
1004
|
+
});
|
|
950
1005
|
|
|
951
1006
|
type IndexQueryMethodSignature<
|
|
952
1007
|
Idx extends SecondaryIndexIrShape,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from '../bridge-types';
|
|
21
21
|
|
|
22
22
|
import { CustomHeaders } from '../client';
|
|
23
|
-
import { resolveOwnerFields } from '../utils
|
|
23
|
+
import { resolveOwnerFields, capitalize } from '../utils';
|
|
24
24
|
|
|
25
25
|
import type { IndexMeta } from './operations/indexQuery';
|
|
26
26
|
|
|
@@ -38,6 +38,39 @@ const connectionType = {
|
|
|
38
38
|
BELONGS_TO: 'BELONGS_TO',
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
// When generating an SK's KeyConditionInput name, string-like types map to String
|
|
42
|
+
const skGraphQlFieldTypeMap = {
|
|
43
|
+
ID: 'ID',
|
|
44
|
+
String: 'String',
|
|
45
|
+
AWSDate: 'String',
|
|
46
|
+
AWSTime: 'String',
|
|
47
|
+
AWSDateTime: 'String',
|
|
48
|
+
AWSTimestamp: 'String',
|
|
49
|
+
AWSEmail: 'String',
|
|
50
|
+
AWSPhone: 'String',
|
|
51
|
+
AWSURL: 'String',
|
|
52
|
+
AWSIPAddress: 'String',
|
|
53
|
+
AWSJSON: 'String',
|
|
54
|
+
Boolean: 'Boolean',
|
|
55
|
+
Int: 'Int',
|
|
56
|
+
Float: 'Float',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// move to util
|
|
60
|
+
const resolvedSkName = (sk: string[]): string => {
|
|
61
|
+
if (sk.length === 1) {
|
|
62
|
+
return sk[0];
|
|
63
|
+
} else {
|
|
64
|
+
return sk.reduce((acc, curr, idx) => {
|
|
65
|
+
if (idx === 0) {
|
|
66
|
+
return curr;
|
|
67
|
+
} else {
|
|
68
|
+
return acc + capitalize(curr);
|
|
69
|
+
}
|
|
70
|
+
}, '');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
41
74
|
/**
|
|
42
75
|
*
|
|
43
76
|
* @param GraphQL response object
|
|
@@ -676,12 +709,14 @@ export function generateGraphQLDocument(
|
|
|
676
709
|
primaryKeyFieldName,
|
|
677
710
|
sortKeyFieldNames,
|
|
678
711
|
},
|
|
712
|
+
attributes,
|
|
679
713
|
} = modelDefinition;
|
|
680
714
|
|
|
681
715
|
// Use pascal case of the model name to generate the operations and the arguments.
|
|
682
716
|
// This is required to be in sync with the resources generated by the GraphQL transformers.
|
|
683
717
|
const namePascalCase = name.charAt(0).toUpperCase() + name.slice(1);
|
|
684
|
-
const pluralNamePascalCase =
|
|
718
|
+
const pluralNamePascalCase =
|
|
719
|
+
pluralName.charAt(0).toUpperCase() + pluralName.slice(1);
|
|
685
720
|
|
|
686
721
|
const { operationPrefix, usePlural } = graphQLOperationsInfo[modelOperation];
|
|
687
722
|
|
|
@@ -696,17 +731,45 @@ export function generateGraphQLDocument(
|
|
|
696
731
|
const { queryField, pk, sk = [] } = indexMeta;
|
|
697
732
|
graphQLFieldName = queryField;
|
|
698
733
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
734
|
+
/**
|
|
735
|
+
* **a. Single field SK** -> single arg where name is the field name and the type is `Model${gqlFieldType}KeyConditionInput` (nullable)
|
|
736
|
+
* Note: string-like data types e.g., AWSDateTime, AWSEmail, AWSPhone, etc. should map to String. See `skGraphQlFieldTypeMap` above
|
|
737
|
+
* @example
|
|
738
|
+
* ```
|
|
739
|
+
* sk1: ModelStringKeyConditionInput
|
|
740
|
+
* ```
|
|
741
|
+
*
|
|
742
|
+
* **b. Composite SK** -> single arg where the name is camelCase concatenation of all the field names that comprise the SK
|
|
743
|
+
* and the type is `Model${modelName}${keyAttributeName}CompositeKeyConditionInput` (nullable)
|
|
744
|
+
* @example
|
|
745
|
+
* ```
|
|
746
|
+
* sk1Sk2: ModelMyModelMyModelByPkAndSk1AndSk2CompositeKeyConditionInput
|
|
747
|
+
*/
|
|
748
|
+
let skQueryArgs = {};
|
|
749
|
+
|
|
750
|
+
if (sk.length === 1) {
|
|
751
|
+
const [skField] = sk;
|
|
752
|
+
const type = (
|
|
753
|
+
typeof fields[skField].type === 'string'
|
|
754
|
+
? fields[skField].type
|
|
755
|
+
: 'String'
|
|
756
|
+
) as keyof typeof skGraphQlFieldTypeMap;
|
|
757
|
+
const normalizedType = skGraphQlFieldTypeMap[type];
|
|
758
|
+
|
|
759
|
+
skQueryArgs = {
|
|
760
|
+
[skField]: `Model${normalizedType}KeyConditionInput`,
|
|
761
|
+
};
|
|
762
|
+
} else if (sk.length > 1) {
|
|
763
|
+
const compositeSkArgName = resolvedSkName(sk);
|
|
764
|
+
|
|
765
|
+
const keyName = attributes?.find(
|
|
766
|
+
(attr) => attr?.properties?.queryField === queryField,
|
|
767
|
+
)?.properties?.name;
|
|
707
768
|
|
|
708
|
-
|
|
709
|
-
|
|
769
|
+
skQueryArgs = {
|
|
770
|
+
[compositeSkArgName]: `Model${capitalize(modelName)}${capitalize(keyName)}CompositeKeyConditionInput`,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
710
773
|
|
|
711
774
|
indexQueryArgs = {
|
|
712
775
|
[pk]: `${
|
|
@@ -739,21 +802,69 @@ export function generateGraphQLDocument(
|
|
|
739
802
|
};
|
|
740
803
|
const listPkArgs = {};
|
|
741
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Generate query field args for the SK if it's defined
|
|
807
|
+
*
|
|
808
|
+
* **1. Get queries** require each SK field to be present as a separate arg where the type is the field's GraphQL scalar type (non-nullable)
|
|
809
|
+
* @example
|
|
810
|
+
* ```
|
|
811
|
+
* sk1: String!, sk2: Int!
|
|
812
|
+
* ```
|
|
813
|
+
*
|
|
814
|
+
* **2. List queries**
|
|
815
|
+
*
|
|
816
|
+
* **a. Single field SK** -> single arg where name is the field name and the type is `Model${gqlFieldType}KeyConditionInput` (nullable)
|
|
817
|
+
* Note: string-like data types e.g., AWSDateTime, AWSEmail, AWSPhone, etc. should map to String. See `skGraphQlFieldTypeMap` above
|
|
818
|
+
* @example
|
|
819
|
+
* ```
|
|
820
|
+
* sk1: ModelStringKeyConditionInput
|
|
821
|
+
* ```
|
|
822
|
+
*
|
|
823
|
+
* **b. Composite SK** -> single arg where the name is camelCase concatenation of all the field names that comprise the SK
|
|
824
|
+
* and the type is `Model${modelName}PrimaryCompositeKeyConditionInput` (nullable)
|
|
825
|
+
* @example
|
|
826
|
+
* ```
|
|
827
|
+
* sk1Sk2: ModelMyModelPrimaryCompositeKeyConditionInput
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
742
830
|
const generateSkArgs = (op: 'get' | 'list') => {
|
|
743
|
-
|
|
744
|
-
(acc: Record<string, any>, fieldName: string) => {
|
|
745
|
-
const fieldType = fields[fieldName].type;
|
|
746
|
-
|
|
747
|
-
if (op === 'get') {
|
|
748
|
-
acc[fieldName] = `${fieldType}!`;
|
|
749
|
-
} else if (op === 'list') {
|
|
750
|
-
acc[fieldName] = `Model${fieldType}KeyConditionInput`;
|
|
751
|
-
}
|
|
831
|
+
if (sortKeyFieldNames.length === 0) return {};
|
|
752
832
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
833
|
+
if (op === 'get') {
|
|
834
|
+
return sortKeyFieldNames.reduce(
|
|
835
|
+
(acc: Record<string, any>, fieldName: string) => {
|
|
836
|
+
const fieldType = fields[fieldName].type;
|
|
837
|
+
|
|
838
|
+
if (op === 'get') {
|
|
839
|
+
acc[fieldName] = `${fieldType}!`; // ! - SK args are non-nullable in Get queries
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return acc;
|
|
843
|
+
},
|
|
844
|
+
{},
|
|
845
|
+
);
|
|
846
|
+
} else {
|
|
847
|
+
// list SK
|
|
848
|
+
if (sortKeyFieldNames.length === 1) {
|
|
849
|
+
// Single SK
|
|
850
|
+
const [sk] = sortKeyFieldNames;
|
|
851
|
+
const type = (
|
|
852
|
+
typeof fields[sk].type === 'string' ? fields[sk].type : 'String'
|
|
853
|
+
) as keyof typeof skGraphQlFieldTypeMap;
|
|
854
|
+
const normalizedType = skGraphQlFieldTypeMap[type];
|
|
855
|
+
|
|
856
|
+
return {
|
|
857
|
+
[sk]: `Model${normalizedType}KeyConditionInput`,
|
|
858
|
+
};
|
|
859
|
+
} else {
|
|
860
|
+
// Composite SK
|
|
861
|
+
const compositeSkArgName = resolvedSkName(sortKeyFieldNames);
|
|
862
|
+
|
|
863
|
+
return {
|
|
864
|
+
[compositeSkArgName]: `Model${capitalize(modelName)}PrimaryCompositeKeyConditionInput`,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
}
|
|
757
868
|
};
|
|
758
869
|
|
|
759
870
|
if (isCustomPrimaryKey) {
|
|
@@ -873,6 +984,8 @@ export function buildGraphQLVariables(
|
|
|
873
984
|
},
|
|
874
985
|
} = modelDefinition;
|
|
875
986
|
|
|
987
|
+
const skName = sortKeyFieldNames?.length && resolvedSkName(sortKeyFieldNames);
|
|
988
|
+
|
|
876
989
|
let variables: Record<string, any> = {};
|
|
877
990
|
|
|
878
991
|
// TODO: process input
|
|
@@ -930,8 +1043,13 @@ export function buildGraphQLVariables(
|
|
|
930
1043
|
}
|
|
931
1044
|
if (arg?.sortDirection) {
|
|
932
1045
|
variables.sortDirection = arg.sortDirection;
|
|
1046
|
+
}
|
|
1047
|
+
if (arg && arg[primaryKeyFieldName]) {
|
|
933
1048
|
variables[primaryKeyFieldName] = arg[primaryKeyFieldName];
|
|
934
1049
|
}
|
|
1050
|
+
if (skName && arg && arg[skName]) {
|
|
1051
|
+
variables[skName] = arg[skName];
|
|
1052
|
+
}
|
|
935
1053
|
if (arg?.nextToken) {
|
|
936
1054
|
variables.nextToken = arg.nextToken;
|
|
937
1055
|
}
|
|
@@ -942,10 +1060,12 @@ export function buildGraphQLVariables(
|
|
|
942
1060
|
case 'INDEX_QUERY': {
|
|
943
1061
|
const { pk, sk = [] } = indexMeta!;
|
|
944
1062
|
|
|
1063
|
+
const indexQuerySkName = sk?.length && resolvedSkName(sk);
|
|
1064
|
+
|
|
945
1065
|
variables[pk] = arg![pk];
|
|
946
1066
|
|
|
947
|
-
|
|
948
|
-
variables[
|
|
1067
|
+
if (indexQuerySkName && arg && arg[indexQuerySkName]) {
|
|
1068
|
+
variables[indexQuerySkName] = arg[indexQuerySkName];
|
|
949
1069
|
}
|
|
950
1070
|
|
|
951
1071
|
if (arg?.filter) {
|
|
@@ -144,7 +144,12 @@ async function _list(
|
|
|
144
144
|
const { data, errors } = error;
|
|
145
145
|
|
|
146
146
|
// `data` is not `null`, and is not an empty object:
|
|
147
|
-
if (
|
|
147
|
+
if (
|
|
148
|
+
data !== undefined &&
|
|
149
|
+
data !== null &&
|
|
150
|
+
Object.keys(data).length !== 0 &&
|
|
151
|
+
errors
|
|
152
|
+
) {
|
|
148
153
|
const [key] = Object.keys(data);
|
|
149
154
|
|
|
150
155
|
if (data[key]?.items) {
|