@aws-amplify/graphql-model-transformer 0.7.0-auth-dir-v-next.2 → 0.7.0-graphql-vnext-dev-preview.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.
@@ -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(),
@@ -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,8 @@ 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);
244
+ SyncUtils.validateResolverConfigForType(ctx, def!.name.value);
245
+ this.options.SyncConfig = SyncUtils.getSyncConfig(ctx, def!.name.value);
241
246
  this.addModelSyncFields(ctx, type);
242
247
  }
243
248
  }
@@ -1072,7 +1077,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1072
1077
  stream: StreamViewType.NEW_AND_OLD_IMAGES,
1073
1078
  encryption: TableEncryption.DEFAULT,
1074
1079
  removalPolicy: removalPolicy,
1075
- ...(this.options.SyncConfig ? { timeToLiveAttribute: '_ttl' } : undefined),
1080
+ ...(context.isProjectUsingDataStore() ? { timeToLiveAttribute: '_ttl' } : undefined),
1076
1081
  });
1077
1082
  const cfnTable = table.node.defaultChild as CfnTable;
1078
1083
 
@@ -1129,7 +1134,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1129
1134
  const cfnDataSource = dataSource.node.defaultChild as CfnDataSource;
1130
1135
  cfnDataSource.addDependsOn(role.node.defaultChild as CfnRole);
1131
1136
 
1132
- if (this.options.SyncConfig) {
1137
+ if (context.isProjectUsingDataStore()) {
1133
1138
  const datasourceDynamoDb = cfnDataSource.dynamoDbConfig as any;
1134
1139
  datasourceDynamoDb.deltaSyncConfig = {
1135
1140
  deltaSyncTableName: context.resourceHelper.generateResourceName(SyncResourceIDs.syncTableName),
@@ -1153,7 +1158,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1153
1158
  }
1154
1159
 
1155
1160
  private createIAMRole(context: TransformerContextProvider, def: ObjectTypeDefinitionNode, stack: cdk.Stack, tableName: string) {
1156
- const roleName = context.resourceHelper.generateResourceName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1161
+ const roleName = context.resourceHelper.generateIAMRoleName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1157
1162
  const role = new iam.Role(stack, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), {
1158
1163
  roleName: roleName,
1159
1164
  assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
@@ -1184,7 +1189,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1184
1189
  cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', {
1185
1190
  tablename: tableName,
1186
1191
  }),
1187
- ...(this.options.SyncConfig
1192
+ ...(context.isProjectUsingDataStore()
1188
1193
  ? [
1189
1194
  // eslint-disable-next-line no-template-curly-in-string
1190
1195
  cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', {
@@ -1228,4 +1233,10 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1228
1233
  ...options,
1229
1234
  };
1230
1235
  };
1236
+
1237
+ private ensureValidSubscriptionName = (name: string): string => {
1238
+ if (name.length <= 50) return name;
1239
+
1240
+ return name.slice(0, 45) + md5(name).slice(0, 5);
1241
+ };
1231
1242
  }
@@ -99,13 +99,13 @@ 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')));
106
106
  else exp = methodCall(ref('util.unauthorized'));
107
107
 
108
108
  return printBlock(`Sandbox Mode ${enabled ? 'Enabled' : 'Disabled'}`)(
109
- compoundExpression([iff(not(ref('ctx.stash.get("hasAuth")')), exp)]),
109
+ compoundExpression([iff(not(ref('ctx.stash.get("hasAuth")')), exp), toJson(obj({}))]),
110
110
  );
111
111
  };
@@ -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
  };