@aws-amplify/data-schema 0.18.2 → 0.18.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-amplify/data-schema",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
package/src/ModelType.ts CHANGED
@@ -10,7 +10,7 @@ import type {
10
10
  ModelRelationalFieldParamShape,
11
11
  } from './ModelRelationalField';
12
12
  import { AllowModifier, Authorization, allow } from './Authorization';
13
- import { RefType } from './RefType';
13
+ import { RefType, RefTypeParamShape } from './RefType';
14
14
  import { EnumType, EnumTypeParamShape } from './EnumType';
15
15
  import { CustomType, CustomTypeParamShape } from './CustomType';
16
16
  import {
@@ -21,6 +21,7 @@ import {
21
21
  import { SecondaryIndexToIR } from './MappedTypes/MapSecondaryIndexes';
22
22
 
23
23
  const brandName = 'modelType';
24
+ export type deferredRefResolvingPrefix = 'deferredRefResolving:';
24
25
 
25
26
  type ModelFields = Record<
26
27
  string,
@@ -57,14 +58,41 @@ export type ModelTypeParamShape = {
57
58
  authorization: Authorization<any, any, any>[];
58
59
  };
59
60
 
60
- // Extract field names that can be used to define a secondary index PK or SK
61
- // i.e., nullable string or nullable number fields
62
- type SecondaryIndexFields<T extends Record<string, unknown>> = keyof {
63
- [Field in keyof T as NonNullable<T[Field]> extends string | number
64
- ? Field
65
- : never]: T[Field];
66
- } &
67
- string;
61
+ /**
62
+ * Extract fields that are eligible to be PK or SK fields with their resolved type.
63
+ *
64
+ * Eligible fields include:
65
+ * 1. ModelField that contains string or number
66
+ * 2. inline EnumType
67
+ * 3. RefType that refers to a top level defined EnumType (this is enforced by
68
+ * validation that happens in the Schema Processor)
69
+ *
70
+ * NOTE: at this point, there is no way to resolve the type from a RefType as
71
+ * we don't have access to the NonModelType at this location. So we generate am
72
+ * indicator string, and resolve its corresponding type later in
73
+ * packages/data-schema/src/runtime/client/index.ts
74
+ */
75
+ type ExtractSecondaryIndexIRFields<T extends ModelTypeParamShape> = {
76
+ [FieldProp in keyof T['fields'] as T['fields'][FieldProp] extends ModelField<
77
+ infer R,
78
+ any,
79
+ any
80
+ >
81
+ ? NonNullable<R> extends string | number
82
+ ? FieldProp
83
+ : never
84
+ : T['fields'][FieldProp] extends EnumType<EnumTypeParamShape>
85
+ ? FieldProp
86
+ : T['fields'][FieldProp] extends RefType<RefTypeParamShape, any, any>
87
+ ? FieldProp
88
+ : never]: T['fields'][FieldProp] extends ModelField<infer R, any, any>
89
+ ? R
90
+ : T['fields'][FieldProp] extends EnumType<infer R>
91
+ ? R['values'][number]
92
+ : T['fields'][FieldProp] extends RefType<infer R, any, any>
93
+ ? `${deferredRefResolvingPrefix}${R['link']}`
94
+ : never;
95
+ };
68
96
 
69
97
  type ExtractType<T extends ModelTypeParamShape> = {
70
98
  [FieldProp in keyof T['fields'] as T['fields'][FieldProp] extends ModelField<
@@ -189,9 +217,9 @@ export type ModelType<
189
217
  identifier: ID,
190
218
  ): ModelType<SetTypeSubArg<T, 'identifier', ID>, K | 'identifier'>;
191
219
  secondaryIndexes<
192
- const SecondaryIndexPKPool extends string = SecondaryIndexFields<
193
- ExtractType<T>
194
- >,
220
+ const SecondaryIndexFields = ExtractSecondaryIndexIRFields<T>,
221
+ const SecondaryIndexPKPool extends string = keyof SecondaryIndexFields &
222
+ string,
195
223
  const Indexes extends readonly ModelIndexType<
196
224
  string,
197
225
  string,
@@ -201,7 +229,7 @@ export type ModelType<
201
229
  >[] = readonly [],
202
230
  const IndexesIR extends readonly any[] = SecondaryIndexToIR<
203
231
  Indexes,
204
- ExtractType<T>
232
+ SecondaryIndexFields
205
233
  >,
206
234
  >(
207
235
  callback: (
@@ -218,7 +246,9 @@ export type ModelType<
218
246
  K | 'secondaryIndexes'
219
247
  >;
220
248
  authorization<AuthRuleType extends Authorization<any, any, any>>(
221
- callback: (allow: Omit<AllowModifier, 'resource'>) => AuthRuleType | AuthRuleType[],
249
+ callback: (
250
+ allow: Omit<AllowModifier, 'resource'>,
251
+ ) => AuthRuleType | AuthRuleType[],
222
252
  ): ModelType<
223
253
  SetTypeSubArg<T, 'authorization', AuthRuleType[]>,
224
254
  K | 'authorization'
@@ -220,7 +220,10 @@ function modelFieldToGql(fieldDef: ModelFieldDef) {
220
220
  return field;
221
221
  }
222
222
 
223
- function refFieldToGql(fieldDef: RefFieldDef): string {
223
+ function refFieldToGql(
224
+ fieldDef: RefFieldDef,
225
+ secondaryIndexes: string[] = [],
226
+ ): string {
224
227
  const { link, valueRequired, array, arrayRequired } = fieldDef;
225
228
 
226
229
  let field = link;
@@ -237,6 +240,20 @@ function refFieldToGql(fieldDef: RefFieldDef): string {
237
240
  field += '!';
238
241
  }
239
242
 
243
+ for (const index of secondaryIndexes) {
244
+ field += ` ${index}`;
245
+ }
246
+
247
+ return field;
248
+ }
249
+
250
+ function enumFieldToGql(enumName: string, secondaryIndexes: string[] = []) {
251
+ let field = enumName;
252
+
253
+ for (const index of secondaryIndexes) {
254
+ field += ` ${index}`;
255
+ }
256
+
240
257
  return field;
241
258
  }
242
259
 
@@ -764,7 +781,7 @@ function processFields(
764
781
  );
765
782
  } else if (isRefField(fieldDef)) {
766
783
  gqlFields.push(
767
- `${fieldName}: ${refFieldToGql(fieldDef.data)}${fieldAuth}`,
784
+ `${fieldName}: ${refFieldToGql(fieldDef.data, secondaryIndexes[fieldName])}${fieldAuth}`,
768
785
  );
769
786
  } else if (isEnumType(fieldDef)) {
770
787
  // The inline enum type name should be `<TypeName><FieldName>` to avoid
@@ -773,7 +790,9 @@ function processFields(
773
790
 
774
791
  models.push([enumName, fieldDef]);
775
792
 
776
- gqlFields.push(`${fieldName}: ${enumName}`);
793
+ gqlFields.push(
794
+ `${fieldName}: ${enumFieldToGql(enumName, secondaryIndexes[fieldName])}`,
795
+ );
777
796
  } else if (isCustomType(fieldDef)) {
778
797
  // The inline CustomType name should be `<TypeName><FieldName>` to avoid
779
798
  // CustomType name conflicts
@@ -830,6 +849,8 @@ const secondaryIndexDefaultQueryField = (
830
849
  const transformedSecondaryIndexesForModel = (
831
850
  modelName: string,
832
851
  secondaryIndexes: readonly InternalModelIndexType[],
852
+ modelFields: Record<string, ModelField<any, any>>,
853
+ getRefType: ReturnType<typeof getRefTypeForSchema>,
833
854
  ): TransformedSecondaryIndexes => {
834
855
  const indexDirectiveWithAttributes = (
835
856
  partitionKey: string,
@@ -837,6 +858,19 @@ const transformedSecondaryIndexesForModel = (
837
858
  indexName: string,
838
859
  queryField: string,
839
860
  ): string => {
861
+ for (const keyName of [partitionKey, ...sortKeys]) {
862
+ const field = modelFields[keyName];
863
+
864
+ if (isRefField(field)) {
865
+ const { def } = getRefType(field.data.link, modelName);
866
+ if (!isEnumType(def)) {
867
+ throw new Error(
868
+ `The ref field \`${keyName}\` used in the secondary index of \`${modelName}\` should refer to an enum type. \`${field.data.link}\` is not a enum type.`,
869
+ );
870
+ }
871
+ }
872
+ }
873
+
840
874
  if (!sortKeys.length && !indexName && !queryField) {
841
875
  return `@index(queryField: "${secondaryIndexDefaultQueryField(
842
876
  modelName,
@@ -1172,6 +1206,8 @@ const schemaPreprocessor = (
1172
1206
  const transformedSecondaryIndexes = transformedSecondaryIndexesForModel(
1173
1207
  typeName,
1174
1208
  typeDef.data.secondaryIndexes,
1209
+ fields,
1210
+ getRefType,
1175
1211
  );
1176
1212
 
1177
1213
  const { authString, authFields } = calculateAuth(mostRelevantAuthRules);
@@ -11,6 +11,7 @@ import {
11
11
  IsEmptyStringOrNever,
12
12
  } from '@aws-amplify/data-schema-types';
13
13
  import type { Observable } from 'rxjs';
14
+ import { deferredRefResolvingPrefix } from '../../ModelType';
14
15
 
15
16
  // temporarily export symbols from `data-schema-types` because in case part of the
16
17
  // problem with the runtime -> data-schema migration comes down to a mismatch
@@ -445,17 +446,17 @@ type SizeFilter = {
445
446
  /**
446
447
  * Filters options that can be used on string-like fields.
447
448
  */
448
- type StringFilter = {
449
+ type StringFilter<T extends string = string> = {
449
450
  attributeExists?: boolean;
450
451
  beginsWith?: string;
451
452
  between?: [string, string];
452
453
  contains?: string;
453
- eq?: string;
454
+ eq?: T;
454
455
  ge?: string;
455
456
  gt?: string;
456
457
  le?: string;
457
458
  lt?: string;
458
- ne?: string;
459
+ ne?: T;
459
460
  notContains?: string;
460
461
  size?: SizeFilter;
461
462
  };
@@ -499,8 +500,14 @@ export type ModelTypesClient<
499
500
  ModelName extends string,
500
501
  Model extends Record<string, unknown>,
501
502
  ModelMeta extends ModelMetaShape,
503
+ Enums extends Record<string, string>,
502
504
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
503
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
505
+ > = IndexQueryMethodsFromIR<
506
+ ModelMeta['secondaryIndexes'],
507
+ ModelName,
508
+ Model,
509
+ Enums
510
+ > & {
504
511
  create: (
505
512
  model: Prettify<CreateModelInput<Model, ModelMeta>>,
506
513
  options?: {
@@ -595,8 +602,14 @@ type ModelTypesSSRCookies<
595
602
  ModelName extends string,
596
603
  Model extends Record<string, unknown>,
597
604
  ModelMeta extends ModelMetaShape,
605
+ Enums extends Record<string, string>,
598
606
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
599
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
607
+ > = IndexQueryMethodsFromIR<
608
+ ModelMeta['secondaryIndexes'],
609
+ ModelName,
610
+ Model,
611
+ Enums
612
+ > & {
600
613
  create: (
601
614
  model: Prettify<CreateModelInput<Model, ModelMeta>>,
602
615
  options?: {
@@ -648,8 +661,14 @@ type ModelTypesSSRRequest<
648
661
  ModelName extends string,
649
662
  Model extends Record<string, unknown>,
650
663
  ModelMeta extends ModelMetaShape,
664
+ Enums extends Record<string, string>,
651
665
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
652
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
666
+ > = IndexQueryMethodsFromIR<
667
+ ModelMeta['secondaryIndexes'],
668
+ ModelName,
669
+ Model,
670
+ Enums
671
+ > & {
653
672
  create: (
654
673
  // TODO: actual type
655
674
  contextSpec: any,
@@ -724,19 +743,22 @@ export type ModelTypes<
724
743
  ? ModelTypesClient<
725
744
  ModelName,
726
745
  Schema[ModelName]['type'],
727
- ModelMeta[ModelName]
746
+ ModelMeta[ModelName],
747
+ ModelMeta['enums']
728
748
  >
729
749
  : Context extends 'COOKIES'
730
750
  ? ModelTypesSSRCookies<
731
751
  ModelName,
732
752
  Schema[ModelName]['type'],
733
- ModelMeta[ModelName]
753
+ ModelMeta[ModelName],
754
+ ModelMeta['enums']
734
755
  >
735
756
  : Context extends 'REQUEST'
736
757
  ? ModelTypesSSRRequest<
737
758
  ModelName,
738
759
  Schema[ModelName]['type'],
739
- ModelMeta[ModelName]
760
+ ModelMeta[ModelName],
761
+ ModelMeta['enums']
740
762
  >
741
763
  : never
742
764
  : never
@@ -874,6 +896,7 @@ type IndexQueryMethodsFromIR<
874
896
  SecondaryIdxTuple extends SecondaryIndexIrShape[],
875
897
  ModelName extends string,
876
898
  Model extends Record<string, unknown>,
899
+ Enums extends Record<string, string>,
877
900
  Res = unknown, // defaulting `unknown` because it gets absorbed in an intersection, e.g. `{a: 1} & unknown` => `{a: 1}`
878
901
  > = SecondaryIdxTuple extends [
879
902
  infer A extends SecondaryIndexIrShape,
@@ -883,7 +906,8 @@ type IndexQueryMethodsFromIR<
883
906
  B,
884
907
  ModelName,
885
908
  Model,
886
- IndexQueryMethodSignature<A, ModelName, Model> & Res
909
+ Enums,
910
+ IndexQueryMethodSignature<A, ModelName, Model, Enums> & Res
887
911
  >
888
912
  : Res;
889
913
 
@@ -891,6 +915,7 @@ type IndexQueryMethodSignature<
891
915
  Idx extends SecondaryIndexIrShape,
892
916
  ModelName extends string,
893
917
  Model extends Record<string, unknown>,
918
+ Enums extends Record<string, string>,
894
919
  > = Record<
895
920
  IsEmptyStringOrNever<Idx['queryField']> extends false
896
921
  ? Idx['queryField']
@@ -899,10 +924,16 @@ type IndexQueryMethodSignature<
899
924
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
900
925
  SelectionSet extends ReadonlyArray<ModelPath<FlatModel>> = never[],
901
926
  >(
902
- input: Idx['pk'] & {
903
- [SKField in keyof Idx['sk']]+?: string extends Idx['sk'][SKField]
904
- ? StringFilter
905
- : NumericFilter;
927
+ input: {
928
+ [PKField in keyof Idx['pk']]: Idx['pk'][PKField] extends `${deferredRefResolvingPrefix}${infer R}`
929
+ ? Enums[R]
930
+ : Idx['pk'][PKField];
931
+ } & {
932
+ [SKField in keyof Idx['sk']]+?: number extends Idx['sk'][SKField]
933
+ ? NumericFilter
934
+ : Idx['sk'][SKField] extends `${deferredRefResolvingPrefix}${infer R}`
935
+ ? StringFilter<Enums[R]>
936
+ : StringFilter<Idx['sk'][SKField] & string>;
906
937
  },
907
938
  options?: {
908
939
  filter?: ModelFilter<Model>;
@@ -692,14 +692,23 @@ export function generateGraphQLDocument(
692
692
  graphQLFieldName = queryField;
693
693
 
694
694
  const skQueryArgs = sk.reduce((acc: Record<string, any>, fieldName) => {
695
- const fieldType = fields[fieldName].type;
695
+ const fieldType = Object.prototype.hasOwnProperty.call(
696
+ fields[fieldName].type,
697
+ 'enum',
698
+ )
699
+ ? 'String' // AppSync schema sets `ModelStringKeyConditionInput` as the type of the enum field that's used as SK
700
+ : fields[fieldName].type;
696
701
  acc[fieldName] = `Model${fieldType}KeyConditionInput`;
697
702
 
698
703
  return acc;
699
704
  }, {});
700
705
 
701
706
  indexQueryArgs = {
702
- [pk]: `${fields[pk].type}!`,
707
+ [pk]: `${
708
+ Object.prototype.hasOwnProperty.call(fields[pk].type, 'enum')
709
+ ? (fields[pk].type as any).enum // AppSync schema sets enum type as the type of the enum fields that's used as PK
710
+ : fields[pk].type
711
+ }!`,
703
712
  ...skQueryArgs,
704
713
  };
705
714
  } else {