@aws-amplify/graphql-model-transformer 0.6.3-headless-s3-not-for-production.0 → 0.6.5-amplify-export2.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(),
@@ -880,7 +881,7 @@ describe('ModelTransformer: ', () => {
880
881
  validateModelSchema(parse(out.schema));
881
882
  });
882
883
 
883
- it('should generate sync resolver with ConflictHandlerType.AUTOMERGE', () => {
884
+ it('should generate sync resolver with ConflictHandlerType.Automerge', () => {
884
885
  const validSchema = `
885
886
  type Post @model {
886
887
  id: ID!
@@ -949,7 +950,7 @@ describe('ModelTransformer: ', () => {
949
950
  validateModelSchema(parse(definition));
950
951
  });
951
952
 
952
- it('should generate sync resolver with ConflictHandlerType.OPTIMISTIC', () => {
953
+ it('should generate sync resolver with ConflictHandlerType.Optimistic', () => {
953
954
  const validSchema = `
954
955
  type Post @model {
955
956
  id: ID!
@@ -982,4 +983,77 @@ describe('ModelTransformer: ', () => {
982
983
 
983
984
  validateModelSchema(parse(definition));
984
985
  });
986
+
987
+ it('should generate iam role names under 64 chars and subscriptions under 50', () => {
988
+ const validSchema = `
989
+ type ThisIsAVeryLongNameModelThatShouldNotGenerateIAMRoleNamesOver64Characters @model {
990
+ id: ID!
991
+ title: String!
992
+ }
993
+ `;
994
+
995
+ const config: SyncConfig = {
996
+ ConflictDetection: 'VERSION',
997
+ ConflictHandler: ConflictHandlerType.AUTOMERGE,
998
+ };
999
+
1000
+ const transformer = new GraphQLTransform({
1001
+ transformers: [new ModelTransformer()],
1002
+ featureFlags,
1003
+ transformConfig: {
1004
+ ResolverConfig: {
1005
+ project: config,
1006
+ },
1007
+ },
1008
+ });
1009
+ const out = transformer.transform(validSchema);
1010
+ expect(out).toBeDefined();
1011
+
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);
1058
+ });
985
1059
  });
@@ -13,4 +13,4 @@ export const BOOLEAN_FUNCTIONS = new Set<string>(['attributeExists', 'attributeT
13
13
 
14
14
  export const ATTRIBUTE_TYPES = ['binary', 'binarySet', 'bool', 'list', 'map', 'number', 'numberSet', 'string', 'stringSet', '_null'];
15
15
 
16
-
16
+ export const OPERATION_KEY = '__operation';
@@ -54,6 +54,7 @@ import {
54
54
  makeUpdateInputField,
55
55
  } from './graphql-types';
56
56
  import {
57
+ generateAuthExpressionForSandboxMode,
57
58
  generateCreateInitSlotTemplate,
58
59
  generateCreateRequestTemplate,
59
60
  generateDefaultResponseMappingTemplate,
@@ -64,7 +65,12 @@ import {
64
65
  generateUpdateInitSlotTemplate,
65
66
  generateUpdateRequestTemplate,
66
67
  } from './resolvers';
67
- import { generateGetRequestTemplate, generateListRequestTemplate, generateSyncRequestTemplate } from './resolvers/query';
68
+ import {
69
+ generateGetRequestTemplate,
70
+ generateGetResponseTemplate,
71
+ generateListRequestTemplate,
72
+ generateSyncRequestTemplate,
73
+ } from './resolvers/query';
68
74
  import {
69
75
  DirectiveWrapper,
70
76
  FieldWrapper,
@@ -72,6 +78,7 @@ import {
72
78
  ObjectDefinitionWrapper,
73
79
  } from './wrappers/object-definition-wrapper';
74
80
  import { CfnRole } from '@aws-cdk/aws-iam';
81
+ import md5 from 'md5';
75
82
 
76
83
  export type Nullable<T> = T | null;
77
84
  export type OptionalAndNullable<T> = Partial<T>;
@@ -87,17 +94,17 @@ export type ModelDirectiveConfiguration = {
87
94
  list: OptionalAndNullable<string>;
88
95
  sync: OptionalAndNullable<string>;
89
96
  }>;
90
- mutations: {
97
+ mutations: OptionalAndNullable<{
91
98
  create: OptionalAndNullable<string>;
92
99
  update: OptionalAndNullable<string>;
93
100
  delete: OptionalAndNullable<string>;
94
- } | null;
95
- subscriptions: {
101
+ }>;
102
+ subscriptions: OptionalAndNullable<{
96
103
  onCreate: OptionalAndNullable<string>[];
97
104
  onUpdate: OptionalAndNullable<string>[];
98
105
  onDelete: OptionalAndNullable<string>[];
99
- level: Partial<SubscriptionLevel>;
100
- } | null;
106
+ level: SubscriptionLevel;
107
+ }>;
101
108
  timestamps: OptionalAndNullable<{
102
109
  createdAt: OptionalAndNullable<string>;
103
110
  updatedAt: OptionalAndNullable<string>;
@@ -168,10 +175,13 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
168
175
  `'${definition.name.value}' is a reserved type name and currently in use within the default schema element.`,
169
176
  );
170
177
  }
171
-
172
178
  // todo: get model configuration with default values and store it in the map
173
179
  const typeName = definition.name.value;
174
- SyncUtils.validateResolverConfigForType(ctx, typeName);
180
+
181
+ if (ctx.isProjectUsingDataStore()) {
182
+ SyncUtils.validateResolverConfigForType(ctx, typeName);
183
+ }
184
+
175
185
  const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive);
176
186
  const options = directiveWrapped.getArguments({
177
187
  queries: {
@@ -185,10 +195,10 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
185
195
  delete: toCamelCase(['delete', typeName]),
186
196
  },
187
197
  subscriptions: {
188
- level: SubscriptionLevel.public,
189
- onCreate: [toCamelCase(['onCreate', typeName])],
190
- onDelete: [toCamelCase(['onDelete', typeName])],
191
- onUpdate: [toCamelCase(['onUpdate', typeName])],
198
+ level: SubscriptionLevel.on,
199
+ onCreate: [this.ensureValidSubscriptionName(toCamelCase(['onCreate', typeName]))],
200
+ onDelete: [this.ensureValidSubscriptionName(toCamelCase(['onDelete', typeName]))],
201
+ onUpdate: [this.ensureValidSubscriptionName(toCamelCase(['onUpdate', typeName]))],
192
202
  },
193
203
  timestamps: {
194
204
  createdAt: 'createdAt',
@@ -238,8 +248,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
238
248
 
239
249
  generateResolvers = (context: TransformerContextProvider): void => {
240
250
  for (let type of this.typesWithModelDirective) {
241
- const def = context.output.getObject(type);
242
-
251
+ const def = context.output.getObject(type)!;
243
252
  // This name is used by the mock functionality. Changing this can break mock.
244
253
  const tableLogicalName = `${def!.name.value}Table`;
245
254
  const stack = context.stackManager.getStackFor(tableLogicalName, def!.name.value);
@@ -262,7 +271,13 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
262
271
  default:
263
272
  throw new Error('Unknown query field type');
264
273
  }
265
-
274
+ resolver.addToSlot(
275
+ 'postAuth',
276
+ MappingTemplate.s3MappingTemplateFromString(
277
+ generateAuthExpressionForSandboxMode(context),
278
+ `${query.typeName}.${query.fieldName}.{slotName}.{slotIndex}.req.vtl`,
279
+ ),
280
+ );
266
281
  resolver.mapToStack(stack);
267
282
  context.resolvers.addResolver(query.typeName, query.fieldName, resolver);
268
283
  }
@@ -281,11 +296,49 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
281
296
  resolver = this.generateUpdateResolver(context, def!, mutation.typeName, mutation.fieldName);
282
297
  break;
283
298
  default:
284
- throw new Error('Unknown query field type');
299
+ throw new Error('Unknown mutation field type');
285
300
  }
301
+ resolver.addToSlot(
302
+ 'postAuth',
303
+ MappingTemplate.s3MappingTemplateFromString(
304
+ generateAuthExpressionForSandboxMode(context),
305
+ `${mutation.typeName}.${mutation.fieldName}.{slotName}.{slotIndex}.req.vtl`,
306
+ ),
307
+ );
286
308
  resolver.mapToStack(stack);
287
309
  context.resolvers.addResolver(mutation.typeName, mutation.fieldName, resolver);
288
310
  }
311
+
312
+ const subscriptionLevel = this.modelDirectiveConfig.get(def.name.value)?.subscriptions?.level;
313
+ // in order to create subscription resolvers the level needs to be on
314
+ if (subscriptionLevel === SubscriptionLevel.on) {
315
+ const subscriptionFields = this.getSubscriptionFieldNames(context, def!);
316
+ for (let subscription of subscriptionFields.values()) {
317
+ let resolver;
318
+ switch (subscription.type) {
319
+ case SubscriptionFieldType.ON_CREATE:
320
+ resolver = this.generateOnCreateResolver(context, def, subscription.typeName, subscription.fieldName);
321
+ break;
322
+ case SubscriptionFieldType.ON_UPDATE:
323
+ resolver = this.generateOnUpdateResolver(context, def, subscription.typeName, subscription.fieldName);
324
+ break;
325
+ case SubscriptionFieldType.ON_DELETE:
326
+ resolver = this.generateOnDeleteResolver(context, def, subscription.typeName, subscription.fieldName);
327
+ break;
328
+ default:
329
+ throw new Error('Unknown subscription field type');
330
+ }
331
+ resolver.addToSlot(
332
+ 'postAuth',
333
+ MappingTemplate.s3MappingTemplateFromString(
334
+ generateAuthExpressionForSandboxMode(context),
335
+ `${subscription.typeName}.${subscription.fieldName}.{slotName}.{slotIndex}.req.vtl`,
336
+ ),
337
+ );
338
+ resolver.mapToStack(stack);
339
+ context.resolvers.addResolver(subscription.typeName, subscription.fieldName, resolver);
340
+ }
341
+ }
289
342
  }
290
343
  };
291
344
 
@@ -304,10 +357,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
304
357
  fieldName,
305
358
  dataSource,
306
359
  MappingTemplate.s3MappingTemplateFromString(generateGetRequestTemplate(), `${typeName}.${fieldName}.req.vtl`),
307
- MappingTemplate.s3MappingTemplateFromString(
308
- generateDefaultResponseMappingTemplate(isSyncEnabled),
309
- `${typeName}.${fieldName}.res.vtl`,
310
- ),
360
+ MappingTemplate.s3MappingTemplateFromString(generateGetResponseTemplate(isSyncEnabled), `${typeName}.${fieldName}.res.vtl`),
311
361
  );
312
362
  }
313
363
  return this.resolverMap[resolverKey];
@@ -356,7 +406,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
356
406
  `${typeName}.${fieldName}.req.vtl`,
357
407
  ),
