@aws-amplify/graphql-model-transformer 0.7.0-auth-dir-v-next.4 → 0.8.0-beta.0

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.
@@ -13,6 +13,7 @@ import {
13
13
  verifyInputCount,
14
14
  verifyMatchingTypes,
15
15
  } from './test-utils/helpers';
16
+ import { expect as cdkExpect, haveResource } from '@aws-cdk/assert';
16
17
 
17
18
  const featureFlags = {
18
19
  getBoolean: jest.fn(),
@@ -912,7 +913,7 @@ describe('ModelTransformer: ', () => {
912
913
  validateModelSchema(parse(definition));
913
914
  });
914
915
 
915
- it('should generate sync resolver with ConflictHandlerType.Lambda', () => {
916
+ it('should generate sync resolver with ConflictHandlerType.LAMBDA', () => {
916
917
  const validSchema = `
917
918
  type Post @model {
918
919
  id: ID!
@@ -983,24 +984,76 @@ describe('ModelTransformer: ', () => {
983
984
  validateModelSchema(parse(definition));
984
985
  });
985
986
 
986
- it('should support sandbox mode of api', async () => {
987
+ it('should generate iam role names under 64 chars and subscriptions under 50', () => {
987
988
  const validSchema = `
988
- type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: "staging")
989
-
990
- type Post @model {
989
+ type ThisIsAVeryLongNameModelThatShouldNotGenerateIAMRoleNamesOver64Characters @model {
991
990
  id: ID!
992
991
  title: String!
993
992
  }
994
993
  `;
995
994
 
995
+ const config: SyncConfig = {
996
+ ConflictDetection: 'VERSION',
997
+ ConflictHandler: ConflictHandlerType.AUTOMERGE,
998
+ };
999
+
996
1000
  const transformer = new GraphQLTransform({
997
1001
  transformers: [new ModelTransformer()],
998
1002
  featureFlags,
1003
+ transformConfig: {
1004
+ ResolverConfig: {
1005
+ project: config,
1006
+ },
1007
+ },
999
1008
  });
1000
-
1001
1009
  const out = transformer.transform(validSchema);
1002
1010
  expect(out).toBeDefined();
1003
1011
 
1004
- parse(out.schema);
1012
+ const definition = out.schema;
1013
+ expect(definition).toBeDefined();
1014
+
1015
+ const parsed = parse(definition);
1016
+ const subscriptionType = getObjectType(parsed, 'Subscription');
1017
+ expect(subscriptionType).toBeDefined();
1018
+
1019
+ subscriptionType!.fields!.forEach(it => {
1020
+ expect(it.name.value.length <= 50).toBeTruthy();
1021
+ });
1022
+
1023
+ const iamStackResource = out.stacks.ThisIsAVeryLongNameModelThatShouldNotGenerateIAMRoleNamesOver64Characters;
1024
+ expect(iamStackResource).toBeDefined();
1025
+ cdkExpect(iamStackResource).to(
1026
+ haveResource('AWS::IAM::Role', {
1027
+ AssumeRolePolicyDocument: {
1028
+ Statement: [
1029
+ {
1030
+ Action: 'sts:AssumeRole',
1031
+ Effect: 'Allow',
1032
+ Principal: {
1033
+ Service: 'appsync.amazonaws.com',
1034
+ },
1035
+ },
1036
+ ],
1037
+ Version: '2012-10-17',
1038
+ },
1039
+ RoleName: {
1040
+ 'Fn::Join': [
1041
+ '',
1042
+ [
1043
+ 'ThisIsAVeryLongNameM2d9fca-',
1044
+ {
1045
+ Ref: 'referencetotransformerrootstackGraphQLAPI20497F53ApiId',
1046
+ },
1047
+ '-',
1048
+ {
1049
+ Ref: 'referencetotransformerrootstackenv10C5A902Ref',
1050
+ },
1051
+ ],
1052
+ ],
1053
+ },
1054
+ }),
1055
+ );
1056
+
1057
+ validateModelSchema(parsed);
1005
1058
  });
1006
1059
  });
@@ -78,6 +78,7 @@ import {
78
78
  ObjectDefinitionWrapper,
79
79
  } from './wrappers/object-definition-wrapper';
80
80
  import { CfnRole } from '@aws-cdk/aws-iam';
81
+ import md5 from 'md5';
81
82
 
82
83
  export type Nullable<T> = T | null;
83
84
  export type OptionalAndNullable<T> = Partial<T>;
@@ -176,14 +177,17 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
176
177
  }
177
178
  // todo: get model configuration with default values and store it in the map
178
179
  const typeName = definition.name.value;
180
+
181
+ if (ctx.isProjectUsingDataStore()) {
182
+ SyncUtils.validateResolverConfigForType(ctx, typeName);
183
+ }
184
+
179
185
  const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive);
180
186
  const options = directiveWrapped.getArguments({
181
187
  queries: {
182
188
  get: toCamelCase(['get', typeName]),
183
189
  list: toCamelCase(['list', plurality(typeName, true)]),
184
- ...((ctx as TransformerContextProvider).isProjectUsingDataStore()
185
- ? { sync: toCamelCase(['sync', plurality(typeName, true)]) }
186
- : undefined),
190
+ ...(ctx.isProjectUsingDataStore() ? { sync: toCamelCase(['sync', plurality(typeName, true)]) } : undefined),
187
191
  },
188
192
  mutations: {
189
193
  create: toCamelCase(['create', typeName]),
@@ -192,9 +196,9 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
192
196
  },
193
197
  subscriptions: {
194
198
  level: SubscriptionLevel.on,
195
- onCreate: [toCamelCase(['onCreate', typeName])],
196
- onDelete: [toCamelCase(['onDelete', typeName])],
197
- onUpdate: [toCamelCase(['onUpdate', typeName])],
199
+ onCreate: [this.ensureValidSubscriptionName(toCamelCase(['onCreate', typeName]))],
200
+ onDelete: [this.ensureValidSubscriptionName(toCamelCase(['onDelete', typeName]))],
201
+ onUpdate: [this.ensureValidSubscriptionName(toCamelCase(['onUpdate', typeName]))],
198
202
  },
199
203
  timestamps: {
200
204
  createdAt: 'createdAt',
@@ -237,7 +241,6 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
237
241
  this.addAutoGeneratableFields(ctx, type);
238
242
 
239
243
  if (ctx.isProjectUsingDataStore()) {
240
- this.options.SyncConfig = SyncUtils.getSyncConfig(ctx as TransformerContextProvider, def!.name.value);
241
244
  this.addModelSyncFields(ctx, type);
242
245
  }
243
246
  }
@@ -1072,7 +1075,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1072
1075
  stream: StreamViewType.NEW_AND_OLD_IMAGES,
1073
1076
  encryption: TableEncryption.DEFAULT,
1074
1077
  removalPolicy: removalPolicy,
1075
- ...(this.options.SyncConfig ? { timeToLiveAttribute: '_ttl' } : undefined),
1078
+ ...(context.isProjectUsingDataStore() ? { timeToLiveAttribute: '_ttl' } : undefined),
1076
1079
  });
1077
1080
  const cfnTable = table.node.defaultChild as CfnTable;
1078
1081
 
@@ -1129,7 +1132,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1129
1132
  const cfnDataSource = dataSource.node.defaultChild as CfnDataSource;
1130
1133
  cfnDataSource.addDependsOn(role.node.defaultChild as CfnRole);
1131
1134
 
1132
- if (this.options.SyncConfig) {
1135
+ if (context.isProjectUsingDataStore()) {
1133
1136
  const datasourceDynamoDb = cfnDataSource.dynamoDbConfig as any;
1134
1137
  datasourceDynamoDb.deltaSyncConfig = {
1135
1138
  deltaSyncTableName: context.resourceHelper.generateResourceName(SyncResourceIDs.syncTableName),
@@ -1153,7 +1156,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1153
1156
  }
1154
1157
 
1155
1158
  private createIAMRole(context: TransformerContextProvider, def: ObjectTypeDefinitionNode, stack: cdk.Stack, tableName: string) {
1156
- const roleName = context.resourceHelper.generateResourceName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1159
+ const roleName = context.resourceHelper.generateIAMRoleName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1157
1160
  const role = new iam.Role(stack, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), {
1158
1161
  roleName: roleName,
1159
1162
  assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
@@ -1184,7 +1187,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1184
1187
  cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', {
1185
1188
  tablename: tableName,
1186
1189
  }),
1187
- ...(this.options.SyncConfig
1190
+ ...(context.isProjectUsingDataStore()
1188
1191
  ? [
1189
1192
  // eslint-disable-next-line no-template-curly-in-string
1190
1193
  cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', {
@@ -1202,13 +1205,10 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1202
1205
  }),
1203
1206
  );
1204
1207
 
1205
- if (this.options.SyncConfig && SyncUtils.isLambdaSyncConfig(this.options.SyncConfig)) {
1208
+ const syncConfig = SyncUtils.getSyncConfig(context, def!.name.value);
1209
+ if (syncConfig && SyncUtils.isLambdaSyncConfig(syncConfig)) {
1206
1210
  role.attachInlinePolicy(
1207
- SyncUtils.createSyncLambdaIAMPolicy(
1208
- stack,
1209
- this.options.SyncConfig.LambdaConflictHandler.name,
1210
- this.options.SyncConfig.LambdaConflictHandler.region,
1211
- ),
1211
+ SyncUtils.createSyncLambdaIAMPolicy(stack, syncConfig.LambdaConflictHandler.name, syncConfig.LambdaConflictHandler.region),
1212
1212
  );
1213
1213
  }
1214
1214
  return role;
@@ -1228,4 +1228,10 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1228
1228
  ...options,
1229
1229
  };
1230
1230
  };
1231
+
1232
+ private ensureValidSubscriptionName = (name: string): string => {
1233
+ if (name.length <= 50) return name;
1234
+
1235
+ return name.slice(0, 45) + md5(name).slice(0, 5);
1236
+ };
1231
1237
  }
@@ -13,7 +13,7 @@ export const makeListQueryFilterInput = (
13
13
  export const makeListQueryModel = (type: ObjectTypeDefinitionNode, modelName: string, isSyncEnabled: boolean): ObjectTypeDefinitionNode => {
14
14
  const outputType = ObjectDefinitionWrapper.create(modelName);
15
15
 
16
- outputType.addField(FieldWrapper.create('items', type.name.value, true, true));
16
+ outputType.addField(FieldWrapper.create('items', type.name.value, false, true));
17
17
  outputType.addField(FieldWrapper.create('nextToken', 'String', true, false));
18
18
 
19
19
  if (isSyncEnabled) {
@@ -99,7 +99,7 @@ export const generateResolverKey = (typeName: string, fieldName: string): string
99
99
  * @param ctx context to get sandbox mode
100
100
  */
101
101
  export const generateAuthExpressionForSandboxMode = (ctx: any): string => {
102
- let enabled = ctx.resourceHelper.api.globalSandboxModeEnabled;
102
+ const enabled = ctx.resourceHelper.api.sandboxModeEnabled;
103
103
  let exp;
104
104
 
105
105
  if (enabled) exp = iff(notEquals(methodCall(ref('util.authType')), str(API_KEY)), methodCall(ref('util.unauthorized')));
@@ -152,8 +152,6 @@ export const generateUpdateRequestTemplate = (modelName: string, isSyncEnabled:
152
152
  */
153
153
  export const generateCreateRequestTemplate = (modelName: string): string => {
154
154
  const statements: Expression[] = [
155
- // set key the condition
156
- ...generateKeyConditionTemplate(false),
157
155
  // Generate conditions
158
156
  comment('Set the default values to put request'),
159
157
  set(ref('mergedValues'), methodCall(ref('util.defaultIfNull'), ref('ctx.stash.defaultValues'), obj({}))),
@@ -174,7 +172,6 @@ export const generateCreateRequestTemplate = (modelName: string): string => {
174
172
  ),
175
173
 
176
174
  // add conditions
177
-
178
175
  iff(ref('context.args.condition'), qref(methodCall(ref('ctx.stash.conditions.add'), ref('context.args.condition')))),
179
176
  // key conditions
180
177
  ...generateKeyConditionTemplate(false),
@@ -34,13 +34,18 @@ export const generateGetRequestTemplate = (): string => {
34
34
  ref('ctx.stash.metadata.modelObjectKey'),
35
35
  compoundExpression([
36
36
  set(ref('expression'), str('')),
37
+ set(ref('expressionNames'), obj({})),
37
38
  set(ref('expressionValues'), obj({})),
38
39
  forEach(ref('item'), ref('ctx.stash.metadata.modelObjectKey.entrySet()'), [
39
- set(ref('expression'), str('$expression$item.key = :$item.key AND ')),
40
- qref(methodCall(ref('expressionValues.put'), str(':$item.key'), ref('item.value'))),
40
+ set(ref('expression'), str('$expression#keyCount$velocityCount = :valueCount$velocityCount AND ')),
41
+ qref(methodCall(ref('expressionNames.put'), str('#keyCount$velocityCount'), ref('item.key'))),
42
+ qref(methodCall(ref('expressionValues.put'), str(':valueCount$velocityCount'), ref('item.value'))),
41
43
  ]),
42
44
  set(ref('expression'), methodCall(ref('expression.replaceAll'), str('AND $'), str(''))),
43
- set(ref('query'), obj({ expression: ref('expression'), expressionValues: ref('expressionValues') })),
45
+ set(
46
+ ref('query'),
47
+ obj({ expression: ref('expression'), expressionNames: ref('expressionNames'), expressionValues: ref('expressionValues') }),
48
+ ),
44
49
  ]),
45
50
  set(
46
51
  ref('query'),
@@ -109,10 +114,7 @@ export const generateListRequestTemplate = (): string => {
109
114
  not(isNullOrEmpty(authFilter)),
110
115
  compoundExpression([
111
116
  set(ref('filter'), authFilter),
112
- iff(
113
- not(isNullOrEmpty(ref('ctx.args.filter'))),
114
- set(ref('filter'), list([obj({ and: list([ref('filter'), ref('ctx.args.filter')]) })])),
115
- ),
117
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))),
116
118
  ]),
117
119
  iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))),
118
120
  ),
@@ -164,10 +166,7 @@ export const generateSyncRequestTemplate = (): string => {
164
166
  not(isNullOrEmpty(authFilter)),
165
167
  compoundExpression([
166
168
  set(ref('filter'), authFilter),
167
- iff(
168
- not(isNullOrEmpty(ref('ctx.args.filter'))),
169
- set(ref('filter'), list([obj({ and: list([ref('filter'), ref('ctx.args.filter')]) })])),
170
- ),
169
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))),
171
170
  ]),
172
171
  iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))),
173
172
  ),
@@ -7,5 +7,5 @@ export const generateSubscriptionRequestTemplate = (): string => {
7
7
 
8
8
  export const generateSubscriptionResponseTemplate = (): string => {
9
9
  const statements: Expression[] = [toJson(nul())];
10
- return printBlock('Subscription Resonse template')(compoundExpression(statements));
10
+ return printBlock('Subscription Response template')(compoundExpression(statements));
11
11
  };
@@ -295,7 +295,7 @@ export class FieldWrapper extends GenericFieldWrapper {
295
295
  field.makeNonNullable();
296
296
  }
297
297
  if (isList) {
298
- field.wrapListType();
298
+ field.wrapListType().makeNonNullable();
299
299
  }
300
300
  return field;
301
301
  };