@aws-amplify/data-schema 0.18.1 → 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.1",
3
+ "version": "0.18.3",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,11 +10,11 @@ const __data = Symbol('data');
10
10
  *
11
11
  * This list should not be used if you need to restrict available providers
12
12
  * according to an auth strategcy. E.g., `public` auth can only be facilitated
13
- * by `apiKey` and `iam` providers.
13
+ * by `apiKey` and `identityPool` providers.
14
14
  */
15
15
  export const Providers = [
16
16
  'apiKey',
17
- 'iam',
17
+ 'identityPool',
18
18
  'userPools',
19
19
  'oidc',
20
20
  'function',
@@ -24,13 +24,13 @@ export type Provider = (typeof Providers)[number];
24
24
  /**
25
25
  * The subset of auth providers that can facilitate `public` auth.
26
26
  */
27
- export const PublicProviders = ['apiKey', 'iam'] as const;
27
+ export const PublicProviders = ['apiKey', 'identityPool'] as const;
28
28
  export type PublicProvider = (typeof PublicProviders)[number];
29
29
 
30
30
  /**
31
31
  * The subset of auth providers that can facilitate `private` auth.
32
32
  */
33
- export const PrivateProviders = ['userPools', 'oidc', 'iam'] as const;
33
+ export const PrivateProviders = ['userPools', 'oidc', 'identityPool'] as const;
34
34
  export type PrivateProvider = (typeof PrivateProviders)[number];
35
35
 
36
36
  /**
@@ -205,7 +205,7 @@ function authData<
205
205
 
206
206
  /**
207
207
  * Defines an authorization rule for your data models and fields. First choose an authorization strategy (`public`,
208
- * `private`, `owner`, `group`, or `custom`), then choose an auth provider (`apiKey`, `iam`, `userPools`, `oidc`, or `function`)
208
+ * `private`, `owner`, `group`, or `custom`), then choose an auth provider (`apiKey`, `identitypool`, `userPools`, `oidc`, or `function`)
209
209
  * and optionally use `.to(...)` to specify the operations that can be performed against your data models and fields.
210
210
  */
211
211
  export const allow = {
@@ -226,14 +226,14 @@ export const allow = {
226
226
  },
227
227
 
228
228
  /**
229
- * Authorize unauthenticated users by using IAM based authorization.
229
+ * Authorize unauthenticated users by using IDENTITYPOOL based authorization.
230
230
  * @returns an authorization rule for unauthenticated users
231
231
  */
232
232
  guest() {
233
233
  return authData(
234
234
  {
235
235
  strategy: 'public',
236
- provider: 'iam',
236
+ provider: 'identityPool',
237
237
  },
238
238
  {
239
239
  to,
@@ -242,9 +242,9 @@ export const allow = {
242
242
  },
243
243
 
244
244
  /**
245
- * Authorize authenticated users. By default, `.private()` uses an Amazon Cognito user pool based authorization. You can additionally
246
- * use `.authenticated("iam")` or `.authenticated("oidc")` to use IAM or OIDC based authorization for authenticated users.
247
- * @param provider the authentication provider - supports "userPools", "iam", or "oidc"
245
+ * Authorize authenticated users. By default, `.authenticated()` uses an Amazon Cognito user pool based authorization. You can additionally
246
+ * use `.authenticated("identityPool")` or `.authenticated("oidc")` to use identityPool or OIDC based authorization for authenticated users.
247
+ * @param provider the authentication provider - supports "userPools", "identityPool", or "oidc"
248
248
  * @returns an authorization rule for authenticated users
249
249
  */
250
250
  authenticated(provider?: PrivateProvider) {
@@ -272,7 +272,7 @@ export const allow = {
272
272
  * To change the specific claim that should be used as the user identifier within the owner field, chain the
273
273
  * `.identityClaim(...)` method.
274
274
  *
275
- * @param provider the authentication provider - supports "userPools", "iam", or "oidc"
275
+ * @param provider the authentication provider - supports "userPools", "identityPool", or "oidc"
276
276
  * @returns an authorization rule for authenticated users
277
277
  */
278
278
  owner(provider?: OwnerProviders) {
@@ -300,7 +300,7 @@ export const allow = {
300
300
  * `.identityClaim(...)` method.
301
301
  *
302
302
  * @param ownerField the field that contains the owner information
303
- * @param provider the authentication provider - supports "userPools", "iam", or "oidc"
303
+ * @param provider the authentication provider - supports "userPools", "identityPool", or "oidc"
304
304
  * @returns an authorization rule for authenticated users
305
305
  */
306
306
  ownerDefinedIn<T extends string>(ownerField: T, provider?: OwnerProviders) {
@@ -333,7 +333,7 @@ export const allow = {
333
333
  * `.identityClaim(...)` method.
334
334
  *
335
335
  * @param ownersField the field that contains the owners information
336
- * @param provider the authentication provider - supports "userPools", "iam", or "oidc"
336
+ * @param provider the authentication provider - supports "userPools", "identityPool", or "oidc"
337
337
  * @returns an authorization rule for authenticated users
338
338
  */
339
339
  ownersDefinedIn<T extends string>(ownersField: T, provider?: OwnerProviders) {
@@ -506,14 +506,14 @@ export const allowForCustomOperations = {
506
506
  },
507
507
 
508
508
  /**
509
- * Authorize unauthenticated users by using IAM based authorization.
509
+ * Authorize unauthenticated users by using identityPool based authorization.
510
510
  * @returns an authorization rule for unauthenticated users
511
511
  */
512
512
  guest() {
513
513
  return authData(
514
514
  {
515
515
  strategy: 'public',
516
- provider: 'iam',
516
+ provider: 'identityPool',
517
517
  },
518
518
  {},
519
519
  );
@@ -521,8 +521,8 @@ export const allowForCustomOperations = {
521
521
 
522
522
  /**
523
523
  * Authorize authenticated users. By default, `.private()` uses an Amazon Cognito user pool based authorization. You can additionally
524
- * use `.authenticated("iam")` or `.authenticated("oidc")` to use IAM or OIDC based authorization for authenticated users.
525
- * @param provider the authentication provider - supports "userPools", "iam", or "oidc"
524
+ * use `.authenticated("identityPool")` or `.authenticated("oidc")` to use Identity Pool or OIDC based authorization for authenticated users.
525
+ * @param provider the authentication provider - supports "userPools", "identityPool", or "oidc"
526
526
  * @returns an authorization rule for authenticated users
527
527
  */
528
528
  authenticated(provider?: PrivateProvider) {
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
 
@@ -571,7 +588,9 @@ function calculateAuth(authorization: Authorization<any, any, any>[]) {
571
588
  }
572
589
 
573
590
  if (rule.provider) {
574
- ruleParts.push(`provider: ${rule.provider}`);
591
+ // identityPool maps to iam in the transform
592
+ const provider = rule.provider === 'identityPool' ? 'iam' : rule.provider;
593
+ ruleParts.push(`provider: ${provider}`);
575
594
  }
576
595
 
577
596
  if (rule.operations) {
@@ -762,7 +781,7 @@ function processFields(
762
781
  );
763
782
  } else if (isRefField(fieldDef)) {
764
783
  gqlFields.push(
765
- `${fieldName}: ${refFieldToGql(fieldDef.data)}${fieldAuth}`,
784
+ `${fieldName}: ${refFieldToGql(fieldDef.data, secondaryIndexes[fieldName])}${fieldAuth}`,
766
785
  );
767
786
  } else if (isEnumType(fieldDef)) {
768
787
  // The inline enum type name should be `<TypeName><FieldName>` to avoid
@@ -771,7 +790,9 @@ function processFields(
771
790
 
772
791
  models.push([enumName, fieldDef]);
773
792
 
774
- gqlFields.push(`${fieldName}: ${enumName}`);
793
+ gqlFields.push(
794
+ `${fieldName}: ${enumFieldToGql(enumName, secondaryIndexes[fieldName])}`,
795
+ );
775
796
  } else if (isCustomType(fieldDef)) {
776
797
  // The inline CustomType name should be `<TypeName><FieldName>` to avoid
777
798
  // CustomType name conflicts
@@ -828,6 +849,8 @@ const secondaryIndexDefaultQueryField = (
828
849
  const transformedSecondaryIndexesForModel = (
829
850
  modelName: string,
830
851
  secondaryIndexes: readonly InternalModelIndexType[],
852
+ modelFields: Record<string, ModelField<any, any>>,
853
+ getRefType: ReturnType<typeof getRefTypeForSchema>,
831
854
  ): TransformedSecondaryIndexes => {
832
855
  const indexDirectiveWithAttributes = (
833
856
  partitionKey: string,
@@ -835,6 +858,19 @@ const transformedSecondaryIndexesForModel = (
835
858
  indexName: string,
836
859
  queryField: string,
837
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
+
838
874
  if (!sortKeys.length && !indexName && !queryField) {
839
875
  return `@index(queryField: "${secondaryIndexDefaultQueryField(
840
876
  modelName,
@@ -1170,6 +1206,8 @@ const schemaPreprocessor = (
1170
1206
  const transformedSecondaryIndexes = transformedSecondaryIndexesForModel(
1171
1207
  typeName,
1172
1208
  typeDef.data.secondaryIndexes,
1209
+ fields,
1210
+ getRefType,
1173
1211
  );
1174
1212
 
1175
1213
  const { authString, authFields } = calculateAuth(mostRelevantAuthRules);
@@ -174,6 +174,7 @@ export type GraphQLAuthMode =
174
174
  | 'oidc'
175
175
  | 'userPool'
176
176
  | 'iam'
177
+ | 'identityPool'
177
178
  | 'lambda'
178
179
  | 'none';
179
180
 
@@ -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
@@ -410,6 +411,7 @@ export type LazyLoader<Model, IsArray extends boolean> = (
410
411
  export type AuthMode =
411
412
  | 'apiKey'
412
413
  | 'iam'
414
+ | 'identityPool'
413
415
  | 'oidc'
414
416
  | 'userPool'
415
417
  | 'lambda'
@@ -444,17 +446,17 @@ type SizeFilter = {
444
446
  /**
445
447
  * Filters options that can be used on string-like fields.
446
448
  */
447
- type StringFilter = {
449
+ type StringFilter<T extends string = string> = {
448
450
  attributeExists?: boolean;
449
451
  beginsWith?: string;
450
452
  between?: [string, string];
451
453
  contains?: string;
452
- eq?: string;
454
+ eq?: T;
453
455
  ge?: string;
454
456
  gt?: string;
455
457
  le?: string;
456
458
  lt?: string;
457
- ne?: string;
459
+ ne?: T;
458
460
  notContains?: string;
459
461
  size?: SizeFilter;
460
462
  };
@@ -498,8 +500,14 @@ export type ModelTypesClient<
498
500
  ModelName extends string,
499
501
  Model extends Record<string, unknown>,
500
502
  ModelMeta extends ModelMetaShape,
503
+ Enums extends Record<string, string>,
501
504
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
502
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
505
+ > = IndexQueryMethodsFromIR<
506
+ ModelMeta['secondaryIndexes'],
507
+ ModelName,
508
+ Model,
509
+ Enums
510
+ > & {
503
511
  create: (
504
512
  model: Prettify<CreateModelInput<Model, ModelMeta>>,
505
513
  options?: {
@@ -594,8 +602,14 @@ type ModelTypesSSRCookies<
594
602
  ModelName extends string,
595
603
  Model extends Record<string, unknown>,
596
604
  ModelMeta extends ModelMetaShape,
605
+ Enums extends Record<string, string>,
597
606
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
598
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
607
+ > = IndexQueryMethodsFromIR<
608
+ ModelMeta['secondaryIndexes'],
609
+ ModelName,
610
+ Model,
611
+ Enums
612
+ > & {
599
613
  create: (
600
614
  model: Prettify<CreateModelInput<Model, ModelMeta>>,
601
615
  options?: {
@@ -647,8 +661,14 @@ type ModelTypesSSRRequest<
647
661
  ModelName extends string,
648
662
  Model extends Record<string, unknown>,
649
663
  ModelMeta extends ModelMetaShape,
664
+ Enums extends Record<string, string>,
650
665
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
651
- > = IndexQueryMethodsFromIR<ModelMeta['secondaryIndexes'], ModelName, Model> & {
666
+ > = IndexQueryMethodsFromIR<
667
+ ModelMeta['secondaryIndexes'],
668
+ ModelName,
669
+ Model,
670
+ Enums
671
+ > & {
652
672
  create: (
653
673
  // TODO: actual type
654
674
  contextSpec: any,
@@ -723,19 +743,22 @@ export type ModelTypes<
723
743
  ? ModelTypesClient<
724
744
  ModelName,
725
745
  Schema[ModelName]['type'],
726
- ModelMeta[ModelName]
746
+ ModelMeta[ModelName],
747
+ ModelMeta['enums']
727
748
  >
728
749
  : Context extends 'COOKIES'
729
750
  ? ModelTypesSSRCookies<
730
751
  ModelName,
731
752
  Schema[ModelName]['type'],
732
- ModelMeta[ModelName]
753
+ ModelMeta[ModelName],
754
+ ModelMeta['enums']
733
755
  >
734
756
  : Context extends 'REQUEST'
735
757
  ? ModelTypesSSRRequest<
736
758
  ModelName,
737
759
  Schema[ModelName]['type'],
738
- ModelMeta[ModelName]
760
+ ModelMeta[ModelName],
761
+ ModelMeta['enums']
739
762
  >
740
763
  : never
741
764
  : never
@@ -873,6 +896,7 @@ type IndexQueryMethodsFromIR<
873
896
  SecondaryIdxTuple extends SecondaryIndexIrShape[],
874
897
  ModelName extends string,
875
898
  Model extends Record<string, unknown>,
899
+ Enums extends Record<string, string>,
876
900
  Res = unknown, // defaulting `unknown` because it gets absorbed in an intersection, e.g. `{a: 1} & unknown` => `{a: 1}`
877
901
  > = SecondaryIdxTuple extends [
878
902
  infer A extends SecondaryIndexIrShape,
@@ -882,7 +906,8 @@ type IndexQueryMethodsFromIR<
882
906
  B,
883
907
  ModelName,
884
908
  Model,
885
- IndexQueryMethodSignature<A, ModelName, Model> & Res
909
+ Enums,
910
+ IndexQueryMethodSignature<A, ModelName, Model, Enums> & Res
886
911
  >
887
912
  : Res;
888
913
 
@@ -890,6 +915,7 @@ type IndexQueryMethodSignature<
890
915
  Idx extends SecondaryIndexIrShape,
891
916
  ModelName extends string,
892
917
  Model extends Record<string, unknown>,
918
+ Enums extends Record<string, string>,
893
919
  > = Record<
894
920
  IsEmptyStringOrNever<Idx['queryField']> extends false
895
921
  ? Idx['queryField']
@@ -898,10 +924,16 @@ type IndexQueryMethodSignature<
898
924
  FlatModel extends Record<string, unknown> = ResolvedModel<Model>,
899
925
  SelectionSet extends ReadonlyArray<ModelPath<FlatModel>> = never[],
900
926
  >(
901
- input: Idx['pk'] & {
902
- [SKField in keyof Idx['sk']]+?: string extends Idx['sk'][SKField]
903
- ? StringFilter
904
- : 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>;
905
937
  },
906
938
  options?: {
907
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 {