358
408
  MappingTemplate.s3MappingTemplateFromString(
359
- generateDefaultResponseMappingTemplate(isSyncEnabled),
409
+ generateDefaultResponseMappingTemplate(isSyncEnabled, true),
360
410
  `${typeName}.${fieldName}.res.vtl`,
361
411
  ),
362
412
  );
@@ -388,7 +438,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
388
438
  dataSource,
389
439
  MappingTemplate.s3MappingTemplateFromString(generateDeleteRequestTemplate(isSyncEnabled), `${typeName}.${fieldName}.req.vtl`),
390
440
  MappingTemplate.s3MappingTemplateFromString(
391
- generateDefaultResponseMappingTemplate(isSyncEnabled),
441
+ generateDefaultResponseMappingTemplate(isSyncEnabled, true),
392
442
  `${typeName}.${fieldName}.res.vtl`,
393
443
  ),
394
444
  );
@@ -698,7 +748,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
698
748
  dataSource,
699
749
  MappingTemplate.s3MappingTemplateFromString(generateCreateRequestTemplate(type.name.value), `${typeName}.${fieldName}.req.vtl`),
700
750
  MappingTemplate.s3MappingTemplateFromString(
701
- generateDefaultResponseMappingTemplate(isSyncEnabled),
751
+ generateDefaultResponseMappingTemplate(isSyncEnabled, true),
702
752
  `${typeName}.${fieldName}.res.vtl`,
703
753
  ),
