@aws-amplify/data-schema 1.1.6 → 1.2.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/ModelSchema.js +19 -0
- package/dist/cjs/ModelSchema.js.map +1 -1
- package/dist/cjs/SchemaProcessor.js +1 -4
- package/dist/cjs/SchemaProcessor.js.map +1 -1
- 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/ModelSchema.d.ts +13 -1
- package/dist/esm/ModelSchema.mjs +20 -1
- package/dist/esm/ModelSchema.mjs.map +1 -1
- package/dist/esm/ModelType.d.ts +1 -0
- package/dist/esm/SchemaProcessor.mjs +1 -4
- package/dist/esm/SchemaProcessor.mjs.map +1 -1
- package/dist/esm/runtime/client/index.d.ts +56 -7
- 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/ModelSchema.ts +43 -1
- package/src/ModelType.ts +1 -1
- package/src/SchemaProcessor.ts +2 -7
- package/src/runtime/client/index.ts +68 -13
- 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/ModelSchema.ts
CHANGED
|
@@ -25,7 +25,7 @@ import type {
|
|
|
25
25
|
} from './CustomOperation';
|
|
26
26
|
import { processSchema } from './SchemaProcessor';
|
|
27
27
|
import { AllowModifier, SchemaAuthorization, allow } from './Authorization';
|
|
28
|
-
import { Brand, brand } from './util';
|
|
28
|
+
import { Brand, brand, getBrand } from './util';
|
|
29
29
|
import {
|
|
30
30
|
ModelRelationalField,
|
|
31
31
|
ModelRelationalFieldParamShape,
|
|
@@ -45,6 +45,10 @@ type SchemaContent =
|
|
|
45
45
|
| EnumType
|
|
46
46
|
| CustomOperation<CustomOperationParamShape, any>;
|
|
47
47
|
|
|
48
|
+
// The SQL-only `addToSchema` accepts all top-level entities, excepts models
|
|
49
|
+
type AddToSchemaContent = Exclude<SchemaContent, BaseModelType>;
|
|
50
|
+
type AddToSchemaContents = Record<string, AddToSchemaContent>;
|
|
51
|
+
|
|
48
52
|
type NonEmpty<T> = keyof T extends never ? never : T;
|
|
49
53
|
|
|
50
54
|
type ModelSchemaContents = Record<string, SchemaContent>;
|
|
@@ -103,6 +107,7 @@ export type ModelSchema<
|
|
|
103
107
|
DDBSchemaBrand;
|
|
104
108
|
|
|
105
109
|
type RDSModelSchemaFunctions =
|
|
110
|
+
| 'addToSchema'
|
|
106
111
|
| 'addQueries'
|
|
107
112
|
| 'addMutations'
|
|
108
113
|
| 'addSubscriptions'
|
|
@@ -124,18 +129,33 @@ export type RDSModelSchema<
|
|
|
124
129
|
>,
|
|
125
130
|
> = Omit<
|
|
126
131
|
{
|
|
132
|
+
addToSchema: <AddedTypes extends AddToSchemaContents>(
|
|
133
|
+
types: AddedTypes,
|
|
134
|
+
) => RDSModelSchema<
|
|
135
|
+
SetTypeSubArg<T, 'types', T['types'] & AddedTypes>,
|
|
136
|
+
UsedMethods | 'addToSchema'
|
|
137
|
+
>;
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated use `addToSchema()` to add operations to a SQL schema
|
|
140
|
+
*/
|
|
127
141
|
addQueries: <Queries extends Record<string, QueryCustomOperation>>(
|
|
128
142
|
types: Queries,
|
|
129
143
|
) => RDSModelSchema<
|
|
130
144
|
SetTypeSubArg<T, 'types', T['types'] & Queries>,
|
|
131
145
|
UsedMethods | 'addQueries'
|
|
132
146
|
>;
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated use `addToSchema()` to add operations to a SQL schema
|
|
149
|
+
*/
|
|
133
150
|
addMutations: <Mutations extends Record<string, MutationCustomOperation>>(
|
|
134
151
|
types: Mutations,
|
|
135
152
|
) => RDSModelSchema<
|
|
136
153
|
SetTypeSubArg<T, 'types', T['types'] & Mutations>,
|
|
137
154
|
UsedMethods | 'addMutations'
|
|
138
155
|
>;
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated use `addToSchema()` to add operations to a SQL schema
|
|
158
|
+
*/
|
|
139
159
|
addSubscriptions: <
|
|
140
160
|
Subscriptions extends Record<string, SubscriptionCustomOperation>,
|
|
141
161
|
>(
|
|
@@ -254,6 +274,22 @@ export const isModelSchema = (
|
|
|
254
274
|
return typeof schema === 'object' && schema.data !== undefined;
|
|
255
275
|
};
|
|
256
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Ensures that only supported entities are being added to the SQL schema through `addToSchema`
|
|
279
|
+
* Models are not supported for brownfield SQL
|
|
280
|
+
*
|
|
281
|
+
* @param types - purposely widened to ModelSchemaContents, because we need to validate at runtime that a model is not being passed in here
|
|
282
|
+
*/
|
|
283
|
+
function validateAddToSchema(types: ModelSchemaContents): void {
|
|
284
|
+
for (const [name, type] of Object.entries(types)) {
|
|
285
|
+
if (getBrand(type) === 'modelType') {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`Invalid value specified for ${name} in addToSchema(). Models cannot be manually added to a SQL schema.`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
257
293
|
function _rdsSchema<
|
|
258
294
|
T extends RDSModelSchemaParamShape,
|
|
259
295
|
DSC extends SchemaConfiguration<any, any>,
|
|
@@ -278,6 +314,12 @@ function _rdsSchema<
|
|
|
278
314
|
const { authorization: _, ...rest } = this;
|
|
279
315
|
return rest;
|
|
280
316
|
},
|
|
317
|
+
addToSchema(types: AddToSchemaContents): any {
|
|
318
|
+
validateAddToSchema(types);
|
|
319
|
+
this.data.types = { ...this.data.types, ...types };
|
|
320
|
+
const { addToSchema: _, ...rest } = this;
|
|
321
|
+
return rest;
|
|
322
|
+
},
|
|
281
323
|
addQueries(types: Record<string, QueryCustomOperation>): any {
|
|
282
324
|
this.data.types = { ...this.data.types, ...types };
|
|
283
325
|
const { addQueries: _, ...rest } = this;
|
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
|
}> {
|
package/src/SchemaProcessor.ts
CHANGED
|
@@ -1159,8 +1159,7 @@ const schemaPreprocessor = (
|
|
|
1159
1159
|
? 'dynamodb'
|
|
1160
1160
|
: 'sql';
|
|
1161
1161
|
|
|
1162
|
-
const staticSchema =
|
|
1163
|
-
schema.data.configuration.database.engine === 'dynamodb' ? false : true;
|
|
1162
|
+
const staticSchema = databaseType === 'sql';
|
|
1164
1163
|
|
|
1165
1164
|
const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
|
|
1166
1165
|
|
|
@@ -1288,11 +1287,7 @@ const schemaPreprocessor = (
|
|
|
1288
1287
|
const [partitionKey] = identifier;
|
|
1289
1288
|
|
|
1290
1289
|
const { authString, authFields } = calculateAuth(mostRelevantAuthRules);
|
|
1291
|
-
|
|
1292
|
-
throw new Error(
|
|
1293
|
-
`Model \`${typeName}\` is missing authorization rules. Add global rules to the schema or ensure every model has its own rules.`,
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1290
|
+
|
|
1296
1291
|
const fieldLevelAuthRules = processFieldLevelAuthRules(
|
|
1297
1292
|
fields,
|
|
1298
1293
|
authFields,
|
|
@@ -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,6 +481,53 @@ 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
|
|
@@ -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) {
|