704
754
  );
@@ -1106,7 +1156,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1106
1156
  }
1107
1157
 
1108
1158
  private createIAMRole(context: TransformerContextProvider, def: ObjectTypeDefinitionNode, stack: cdk.Stack, tableName: string) {
1109
- const roleName = context.resourceHelper.generateResourceName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1159
+ const roleName = context.resourceHelper.generateIAMRoleName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value));
1110
1160
  const role = new iam.Role(stack, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), {
1111
1161
  roleName: roleName,
1112
1162
  assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
@@ -1161,7 +1211,6 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1161
1211
  SyncUtils.createSyncLambdaIAMPolicy(stack, syncConfig.LambdaConflictHandler.name, syncConfig.LambdaConflictHandler.region),
1162
1212
  );
1163
1213
  }
1164
-
1165
1214
  return role;
1166
1215
  }
1167
1216
 
@@ -1179,4 +1228,10 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
1179
1228
  ...options,
1180
1229
  };
1181
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
+ };
1182
1237
  }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { ModelTransformer } from './graphql-model-transformer';
1
+ export { ModelTransformer, ModelDirectiveConfiguration, SubscriptionLevel } from './graphql-model-transformer';
2
+ export { OPERATION_KEY } from './definitions';
2
3
  export * from './graphql-types';
3
4
  export * from './resolvers';
@@ -15,7 +15,13 @@ import {
15
15
  ifElse,
16
16
  printBlock,
17
17
  toJson,
18
+ qref,
19
+ str,
20
+ not,
18
21
  } from 'graphql-mapping-template';
22
+ import { OPERATION_KEY } from '../definitions';
23
+
24
+ const API_KEY = 'API Key Authorization';
19
25
 
20
26
  /**
21
27
  * Helper method to generate code that converts DynamoDB condition object to condition
@@ -57,9 +63,11 @@ export const generateConditionSlot = (inputConditionObjectName: string, conditio
57
63
 
58
64
  /**
59
65
  * Generate common response template used by most of the resolvers.
66
+ * Append operation if response is coming from a mutation, this is to protect field resolver for subscriptions
60
67
  */
61
- export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean): string => {
68
+ export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean, mutation = false): string => {
62
69
  const statements: Expression[] = [];
70
+ if (mutation) statements.push(qref(methodCall(ref('ctx.result.put'), str(OPERATION_KEY), str('Mutation'))));
63
71
  if (isSyncEnabled) {
64
72
  statements.push(
65
73
  ifElse(
@@ -74,14 +82,30 @@ export const generateDefaultResponseMappingTemplate = (isSyncEnabled: boolean):
74
82
  );
75
83
  }
76
84
 
77
- return printBlock('Get ResponseTemplate')(compoundExpression(statements));
85
+ return printBlock('ResponseTemplate')(compoundExpression(statements));
78
86
  };
79
87
 
80
88
  /**
81
- * Util function to gernate resolver key used to keep track of all the resolvers in memory
89
+ * Util function to generate resolver key used to keep track of all the resolvers in memory
82
90
  * @param typeName Name of the type
83
91
  * @param fieldName Name of the field
84
92
  */
85
93
  export const generateResolverKey = (typeName: string, fieldName: string): string => {
86
94
  return `${typeName}.${fieldName}`;
87
95
  };
96
+
97
+ /**
98
+ * Util function to generate sandbox mode expression
99
+ * @param ctx context to get sandbox mode
100
+ */
101
+ export const generateAuthExpressionForSandboxMode = (ctx: any): string => {
102
+ const enabled = ctx.resourceHelper.api.sandboxModeEnabled;
103
+ let exp;
104
+
105
+ if (enabled) exp = iff(notEquals(methodCall(ref('util.authType')), str(API_KEY)), methodCall(ref('util.unauthorized')));
106
+ else exp = methodCall(ref('util.unauthorized'));
107
+
108
+ return printBlock(`Sandbox Mode ${enabled ? 'Enabled' : 'Disabled'}`)(
109
+ compoundExpression([iff(not(ref('ctx.stash.get("hasAuth")')), exp), toJson(obj({}))]),
110
+ );
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),
@@ -16,27 +16,86 @@ import {
16
16
  equals,
17
17
  bool,
18
18
  and,
19
+ isNullOrEmpty,
20
+ list,
21
+ forEach,
19
22
  nul,
20
23
  } from 'graphql-mapping-template';
21
24
  import { ResourceConstants } from 'graphql-transformer-common';
25
+ const authFilter = ref('ctx.stash.authFilter');
26
+
22
27
  /**
23
28
  * Generate get query resolver template
24
29
  */
25
30
  export const generateGetRequestTemplate = (): string => {
26
31
  const statements: Expression[] = [
27
- set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('GetItem') })),
32
+ set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('Query') })),
28
33
  ifElse(
29
34
  ref('ctx.stash.metadata.modelObjectKey'),
30
- set(ref('key'), ref('ctx.stash.metadata.modelObjectKey')),
31
- compoundExpression([set(ref('key'), obj({ id: methodCall(ref('util.dynamodb.toDynamoDB'), ref('ctx.args.id')) }))]),
35
+ compoundExpression([
36
+ set(ref('expression'), str('')),
37
+ set(ref('expressionNames'), obj({})),
38
+ set(ref('expressionValues'), obj({})),
39
+ forEach(ref('item'), ref('ctx.stash.metadata.modelObjectKey.entrySet()'), [
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'))),
43
+ ]),
44
+ set(ref('expression'), methodCall(ref('expression.replaceAll'), str('AND $'), str(''))),
45
+ set(
46
+ ref('query'),
47
+ obj({ expression: ref('expression'), expressionNames: ref('expressionNames'), expressionValues: ref('expressionValues') }),
48
+ ),
49
+ ]),
50
+ set(
51
+ ref('query'),
52
+ obj({
53
+ expression: str('id = :id'),
54
+ expressionValues: obj({
55
+ ':id': methodCall(ref('util.parseJson'), methodCall(ref('util.dynamodb.toDynamoDBJson'), ref('ctx.args.id'))),
56
+ }),
57
+ }),
58
+ ),
59
+ ),
60
+ qref(methodCall(ref('GetRequest.put'), str('query'), ref('query'))),
61
+ iff(
62
+ not(isNullOrEmpty(authFilter)),
63
+ qref(
64
+ methodCall(
65
+ ref('GetRequest.put'),
66
+ str('filter'),
67
+ methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), authFilter)),
68
+ ),
69
+ ),
32
70
  ),
33
- qref(methodCall(ref('GetRequest.put'), str('key'), ref('key'))),
34
71
  toJson(ref('GetRequest')),
35
72
  ];
36
73
 
37
74
  return printBlock('Get Request template')(compoundExpression(statements));
38
75
  };
39
76
 
77
+ export const generateGetResponseTemplate = (isSyncEnabled: boolean): string => {
78
+ const statements = new Array<Expression>();
79
+ if (isSyncEnabled) {
80
+ statements.push(
81
+ iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type'), ref('ctx.result'))),
82
+ );
83
+ } else {
84
+ statements.push(iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type'))));
85
+ }
86
+ statements.push(
87
+ ifElse(
88
+ and([not(ref('ctx.result.items.isEmpty()')), equals(ref('ctx.result.scannedCount'), int(1))]),
89
+ toJson(ref('ctx.result.items[0]')),
90
+ compoundExpression([
91
+ iff(and([ref('ctx.result.items.isEmpty()'), equals(ref('ctx.result.scannedCount'), int(1))]), ref('util.unauthorized()')),
92
+ toJson(nul()),
93
+ ]),
94
+ ),
95
+ );
96
+ return printBlock('Get Response template')(compoundExpression(statements));
97
+ };
98
+
40
99
  export const generateListRequestTemplate = (): string => {
41
100
  const requestVariable = 'ListRequest';
42
101
  const modelQueryObj = 'ctx.stash.modelQueryExpression';
@@ -51,12 +110,20 @@ export const generateListRequestTemplate = (): string => {
51
110
  }),
52
111
  ),
53
112
  iff(ref('context.args.nextToken'), set(ref(`${requestVariable}.nextToken`), ref('context.args.nextToken'))),
113
+ ifElse(
114
+ not(isNullOrEmpty(authFilter)),
115
+ compoundExpression([
116
+ set(ref('filter'), authFilter),
117
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))),
118
+ ]),
119
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))),
120
+ ),
54
121
  iff(
55
- ref('context.args.filter'),
122
+ not(isNullOrEmpty(ref('filter'))),
56
123
  compoundExpression([
57
124
  set(
58
125
  ref(`filterExpression`),
59
- methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('ctx.args.filter'))),
126
+ methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))),
60
127
  ),
61
128
  iff(
62
129
  not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))),
@@ -95,10 +162,37 @@ export const generateListRequestTemplate = (): string => {
95
162
  export const generateSyncRequestTemplate = (): string => {
96
163
  return printBlock('Sync Request template')(
97
164
  compoundExpression([
165
+ ifElse(
166
+ not(isNullOrEmpty(authFilter)),
167
+ compoundExpression([
168
+ set(ref('filter'), authFilter),
169
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('ctx.args.filter')]) }))),
170
+ ]),
171
+ iff(not(isNullOrEmpty(ref('ctx.args.filter'))), set(ref('filter'), ref('ctx.args.filter'))),
172
+ ),
173
+ iff(
174
+ not(isNullOrEmpty(ref('filter'))),
175
+ compoundExpression([
176
+ set(
177
+ ref(`filterExpression`),
178
+ methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))),
179
+ ),
180
+ iff(
181
+ not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))),
182
+ compoundExpression([
183
+ iff(
184
+ equals(methodCall(ref('filterEpression.expressionValues.size')), int(0)),
185
+ qref(methodCall(ref('filterEpression.remove'), str('expressionValues'))),
186
+ ),
187
+ set(ref('filter'), ref('filterExpression')),
188
+ ]),
189
+ ),
190
+ ]),
191
+ ),
98
192
  obj({
99
193
  version: str('2018-05-29'),
100
194
  operation: str('Sync'),
101
- filter: ifElse(ref('context.args.filter'), ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'), nul()),
195
+ filter: ifElse(ref('filter'), ref('util.toJson($filter)'), nul()),
102
196
  limit: ref(`util.defaultIfNull($ctx.args.limit, ${ResourceConstants.DEFAULT_SYNC_QUERY_PAGE_LIMIT})`),
103
197
  lastSync: ref('util.toJson($util.defaultIfNull($ctx.args.lastSync, null))'),
104
198
  nextToken: ref('util.toJson($util.defaultIfNull($ctx.args.nextToken, null))'),