From acaff150efa7da285330e718aaf3fc36cae465d8 Mon Sep 17 00:00:00 2001 From: SwaySway <7465495+SwaySway@users.noreply.github.com> Date: Fri, 20 Aug 2021 11:52:28 -0700 Subject: [PATCH] feat: graphql auth v2 add auth on mutation and subscription resolvers --- .../package.json | 20 +- .../src/graphql-auth-transformer.ts | 342 +++--- .../src/resolvers/field.ts | 137 +++ .../src/resolvers/helpers.ts | 142 +++ .../src/resolvers/index.ts | 8 +- .../src/resolvers/mutation.create.ts | 236 ++++ .../src/resolvers/mutation.delete.ts | 178 +++ .../src/resolvers/mutation.update.ts | 296 +++++ .../src/resolvers/query.ts | 313 ++--- .../src/resolvers/subscriptions.ts | 71 ++ .../src/utils/constants.ts | 8 + .../src/utils/definitions.ts | 6 +- .../src/utils/index.ts | 34 +- .../src/utils/schema.ts | 142 ++- ...ify-graphql-index-transformer.test.ts.snap | 360 +++++- ...aphql-primary-key-transformer.test.ts.snap | 314 +++-- .../src/resolvers.ts | 2 +- .../src/definitions.ts | 2 +- .../src/graphql-model-transformer.ts | 35 +- .../src/index.ts | 1 + .../src/resolvers/common.ts | 21 +- ...-graphql-has-many-transformer.test.ts.snap | 1021 ++++++++++++++--- ...aphql-searchable-transformer.tests.ts.snap | 36 +- .../src/transformer-context/datasource.ts | 4 + .../transformer-datasource-provider.ts | 1 + .../graphql-mapping-template/src/print.ts | 2 +- 26 files changed, 3034 insertions(+), 698 deletions(-) create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/field.ts create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts create mode 100644 packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts diff --git a/packages/amplify-graphql-auth-transformer/package.json b/packages/amplify-graphql-auth-transformer/package.json index 7f73e4fab59..5e2ed2a6ed8 100644 --- a/packages/amplify-graphql-auth-transformer/package.json +++ b/packages/amplify-graphql-auth-transformer/package.json @@ -27,17 +27,17 @@ "watch": "tsc -w" }, "dependencies": { - "@aws-amplify/graphql-transformer-core": "0.8.1", - "@aws-amplify/graphql-transformer-interfaces": "1.8.1", - "@aws-amplify/graphql-model-transformer": "0.5.1", - "@aws-cdk/aws-appsync": "~1.72.0", - "@aws-cdk/aws-dynamodb": "~1.72.0", - "@aws-cdk/core": "~1.72.0", - "@aws-cdk/aws-iam": "~1.72.0", - "constructs": "^3.0.12", + "@aws-amplify/graphql-transformer-core": "0.9.0", + "@aws-amplify/graphql-transformer-interfaces": "1.9.0", + "@aws-amplify/graphql-model-transformer": "0.6.1", + "@aws-cdk/aws-appsync": "~1.119.0", + "@aws-cdk/aws-dynamodb": "~1.119.0", + "@aws-cdk/core": "~1.119.0", + "@aws-cdk/aws-iam": "~1.119.0", + "constructs": "^3.3.125", "graphql": "^14.5.8", - "graphql-mapping-template": "4.18.2", - "graphql-transformer-common": "4.19.7", + "graphql-mapping-template": "4.18.3", + "graphql-transformer-common": "4.19.9", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts b/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts index 5cae8495058..94d9c82fec0 100644 --- a/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts +++ b/packages/amplify-graphql-auth-transformer/src/graphql-auth-transformer.ts @@ -6,6 +6,8 @@ import { MappingTemplate, } from '@aws-amplify/graphql-transformer-core'; import { + DataSourceProvider, + MutationFieldType, QueryFieldType, TransformerContextProvider, TransformerResolverProvider, @@ -36,8 +38,12 @@ import { createPolicyDocumentForManagedPolicy, IAM_AUTH_ROLE_PARAMETER, IAM_UNAUTH_ROLE_PARAMETER, + getQueryFieldNames, + getMutationFieldNames, + addSubscriptionArguments, + fieldIsList, + getSubscriptionFieldNames, } from './utils'; -import { generateAuthExpressionForField, generateAuthExpressionForQueries, generateFieldAuthResponse } from './resolvers'; import { DirectiveNode, FieldDefinitionNode, @@ -48,17 +54,18 @@ import { } from 'graphql'; import { SubscriptionLevel, ModelDirectiveConfiguration } from '@aws-amplify/graphql-model-transformer'; import { AccessControlMatrix } from './accesscontrol'; -import { - getBaseType, - makeDirective, - makeField, - makeInputValueDefinition, - makeNamedType, - ResourceConstants, - toCamelCase, -} from 'graphql-transformer-common'; +import { getBaseType, makeDirective, makeField, makeNamedType, ResourceConstants } from 'graphql-transformer-common'; import * as iam from '@aws-cdk/aws-iam'; -import * as cdk from '@aws-cdk/core'; +import { + generateAuthExpressionForCreate, + generateAuthExpressionForUpdate, + generateAuthRequestExpression, + geneateAuthExpressionForDelete, + generateAuthExpressionForField, + generateFieldAuthResponse, + generateAuthExpressionForQueries, + generateAuthExpressionForSubscriptions, +} from './resolvers'; // @ auth // changing the schema @@ -74,11 +81,15 @@ import * as cdk from '@aws-cdk/core'; export class AuthTransformer extends TransformerAuthBase { private config: AuthTransformerConfig; private configuredAuthProviders: ConfiguredAuthProviders; + // access control private roleMap: Map; private authModelConfig: Map; private authNonModelConfig: Map; + // model config private modelDirectiveConfig: Map; + // schema generation private seenNonModelTypes: Map>; + // iam policy generation private generateIAMPolicyforUnauthRole: boolean; private generateIAMPolicyforAuthRole: boolean; private authPolicyResources = new Set(); @@ -87,7 +98,7 @@ export class AuthTransformer extends TransformerAuthBase { constructor(config: AuthTransformerConfig) { super('amplify-auth-transformer', authDirectiveDefinition); this.config = config; - this.configuredAuthProviders = getConfiguredAuthProviders(this.config.authConfig); + this.configuredAuthProviders = getConfiguredAuthProviders(this.config); this.modelDirectiveConfig = new Map(); this.seenNonModelTypes = new Map(); this.authModelConfig = new Map(); @@ -104,9 +115,7 @@ export class AuthTransformer extends TransformerAuthBase { } const typeName = def.name.value; const authDir = new DirectiveWrapper(directive); - const rules: AuthRule[] = this.extendAuthRulesForAdminUI( - authDir.getArguments<{ rules: Array }>({ rules: [] }).rules, - ); + const rules: AuthRule[] = authDir.getArguments<{ rules: Array }>({ rules: [] }).rules; ensureAuthRuleDefaults(rules); // validate rules validateRules(rules, this.configuredAuthProviders); @@ -154,7 +163,7 @@ Static group authorization should perform as expected.`, const typeName = parent.name.value; const fieldName = field.name.value; const authDir = new DirectiveWrapper(directive); - const rules: AuthRule[] = authDir.getArguments([]); + const rules: AuthRule[] = authDir.getArguments<{ rules: Array }>({ rules: [] }).rules; ensureAuthRuleDefaults(rules); validateFieldRules(rules, isParentTypeBuiltinType, modelDirective !== undefined, this.configuredAuthProviders); @@ -167,7 +176,7 @@ Static group authorization should perform as expected.`, // auth on models let acm: AccessControlMatrix; // check if the parent is already in the model config if not add it - if (!(typeName in this.modelDirectiveConfig)) { + if (!this.modelDirectiveConfig.has(typeName)) { this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName)); acm = new AccessControlMatrix({ operations: MODEL_OPERATIONS, @@ -175,6 +184,7 @@ Static group authorization should perform as expected.`, }); } else { acm = this.authModelConfig.get(typeName) as AccessControlMatrix; + acm.resetAccessForResource(fieldName); } this.convertModelRulesToRoles(acm, rules, fieldName); } else { @@ -193,19 +203,24 @@ Static group authorization should perform as expected.`, }; transformSchema = (ctx: TransformerContextProvider): void => { + const getOwnerFields = (acm: AccessControlMatrix) => { + return acm.getRoles().reduce((prev: string[], role: string) => { + if (this.roleMap.get(role)!.strategy === 'owner') prev.push(this.roleMap.get(role)!.entity!); + return prev; + }, []); + }; // generate schema changes for (let [modelName, acm] of this.authModelConfig) { - const modelDirectiveConfig = this.modelDirectiveConfig.get(modelName)!; - // collect ownerFields - // add the owner fields for the model - this.addOwnerFieldsToObject(ctx, modelName, this.getOwnerFields(acm)); + const def = ctx.output.getObject(modelName)!; + // collect ownerFields and them in the model + this.addFieldsToObject(ctx, modelName, getOwnerFields(acm)); // Get the directives we need to add to the GraphQL nodes - let providers = this.getAuthProvidersPerModel(modelName); - let directives = this.getServiceDirectives(providers, providers.length === 0 ? this.shouldAddDefaultServiceDirective() : false); + const providers = this.getAuthProvidersPerModel(modelName); + const directives = this.getServiceDirectives(providers, providers.length === 0 ? this.shouldAddDefaultServiceDirective() : false); if (directives.length > 0) { extendTypeWithDirectives(ctx, modelName, directives); } - this.protectSchemaOperations(ctx, acm, providers, modelDirectiveConfig); + this.protectSchemaOperations(ctx, def, acm, providers); this.propagateAuthDirectivesToNestedTypes(ctx, ctx.output.getObject(modelName)!, providers); } }; @@ -217,8 +232,7 @@ Static group authorization should perform as expected.`, for (let [modelName, acm] of this.authModelConfig) { const def = ctx.output.getObject(modelName)!; // queries - const queryFields = this.getQueryFieldNames(ctx, def!); - const readRoles = acm.getRolesPerOperation('read'); + const queryFields = getQueryFieldNames(this.modelDirectiveConfig.get(modelName)!); for (let query of queryFields.values()) { switch (query.type) { case QueryFieldType.GET: @@ -228,11 +242,12 @@ Static group authorization should perform as expected.`, this.protectListResolver(ctx, def, query.typeName, query.fieldName, acm); break; default: - throw new Error('Unkown query field type'); + throw new TransformerContractError('Unkown query field type'); } } // get fields specified in the schema // if there is a role that does not have read access on the field then we create a field resolver + const readRoles = acm.getRolesPerOperation('read'); const modelFields = def.fields?.filter(f => acm.getResources().includes(f.name.value)) ?? []; for (let field of modelFields) { const allowedRoles = readRoles.filter(r => acm.isAllowed(r, field.name.value, 'read')); @@ -241,18 +256,42 @@ Static group authorization should perform as expected.`, throw new InvalidDirectiveError(`\nPer-field auth on the required field ${field.name.value} is not supported with subscriptions. Either make the field optional, set auth on the object and not the field, or disable subscriptions for the object (setting level to off or public)\n`); } - this.protectFieldResolver(ctx, modelName, field.name.value, allowedRoles); + // TODO: check if a function resolver is created here + this.protectFieldResolver(ctx, def, modelName, field.name.value, allowedRoles); + } + } + const mutationFields = getMutationFieldNames(this.modelDirectiveConfig.get(modelName)!); + for (let mutation of mutationFields.values()) { + switch (mutation.type) { + case MutationFieldType.CREATE: + this.protectCreateResolver(ctx, def, mutation.typeName, mutation.fieldName, acm); + break; + case MutationFieldType.UPDATE: + this.protectUpdateResolver(ctx, def, mutation.typeName, mutation.fieldName, acm); + break; + case MutationFieldType.DELETE: + this.protectDeleteResolver(ctx, def, mutation.typeName, mutation.fieldName, acm); + break; + default: + throw new TransformerContractError('Unkown Mutation field type'); } } + + const subscriptionFieldNames = getSubscriptionFieldNames(this.modelDirectiveConfig.get(modelName)!); + const subscriptionRoles = this.getSubscriptionRoles(def.fields ?? [], acm); + for (let subscription of subscriptionFieldNames) { + this.protectSubscriptionResolver(ctx, subscription.typeName, subscription.fieldName, subscriptionRoles); + } } }; protectSchemaOperations = ( ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, acm: AccessControlMatrix, providers: Array, - modelConfig: ModelDirectiveConfiguration, ): void => { + const modelConfig = this.modelDirectiveConfig.get(def.name.value)!; const addServiceDirective = (operation: ModelOperation, operationName: string | null = null) => { if (operationName) { let includeDefault = this.doesTypeHaveRulesForOperation(acm, operation); @@ -270,26 +309,24 @@ Static group authorization should perform as expected.`, addServiceDirective('delete', modelConfig?.mutations?.delete); // TODO: protect sync queries once supported - // subscriptions const subscriptions = modelConfig?.subscriptions; if (subscriptions && subscriptions.level === SubscriptionLevel.on) { - const ownerFields = this.getOwnerFields(acm) ?? []; - for (let onCreateSub of subscriptions.onCreate ?? []) { + const subscriptionArguments = this.getSubscriptionRoles(def.fields ?? [], acm); + for (let onCreateSub of (subscriptions.onCreate && modelConfig?.mutations?.create) ?? []) { addServiceDirective('read', onCreateSub); - this.addSubscriptionOwnerArguments(ctx, onCreateSub, ownerFields); + addSubscriptionArguments(ctx, onCreateSub, subscriptionArguments); } - for (let onUpdateSub of subscriptions.onUpdate ?? []) { + for (let onUpdateSub of (subscriptions.onUpdate && modelConfig?.mutations?.update) ?? []) { addServiceDirective('read', onUpdateSub); - this.addSubscriptionOwnerArguments(ctx, onUpdateSub, ownerFields); + addSubscriptionArguments(ctx, onUpdateSub, subscriptionArguments); } - for (let onDeleteSub of subscriptions.onDelete ?? []) { + for (let onDeleteSub of (subscriptions.onDelete && modelConfig?.mutations?.delete) ?? []) { addServiceDirective('read', onDeleteSub); - this.addSubscriptionOwnerArguments(ctx, onDeleteSub, ownerFields); + addSubscriptionArguments(ctx, onDeleteSub, subscriptionArguments); } } }; - // Queries protectGetResolver = ( ctx: TransformerContextProvider, def: ObjectTypeDefinitionNode, @@ -299,7 +336,7 @@ Static group authorization should perform as expected.`, ): void => { const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); - const authExpression = generateAuthExpressionForQueries(roleDefinitions, def.fields ?? []); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); resolver.addToSlot( 'auth', MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), @@ -314,57 +351,122 @@ Static group authorization should perform as expected.`, ): void => { const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; const roleDefinitions = acm.getRolesPerOperation('read').map(r => this.roleMap.get(r)!); - const authExpression = generateAuthExpressionForQueries(roleDefinitions, def.fields ?? []); + const authExpression = generateAuthExpressionForQueries(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); resolver.addToSlot( 'auth', MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), ); }; - protectFieldResolver = (ctx: TransformerContextProvider, typeName: string, fieldName: string, roles: Array): void => { + protectFieldResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + roles: Array, + ): void => { const roleDefinitions = roles.map(r => this.roleMap.get(r)!); - const fieldAuthExpression = generateAuthExpressionForField(roleDefinitions); + const fieldAuthExpression = generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, def.fields ?? []); const subsEnabled = this.modelDirectiveConfig.get(typeName)!.subscriptions.level === 'on'; - const fieldResponse = generateFieldAuthResponse(ctx.output.getMutationTypeName()!, fieldName, subsEnabled); - // TODO: check if a function resolver is created here + const fieldResponse = generateFieldAuthResponse('Mutation', fieldName, subsEnabled); + if (!ctx.api.host.hasDataSource('NONE')) { + ctx.api.host.addNoneDataSource('NONE'); + } ctx.api.host.addResolver( typeName, fieldName, - MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.req.vtl`), - MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.req.vtl`, 'resolver'), + MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.res.vtl`, 'resolver'), + 'NONE', + ); + }; + protectCreateResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const fields = acm.getResources(); + const createRoles = acm.getRolesPerOperation('create').map(role => { + const allowedFields = fields.filter(resource => acm.isAllowed(role, resource, 'create')); + const roleDefinition = this.roleMap.get(role)!; + roleDefinition.allowedFields = allowedFields.length === fields.length ? [] : allowedFields; + return roleDefinition; + }); + const authExpression = generateAuthExpressionForCreate(this.configuredAuthProviders, createRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); + }; + protectUpdateResolver = ( + ctx: TransformerContextProvider, + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const fields = acm.getResources(); + const updateDeleteRoles = [...new Set([...acm.getRolesPerOperation('update'), ...acm.getRolesPerOperation('delete')])]; + const totalRoles = updateDeleteRoles.map(role => { + const allowedFields = fields.filter(resource => acm.isAllowed(role, resource, 'update')); + const nullAllowedFileds = fields.filter(resource => acm.isAllowed(role, resource, 'delete')); + const roleDefinition = this.roleMap.get(role)!; + roleDefinition.allowedFields = allowedFields.length === fields.length ? [] : allowedFields; + roleDefinition.nullAllowedFields = nullAllowedFileds.length === fields.length ? [] : nullAllowedFileds; + return roleDefinition; + }); + const datasource = ctx.api.host.getDataSource(`${def!.name.value}DS`) as DataSourceProvider; + const requestExpression = generateAuthRequestExpression(); + const authExpression = generateAuthExpressionForUpdate(this.configuredAuthProviders, totalRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), + datasource, ); }; - getQueryFieldNames = ( + protectDeleteResolver = ( ctx: TransformerContextProvider, - type: ObjectTypeDefinitionNode, - ): Set<{ fieldName: string; typeName: string; type: QueryFieldType }> => { - const typeName = type.name.value; - const fields: Set<{ fieldName: string; typeName: string; type: QueryFieldType }> = new Set(); - const modelDirectiveConfig = this.modelDirectiveConfig.get(type.name.value); - if (modelDirectiveConfig?.queries?.get) { - fields.add({ - typeName: 'Query', - fieldName: modelDirectiveConfig.queries.get || toCamelCase(['get', typeName]), - type: QueryFieldType.GET, - }); - } + def: ObjectTypeDefinitionNode, + typeName: string, + fieldName: string, + acm: AccessControlMatrix, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + // only roles with full delete on every field can delete + const deleteRoles = acm.getRolesPerOperation('delete', true).map(role => this.roleMap.get(role)!); + const datasource = ctx.api.host.getDataSource(`${def!.name.value}DS`) as DataSourceProvider; + const requestExpression = generateAuthRequestExpression(); + const authExpression = geneateAuthExpressionForDelete(this.configuredAuthProviders, deleteRoles, def.fields ?? []); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), + datasource, + ); + }; - if (modelDirectiveConfig?.queries?.list) { - fields.add({ - typeName: 'Query', - fieldName: modelDirectiveConfig.queries.list || toCamelCase(['list', typeName]), - type: QueryFieldType.LIST, - }); - } - // check if this API is sync enabled and then if the model is sync enabled - // fields.add({ - // typeName: 'Query', - // fieldName: camelCase(`sync ${typeName}`), - // type: QueryFieldType.SYNC, - // }); - return fields; + protectSubscriptionResolver = ( + ctx: TransformerContextProvider, + typeName: string, + fieldName: string, + subscriptionRoles: Array, + ): void => { + const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider; + const authExpression = generateAuthExpressionForSubscriptions(this.configuredAuthProviders, subscriptionRoles); + resolver.addToSlot( + 'auth', + MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), + ); }; + /* + Role Helpers + */ private convertNonModelRulesToRoles(acm: AccessControlMatrix, authRules: AuthRule[], field?: string) { for (let rule of authRules) { rule.groups!.forEach(group => { @@ -453,9 +555,7 @@ Static group authorization should perform as expected.`, } } } - /* - Role Helpers - */ + private doesTypeHaveRulesForOperation(acm: AccessControlMatrix, operation: ModelOperation) { const rolesHasDefaultProvider = (roles: Array) => { return roles.some(r => this.roleMap.get(r)!.provider! === this.configuredAuthProviders.default); @@ -470,18 +570,24 @@ Static group authorization should perform as expected.`, for (let role of roles) { providers.add(this.roleMap.get(role)!.provider); } + if (this.configuredAuthProviders.hasAdminUIEnabled) { + providers.add('iam'); + } return Array.from(providers); } - private getOwnerFields(acm: AccessControlMatrix): Array { - return acm.getRoles().reduce((prev: string[], role: string) => { - if (this.roleMap.get(role)!.strategy === 'owner') prev.push(this.roleMap.get(role)!.entity!); - return prev; - }, []); - } + + // Remove all the owner roles where the entity is a list + private getSubscriptionRoles = (fields: ReadonlyArray, acm: AccessControlMatrix) => { + return acm + .getRoles() + .map(role => this.roleMap.get(role)!) + .filter(roleDef => roleDef.strategy === 'owner' && !fieldIsList(fields, roleDef.entity!)); + }; + /* Schema Generation Helpers */ - private addOwnerFieldsToObject(ctx: TransformerContextProvider, modelName: string, ownerFields: Array) { + private addFieldsToObject(ctx: TransformerContextProvider, modelName: string, ownerFields: Array) { const modelObject = ctx.output.getObject(modelName)!; const existingFields = collectFieldNames(modelObject); const ownerFieldsToAdd = ownerFields.filter(field => !existingFields.includes(field)); @@ -490,34 +596,22 @@ Static group authorization should perform as expected.`, } ctx.output.putType(modelObject); } - private addSubscriptionOwnerArguments(ctx: TransformerContextProvider, operationName: string, ownerFields: Array) { - let subscription = ctx.output.getSubscription()!; - let createField: FieldDefinitionNode = subscription!.fields!.find(field => field.name.value === operationName) as FieldDefinitionNode; - const ownerArgumentList = ownerFields.map(role => { - return makeInputValueDefinition(role, makeNamedType('String')); - }); - createField = { - ...createField, - arguments: ownerArgumentList, - }; - subscription = { - ...subscription, - fields: subscription!.fields!.map(field => (field.name.value === operationName ? createField : field)), - }; - ctx.output.putType(subscription); - } private propagateAuthDirectivesToNestedTypes( ctx: TransformerContextProvider, - type: ObjectTypeDefinitionNode, + def: ObjectTypeDefinitionNode, providers: Array, ) { - const getDirectivesToAdd = (nonModelName: string): { current: DirectiveNode[]; old?: Set } => { + const getDirectivesToAdd = (nonModelName: string): Array => { const directives = this.getServiceDirectives(providers, true); if (this.seenNonModelTypes.has(nonModelName)) { - const nonModelDirectives: Set = this.seenNonModelTypes.get(nonModelName)!; - return { current: directives.filter(directive => !nonModelDirectives.has(directive.name.value)), old: nonModelDirectives }; + const currentDirectives: Set = this.seenNonModelTypes.get(nonModelName)!; + const newDirectives = directives.filter(dir => !currentDirectives.has(dir.name.value)); + // merge back the newly added auth directives with what already exists in the set + this.seenNonModelTypes.set(nonModelName, new Set([...newDirectives.map(dir => dir.name.value), ...currentDirectives])); + return newDirectives; } - return { current: directives }; + this.seenNonModelTypes.set(nonModelName, new Set([...directives.map(dir => dir.name.value)])); + return directives; }; const nonModelTypePredicate = (fieldType: TypeDefinitionNode): TypeDefinitionNode | undefined => { @@ -530,21 +624,14 @@ Static group authorization should perform as expected.`, } return fieldType; }; - const nonModelFieldTypes = type + const nonModelFieldTypes = def .fields!.map(f => ctx.output.getType(getBaseType(f.type)) as TypeDefinitionNode) .filter(nonModelTypePredicate); for (const nonModelFieldType of nonModelFieldTypes) { const directives = getDirectivesToAdd(nonModelFieldType.name.value); - if (directives.current.length > 0) { - // merge back the newly added auth directives with what already exists in the set - const totalDirectives = new Set([ - ...directives.current.map(dir => dir.name.value), - ...(directives.old ? directives.old : []), - ]); - this.seenNonModelTypes.set(nonModelFieldType.name.value, totalDirectives); - extendTypeWithDirectives(ctx, nonModelFieldType.name.value, directives.current); - const hasIAM = - directives.current.filter(directive => directive.name.value === 'aws_iam') || this.configuredAuthProviders.default === 'iam'; + if (directives.length > 0) { + extendTypeWithDirectives(ctx, nonModelFieldType.name.value, directives); + const hasIAM = directives.some(dir => dir.name.value === 'aws_iam') || this.configuredAuthProviders.default === 'iam'; if (hasIAM) { this.unauthPolicyResources.add(`${nonModelFieldType.name.value}/null`); this.authPolicyResources.add(`${nonModelFieldType.name.value}/null`); @@ -587,29 +674,14 @@ Static group authorization should perform as expected.`, cannot add @aws_api_key to other operations since their is no rule granted access to it */ if ( - Boolean(providers.find(p => p === this.configuredAuthProviders.default)) && - Boolean( - providers.find(p => p !== this.configuredAuthProviders.default) && - !Boolean(directives.find(d => d.name.value === AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default))), - ) + providers.some(p => p === this.configuredAuthProviders.default) && + providers.some(p => p !== this.configuredAuthProviders.default) && + !directives.some(d => d.name.value === AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default)) ) { directives.push(makeDirective(AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default) as string, [])); } return directives; } - /* - Admin UI Helpers - */ - private isAdminUIEnabled(): boolean { - return this.configuredAuthProviders.hasIAM && this.config.addAwsIamAuthInOutputSchema; - } - private extendAuthRulesForAdminUI(rules: AuthRule[]): AuthRule[] { - // Check for Amplify Admin - if (this.isAdminUIEnabled()) { - return [...rules, { allow: 'private', provider: 'iam', generateIAMPolicy: false }]; - } - return rules; - } /** * When AdminUI is enabled, all the types and operations get IAM auth. If the default auth mode is * not IAM all the fields will need to have the default auth mode directive to ensure both IAM and deault @@ -618,7 +690,7 @@ Static group authorization should perform as expected.`, * @returns boolean */ private shouldAddDefaultServiceDirective(): boolean { - return this.isAdminUIEnabled() && this.config.authConfig.defaultAuthentication.authenticationType !== 'AWS_IAM'; + return this.configuredAuthProviders.hasAdminUIEnabled && this.config.authConfig.defaultAuthentication.authenticationType !== 'AWS_IAM'; } /* IAM Helpers @@ -629,7 +701,7 @@ Static group authorization should perform as expected.`, // Sanity check to make sure we're not generating invalid policies, where no resources are defined. if (this.authPolicyResources.size === 0) { // When AdminUI is enabled, IAM auth is added but it does not need any policies to be generated - if (!this.isAdminUIEnabled()) { + if (!this.configuredAuthProviders.hasAdminUIEnabled) { throw new TransformerContractError('AuthRole policies should be generated, but no resources were added.'); } } else { diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts new file mode 100644 index 00000000000..9e98a0fd43d --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/field.ts @@ -0,0 +1,137 @@ +import { OPERATION_KEY } from '@aws-amplify/graphql-model-transformer'; +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + iff, + not, + ref, + equals, + str, + compoundExpression, + printBlock, + toJson, + obj, + set, + methodCall, + nul, + ifElse, + bool, + raw, + forEach, +} from 'graphql-mapping-template'; +import { + RoleDefinition, + splitRoles, + COGNITO_AUTH_TYPE, + OIDC_AUTH_TYPE, + ConfiguredAuthProviders, + fieldIsList, + IS_AUTHORIZED_FLAG, +} from '../utils'; +import { getOwnerClaim, staticGroupRoleExpression, apiKeyExpression, iamExpression } from './helpers'; + +// Field Read VTL Functions +const generateDynamicAuthReadExpression = (roles: Array, fields: ReadonlyArray) => { + const ownerExpressions = new Array(); + const dynamicGroupExpressions = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpressions.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff( + equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ] + : [iff(equals(ref('ownerEntity'), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true)))]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpressions.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`groupEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity!}`), nul())), + set(ref(`groupClaim${idx}`), getOwnerClaim(role.claim!)), + forEach(ref('userGroup'), ref('dynamicGroupClaim'), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpressions.length > 0 ? ownerExpressions : []), ...(dynamicGroupExpressions.length > 0 ? dynamicGroupExpressions : [])]; +}; + +export const generateAuthExpressionForField = ( + provider: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +): string => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, iamRoles, apiKeyRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (provider.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (provider.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, provider.hasAdminUIEnabled)); + } + if (provider.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([ + ...staticGroupRoleExpression(cognitoStaticGroupRoles), + ...generateDynamicAuthReadExpression(cognitoDynamicRoles, fields), + ]), + ), + ); + } + if (provider.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([ + ...staticGroupRoleExpression(oidcStaticGroupRoles), + ...generateDynamicAuthReadExpression(oidcDynamicRoles, fields), + ]), + ), + ); + } + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Field Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; + +/** + * This is the response resolver for fields to protect subscriptions + * @param subscriptionsEnabled + * @returns + */ +export const generateFieldAuthResponse = (operation: string, fieldName: string, subscriptionsEnabled: boolean): string => { + if (subscriptionsEnabled) { + return printBlock('Checking for allowed operations which can return this field')( + compoundExpression([ + set(ref('operation'), methodCall(ref('util.defaultIfNull'), methodCall(ref('ctx.source.get'), str(OPERATION_KEY)), nul())), + ifElse(equals(ref('operation'), str(operation)), toJson(nul()), toJson(ref(`context.source.${fieldName}`))), + ]), + ); + } + return printBlock('Return Source Field')(toJson(ref(`context.source.${fieldName}`))); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts new file mode 100644 index 00000000000..d3690d19540 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/helpers.ts @@ -0,0 +1,142 @@ +import { + qref, + Expression, + ifElse, + iff, + methodCall, + not, + ref, + set, + str, + raw, + obj, + bool, + compoundExpression, + printBlock, + toJson, + forEach, + list, + equals, + or, +} from 'graphql-mapping-template'; +import { NONE_VALUE } from 'graphql-transformer-common'; +import { + DEFAULT_COGNITO_IDENTITY_CLAIM, + RoleDefinition, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + API_KEY_AUTH_TYPE, + ADMIN_ROLE, + IAM_AUTH_TYPE, + MANAGE_ROLE, +} from '../utils'; + +// since the keySet returns a set we can convert it to a list by converting to json and parsing back as a list +export const getInputFields = () => + compoundExpression([ + set(ref('inputFields'), methodCall(ref('util.parseJson'), methodCall(ref('util.toJson'), ref('ctx.args.input.keySet()')))), + iff( + ref('ctx.stash.metadata.modelObjectKey'), + forEach(ref('entry'), ref('ctx.stash.metadata.modelObjectKey.keySet()'), [qref(methodCall(ref('inputFields.remove'), ref('entry')))]), + ), + ]); + +export const getIdentityClaimExp = (value: Expression, defaultValueExp: Expression) => { + return methodCall(ref('util.defaultIfNull'), methodCall(ref('ctx.identity.claims.get'), value), defaultValueExp); +}; + +// for create mutations and subscriptions +export const addAllowedFieldsIfElse = (fieldKey: string, breakLoop: boolean = false) => + ifElse( + not(ref(`${fieldKey}.isEmpty()`)), + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(fieldKey))), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), ...(breakLoop ? [raw('#break')] : [])]), + ); + +/** + * Behavior of auth v1 + * Order of how the owner value is retrieved from the jwt + * if claim is username + * 1. username + * 2. cognito:username + * 3. none value + * + * if claim is custom + * 1. custom + * 2. none value + */ +export const getOwnerClaim = (ownerClaim: string): Expression => { + if (ownerClaim === 'username') { + return getIdentityClaimExp(str(ownerClaim), getIdentityClaimExp(str(DEFAULT_COGNITO_IDENTITY_CLAIM), str(NONE_VALUE))); + } + return getIdentityClaimExp(str(ownerClaim), str(NONE_VALUE)); +}; + +export const responseCheckForErrors = () => + iff(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type'))); + +// Common Expressions + +export const staticGroupRoleExpression = (roles: Array): Array => { + return roles.length > 0 + ? [ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(ref('groupRole'), ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw(`#break`)]), + ), + ]), + ] + : []; +}; + +export const apiKeyExpression = (roles: Array) => + iff( + equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), + compoundExpression([ + ...(roles.length > 0 ? [set(ref(IS_AUTHORIZED_FLAG), bool(true))] : []), + iff(not(ref(IS_AUTHORIZED_FLAG)), methodCall(ref('util.unauthorized'))), + ]), + ); + +export const iamExpression = (roles: Array, adminuiEnabled: boolean = false) => { + const iamCheck = (claim: string, exp: Expression) => + iff(equals(methodCall(ref('ctx.identity.get'), str('cognitoIdentityAuthType')), str(claim)), exp); + const expression = new Array(); + // allow if using admin ui + if (adminuiEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(ADMIN_ROLE)), + methodCall(ref('ctx.identity.userArn.contains'), str(MANAGE_ROLE)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + expression.push(iff(not(ref(IS_AUTHORIZED_FLAG)), iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))))); + } + } + expression.push(iff(not(ref(IS_AUTHORIZED_FLAG)), methodCall(ref('util.unauthorized')))); + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +// Get Request for Update and Delete +export const generateAuthRequestExpression = () => { + const statements = [ + set(ref('GetRequest'), obj({ version: str('2018-05-29'), operation: str('GetItem') })), + ifElse( + ref('ctx.stash.metadata.modelObjectKey'), + set(ref('key'), ref('ctx.stash.metadata.modelObjectKey')), + compoundExpression([set(ref('key'), obj({ id: methodCall(ref('util.dynamodb.toDynamoDB'), ref('ctx.args.input.id')) }))]), + ), + qref(methodCall(ref('GetRequest.put'), str('key'), ref('key'))), + toJson(ref('GetRequest')), + ]; + return printBlock('Get Request template')(compoundExpression(statements)); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts index a38910e42af..5f9e8476d39 100644 --- a/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/index.ts @@ -1 +1,7 @@ -export * from './query'; +export { generateAuthExpressionForQueries } from './query'; +export { generateAuthExpressionForCreate } from './mutation.create'; +export { generateAuthExpressionForUpdate } from './mutation.update'; +export { geneateAuthExpressionForDelete } from './mutation.delete'; +export { generateAuthExpressionForField, generateFieldAuthResponse } from './field'; +export { generateAuthExpressionForSubscriptions } from './subscriptions'; +export { generateAuthRequestExpression } from './helpers'; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts new file mode 100644 index 00000000000..49301f68a30 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.create.ts @@ -0,0 +1,236 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + compoundExpression, + set, + ref, + bool, + raw, + iff, + and, + isNullOrEmpty, + not, + methodCall, + qref, + list, + nul, + forEach, + equals, + str, + obj, + or, + printBlock, + toJson, +} from 'graphql-mapping-template'; +import { getOwnerClaim, getIdentityClaimExp, getInputFields, addAllowedFieldsIfElse } from './helpers'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + COGNITO_AUTH_TYPE, + ConfiguredAuthProviders, + IAM_AUTH_TYPE, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, + fieldIsList, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + DENIED_FIELDS, +} from '../utils'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0) { + expression.push(set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields)))); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; + +/** + * No need to combine allowed fields as the request can only be signed by one iam role + * @param roles + * @returns + */ +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false) => { + const iamCheck = (claim: string, exp: Expression) => + iff(equals(methodCall(ref('ctx.identity.get'), str('cognitoIdentityAuthType')), str(claim)), exp); + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(ADMIN_ROLE)), + methodCall(ref('ctx.identity.userArn.contains'), str(MANAGE_ROLE)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + if (role.allowedFields!.length > 0 || role.nullAllowedFields!.length > 0) { + expression.push( + iamCheck(role.claim!, compoundExpression([set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(role.allowedFields)))])), + ); + } else { + iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + } + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(false))); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +const staticRoleExpression = (roles: Array): Array => { + return roles.length > 0 + ? [ + set( + ref('staticGroupRoles'), + raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity, allowedFields: r.allowedFields ?? [] })))), + ), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff(methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), addAllowedFieldsIfElse('groupRole.allowedFields', true)), + ]), + ] + : []; +}; + +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray): Array => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`ownerEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.input.args.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + set(ref(`ownerAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff(equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), addAllowedFieldsIfElse(`ownerAllowedFields${idx}`, true)), + ]), + ] + : [iff(equals(ref(`ownerClaim${idx}`), ref(`ownerClaim${idx}`)), addAllowedFieldsIfElse(`ownerAllowedFields${idx}`))]), + iff( + and([isNullOrEmpty(ref(`ownerEntity${idx}`)), not(methodCall(ref('ctx.args.input.containsKey'), ref(`${role.entity!}`)))]), + compoundExpression([ + qref( + methodCall( + ref('ctx.args.input.put'), + str(role.entity!), + entityIsList ? list([ref(`ownerClaim${idx}`)]) : ref(`ownerClaim${idx}`), + ), + ), + addAllowedFieldsIfElse(`ownerAllowedFields${idx}`), + ]), + ), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.input.args.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getOwnerClaim(role.claim!)), + set(ref(`groupAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + forEach(ref('userGroup'), ref('dynamicGroupClaim'), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + addAllowedFieldsIfElse(`groupAllowedFields${idx}`, true), + ), + ]), + ]), + ), + ); + } + }); + + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +/** + * Unauthorized if + * - auth conditions could not be met + * - there are fields conditions that could not be met + * @param providers + * @param roles + * @param fields + * @returns + */ +export const generateAuthExpressionForCreate = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +): string => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, apiKeyRoles, iamRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [ + getInputFields(), + set(ref(IS_AUTHORIZED_FLAG), bool(false)), + set(ref(ALLOWED_FIELDS), list([])), + ]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(cognitoStaticGroupRoles), ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields)]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(oidcStaticGroupRoles), ...dynamicGroupRoleExpression(oidcDynamicRoles, fields)]), + ), + ); + } + totalAuthExpressions.push( + iff(and([not(ref(IS_AUTHORIZED_FLAG)), ref(`${ALLOWED_FIELDS}.isEmpty()`)]), ref('util.unauthorized()')), + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(DENIED_FIELDS), methodCall(ref('util.list.copyAndRemoveAll'), ref('inputFields'), ref(ALLOWED_FIELDS))), + iff( + ref(`${DENIED_FIELDS}.size() > 0`), + methodCall(ref('util.error'), str(`Unauthorized on \${${DENIED_FIELDS}}`), str('Unauthorized')), + ), + ]), + ), + ); + return printBlock('Create Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts new file mode 100644 index 00000000000..4514a89db55 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.delete.ts @@ -0,0 +1,178 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + Expression, + printBlock, + compoundExpression, + toJson, + obj, + bool, + equals, + iff, + raw, + ref, + set, + str, + methodCall, + or, + forEach, + list, + not, + nul, +} from 'graphql-mapping-template'; +import { getIdentityClaimExp, getOwnerClaim } from './helpers'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + COGNITO_AUTH_TYPE, + ConfiguredAuthProviders, + fieldIsList, + IAM_AUTH_TYPE, + IS_AUTHORIZED_FLAG, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, +} from '../utils'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles.length > 0) { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; +/** + * No need to combine allowed fields as the request can only be signed by one iam role + * @param roles + * @returns + */ +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false) => { + const iamCheck = (claim: string, exp: Expression) => + iff(equals(methodCall(ref('ctx.identity.get'), str('cognitoIdentityAuthType')), str(claim)), exp); + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(ADMIN_ROLE)), + methodCall(ref('ctx.identity.userArn.contains'), str(MANAGE_ROLE)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(false))); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +const staticRoleExpression = (roles: Array): Array => { + return roles.length > 0 + ? [ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ] + : []; +}; + +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray) => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff(equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true))), + ]), + ] + : [iff(equals(ref('ownerEntity'), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true)))]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getIdentityClaimExp(str(role.claim!), list([]))), + forEach(ref('userGroup'), ref(`groupClaim${idx}`), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + set(ref(IS_AUTHORIZED_FLAG), bool(true)), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +export const geneateAuthExpressionForDelete = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +) => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, apiKeyRoles, iamRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(cognitoStaticGroupRoles), ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields)]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(oidcStaticGroupRoles), ...dynamicGroupRoleExpression(oidcDynamicRoles, fields)]), + ), + ); + } + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts new file mode 100644 index 00000000000..293ee5df383 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/mutation.update.ts @@ -0,0 +1,296 @@ +import { FieldDefinitionNode } from 'graphql'; +import { + compoundExpression, + iff, + raw, + set, + ref, + forEach, + bool, + Expression, + not, + obj, + list, + qref, + equals, + str, + and, + methodCall, + toJson, + printBlock, + ifElse, + nul, + or, +} from 'graphql-mapping-template'; +import { + ADMIN_ROLE, + API_KEY_AUTH_TYPE, + COGNITO_AUTH_TYPE, + ConfiguredAuthProviders, + IAM_AUTH_TYPE, + MANAGE_ROLE, + OIDC_AUTH_TYPE, + RoleDefinition, + splitRoles, + fieldIsList, + IS_AUTHORIZED_FLAG, + ALLOWED_FIELDS, + NULL_ALLOWED_FIELDS, + DENIED_FIELDS, +} from '../utils'; +import { getIdentityClaimExp, responseCheckForErrors, getOwnerClaim, getInputFields } from './helpers'; + +/** + * There is only one role for ApiKey we can use the first index + * @param roles + * @returns Expression | null + */ +const apiKeyExpression = (roles: Array) => { + const expression = new Array(); + if (roles.length === 0) { + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), ref('util.unauthorized()')); + } + if (roles[0].allowedFields!.length > 0 || roles[0].nullAllowedFields!.length > 0) { + expression.push( + set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].allowedFields))), + set(ref(`${NULL_ALLOWED_FIELDS}`), raw(JSON.stringify(roles[0].nullAllowedFields))), + ); + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), compoundExpression(expression)); +}; + +const iamExpression = (roles: Array, hasAdminUIEnabled: boolean = false) => { + const iamCheck = (claim: string, exp: Expression) => + iff(equals(methodCall(ref('ctx.identity.get'), str('cognitoIdentityAuthType')), str(claim)), exp); + const expression = new Array(); + // allow if using admin ui + if (hasAdminUIEnabled) { + expression.push( + iff( + or([ + methodCall(ref('ctx.identity.userArn.contains'), str(ADMIN_ROLE)), + methodCall(ref('ctx.identity.userArn.contains'), str(MANAGE_ROLE)), + ]), + raw('#return($util.toJson({})'), + ), + ); + } + if (roles.length > 0) { + for (let role of roles) { + if (role.allowedFields!.length > 0 || role.nullAllowedFields!.length > 0) { + expression.push( + iamCheck( + role.claim!, + compoundExpression([ + set(ref(`${ALLOWED_FIELDS}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`${NULL_ALLOWED_FIELDS}`), raw(JSON.stringify(role.nullAllowedFields))), + ]), + ), + ); + } else { + iamCheck(role.claim!, set(ref(IS_AUTHORIZED_FLAG), bool(true))); + } + } + } else { + expression.push(set(ref(IS_AUTHORIZED_FLAG), bool(false))); + } + return iff(equals(ref('util.authType()'), str(IAM_AUTH_TYPE)), compoundExpression(expression)); +}; + +const staticRoleExpression = (roles: Array) => { + return roles.length > 0 + ? [ + set( + ref('staticGroupRoles'), + raw( + JSON.stringify( + roles.map(r => ({ + claim: r.claim, + entity: r.entity, + allowedFields: r.allowedFields, + nullAllowedFields: r.nullAllowedFields, + })), + ), + ), + ), + forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([ + // if we find that it's not fully allowed on update (update/delete) we add the field conditions + // otherwise we set to true and break + ifElse( + or([not(ref(`groupRole.allowedFields.isEmpty()`)), not(ref('groupRole.nullAllowedFields.isEmpty()'))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref('groupRole.allowedFields'))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref('groupRole.nullAllowedFields'))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ]), + ), + ]), + ] + : []; +}; +const dynamicGroupRoleExpression = (roles: Array, fields: ReadonlyArray): Array => { + const ownerExpression = new Array(); + const dynamicGroupExpression = new Array(); + roles.forEach((role, idx) => { + const entityIsList = fieldIsList(fields, role.entity!); + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`ownerEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity!}`), entityIsList ? list([]) : nul()), + ), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + set(ref(`ownerAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`ownerNullAllowedFields${idx}`), raw(JSON.stringify(role.nullAllowedFields))), + ...(entityIsList + ? [ + forEach(ref('allowedOwner'), ref(`ownerEntity${idx}`), [ + iff( + equals(ref('allowedOwner'), ref(`ownerClaim${idx}`)), + ifElse( + or([not(ref(`ownerAllowedFields${idx}.isEmpty()`)), not(ref(`ownerNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(`ownerAllowedFields${idx}`))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref(`ownerNullAllowedFields${idx}`))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ), + ]), + ] + : [ + iff( + equals(ref('ownerEntity'), ref(`ownerClaim${idx}`)), + ifElse( + or([not(ref(`ownerAllowedFields${idx}.isEmpty()`)), not(ref(`ownerNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref(`ownerAllowedFields${idx}`))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref(`ownerNullAllowedFields${idx}`))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true))]), + ), + ), + ]), + ]), + ), + ); + } + if (role.strategy === 'groups') { + dynamicGroupExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set( + ref(`groupEntity${idx}`), + methodCall(ref('util.defaultIfNull'), ref(`ctx.result.${role.entity}`), entityIsList ? list([]) : nul()), + ), + set(ref(`groupClaim${idx}`), getIdentityClaimExp(str(role.claim!), list([]))), + set(ref(`groupAllowedFields${idx}`), raw(JSON.stringify(role.allowedFields))), + set(ref(`groupNullAllowedFields${idx}`), raw(JSON.stringify(role.nullAllowedFields))), + forEach(ref('userGroup'), ref(`groupClaim${idx}`), [ + iff( + entityIsList + ? methodCall(ref(`groupEntity${idx}.contains`), ref('userGroup')) + : equals(ref(`groupEntity${idx}`), ref('userGroup')), + ifElse( + or([not(ref(`groupAllowedFields${idx}.isEmpty()`)), not(ref(`groupNullAllowedFields${idx}.isEmpty()`))]), + compoundExpression([ + qref(methodCall(ref(`${ALLOWED_FIELDS}.addAll`), ref('groupRole.allowedFields'))), + qref(methodCall(ref(`${NULL_ALLOWED_FIELDS}.addAll`), ref('groupRole.nullAllowedFields'))), + ]), + compoundExpression([set(ref(IS_AUTHORIZED_FLAG), bool(true)), raw('#break')]), + ), + ), + ]), + ]), + ), + ); + } + }); + return [...(ownerExpression.length > 0 ? ownerExpression : []), ...(dynamicGroupExpression.length > 0 ? dynamicGroupExpression : [])]; +}; + +/** + * For update we need to check for allowed fields and null allowed fields + * unauthorized if + * - none of the roles have been met and there are no field conditions + * - role is partially allowed but the field conditions have not been met + * @param providers + * @param roles + * @param fields + * @returns + */ +export const generateAuthExpressionForUpdate = ( + providers: ConfiguredAuthProviders, + roles: Array, + fields: ReadonlyArray, +) => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, apiKeyRoles, iamRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [ + responseCheckForErrors(), + getInputFields(), + set(ref(IS_AUTHORIZED_FLAG), bool(false)), + set(ref(`${ALLOWED_FIELDS}`), list([])), + set(ref(`${NULL_ALLOWED_FIELDS}`), list([])), + set(ref(`${DENIED_FIELDS}`), obj({})), + ]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled)); + } + if (providers.hasUserPools) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(cognitoStaticGroupRoles), ...dynamicGroupRoleExpression(cognitoDynamicRoles, fields)]), + ), + ); + } + if (providers.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...staticRoleExpression(oidcStaticGroupRoles), ...dynamicGroupRoleExpression(oidcDynamicRoles, fields)]), + ), + ); + } + totalAuthExpressions.push( + iff( + and([not(ref(IS_AUTHORIZED_FLAG)), ref(`${ALLOWED_FIELDS}.isEmpty()`), ref(`${NULL_ALLOWED_FIELDS}.isEmpty()`)]), + ref('util.unauthorized()'), + ), + iff( + not(ref(IS_AUTHORIZED_FLAG)), + forEach(ref('entry'), ref('inputFields'), [ + iff( + and([methodCall(ref('util.isNull'), ref('entry.value')), not(ref(`${NULL_ALLOWED_FIELDS}.contains($entry.value)`))]), + qref(methodCall(ref(`${DENIED_FIELDS}.put`), ref('entry.key'), str(''))), + ), + ]), + ), + iff( + not(ref(IS_AUTHORIZED_FLAG)), + forEach(ref('deniedField'), ref(`$util.list.copyAndRemoveAll($inputFields, \$${ALLOWED_FIELDS})`), [ + qref(methodCall(ref(`${DENIED_FIELDS}.add`), ref('deniedField'), str(''))), + ]), + ), + iff( + ref(`${DENIED_FIELDS}.keySet().size() > 0`), + methodCall(ref('util.error'), str(`Unauthorized on \${${DENIED_FIELDS}.keySet()}`), str('Unauthorized')), + ), + ); + return printBlock('Update Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts index 2e2138631be..2fbd5ebb5d4 100644 --- a/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/query.ts @@ -1,101 +1,60 @@ import { FieldDefinitionNode } from 'graphql'; import { compoundExpression, + Expression, + obj, + toJson, + printBlock, + and, + equals, iff, - raw, - set, + methodCall, + not, ref, - forEach, + str, bool, - Expression, - not, - ObjectNode, - obj, + forEach, list, + ObjectNode, qref, - equals, - str, - and, - methodCall, - toJson, - printBlock, - print, - block, - ifElse, - nul, + raw, + set, } from 'graphql-mapping-template'; -import { isListType, NONE_VALUE } from 'graphql-transformer-common'; +import { getIdentityClaimExp, getOwnerClaim, apiKeyExpression, iamExpression } from './helpers'; import { COGNITO_AUTH_TYPE, OIDC_AUTH_TYPE, - DEFAULT_COGNITO_IDENTITY_CLAIM, - API_KEY_AUTH_TYPE, RoleDefinition, - RolesByProvider, + splitRoles, + ConfiguredAuthProviders, + IS_AUTHORIZED_FLAG, + fieldIsList, + NONE_VALUE, + API_KEY_AUTH_TYPE, } from '../utils'; -// Generic Auth VTL Functions -const getIdentityClaimExp = (value: Expression, defaultValueExp: Expression) => { - return methodCall(ref('util.defaultIfNull'), methodCall(ref('ctx.identity.claims.get'), value), defaultValueExp); -}; - -export const splitRoles = (roles: Array): RolesByProvider => { - return { - cognitoStaticGroupRoles: roles.filter(r => r.static && r.provider === 'userPools'), - cognitoDynamicRoles: roles.filter(r => !r.static && r.provider === 'userPools'), - oidcStaticGroupRoles: roles.filter(r => r.static && r.provider === 'oidc'), - oidcDynamicRoles: roles.filter(r => !r.static && r.provider === 'oidc'), - apiKeyRoles: roles.filter(r => r.provider === 'apiKey'), - }; -}; - -export const staticRuleExpression = (roles: Array): Array => { - return [ - set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), - forEach(/** for */ ref('groupRole'), /** in */ ref('staticGroupRoles'), [ - set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), - iff( - methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), - compoundExpression([ - set(ref('isStaticAuthorized'), bool(true)), - qref(methodCall(ref('ctx.stash.remove'), str('authFilter'))), - raw(`#break`), +const staticRuleExpression = (roles: Array): Array => { + return roles.length > 0 + ? [ + set(ref('staticGroupRoles'), raw(JSON.stringify(roles.map(r => ({ claim: r.claim, entity: r.entity }))))), + forEach(ref('groupRole'), ref('staticGroupRoles'), [ + set(ref('groupsInToken'), getIdentityClaimExp(ref('groupRole.claim'), list([]))), + iff( + methodCall(ref('groupsInToken.contains'), ref('groupRole.entity')), + compoundExpression([ + set(ref(IS_AUTHORIZED_FLAG), bool(true)), + qref(methodCall(ref('ctx.stash.remove'), str('authFilter'))), + raw(`#break`), + ]), + ), ]), - ), - ]), - ]; + ] + : []; }; -/** - * Behavior of auth v1 - * Order of how the owner value is retrieved from the jwt - * if claim is username - * 1. username - * 2. cognito:username - * 3. none value - * - * if claim is custom - * 1. custom - * 2. none value - */ -export const getOwnerExpression = (ownerClaim: string): Expression => { - if (ownerClaim === 'username') { - return getIdentityClaimExp(str(ownerClaim), getIdentityClaimExp(str(DEFAULT_COGNITO_IDENTITY_CLAIM), str(NONE_VALUE))); - } - return getIdentityClaimExp(str(ownerClaim), str(NONE_VALUE)); -}; - -export const fieldIsList = (fields: ReadonlyArray, fieldName: string) => { - const field = fields.find(field => field.name.value === fieldName); - if (field) { - return isListType(field.type); - } - return false; -}; - -// Query VTL Functions -export const generateAuthFilter = (roles: Array, fields: ReadonlyArray): Expression => { +const generateAuthFilter = (roles: Array, fields: ReadonlyArray): Array => { const authFilter = new Array(); + if (!(roles.length > 0)) return []; /** * if ownerField is string * ownerField: { eq: "cognito:owner" } @@ -111,188 +70,50 @@ export const generateAuthFilter = (roles: Array, fields: Readonl const entityIsList = fieldIsList(fields, role.entity!); if (role.strategy === 'owner') { const ownerCondition = entityIsList ? 'contains' : 'eq'; - authFilter.push(obj({ [role.entity!]: obj({ [ownerCondition]: getOwnerExpression(role.claim!) }) })); + authFilter.push(obj({ [role.entity!]: obj({ [ownerCondition]: getOwnerClaim(role.claim!) }) })); } if (role.strategy === 'groups') { const groupsCondition = entityIsList ? 'contains' : 'in'; authFilter.push(obj({ [role.entity!]: obj({ [groupsCondition]: getIdentityClaimExp(str(role.claim!), list([str(NONE_VALUE)])) }) })); } } - return qref(methodCall(ref('ctx.stash.put'), str('authFilter'), obj({ or: list(authFilter) }))); + return [qref(methodCall(ref('ctx.stash.put'), str('authFilter'), obj({ or: list(authFilter) })))]; }; -export const jwtQueryAuthExpression = ( - provider: string, +export const generateAuthExpressionForQueries = ( + provider: ConfiguredAuthProviders, + roles: Array, fields: ReadonlyArray, - staticRoles: Array, - dynamicRoles: Array, -): Expression | null => { - const authExpressions = new Array(); - if (dynamicRoles.length > 0) { - authExpressions.push(generateAuthFilter(dynamicRoles, fields)); +): string => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, apiKeyRoles, iamRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [set(ref(IS_AUTHORIZED_FLAG), bool(false))]; + if (provider.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); } - if (staticRoles.length > 0) { - authExpressions.push(...staticRuleExpression(staticRoles)); + if (provider.hasIAM) { + iamExpression(iamRoles, provider.hasAdminUIEnabled); } - if (authExpressions.length > 0) { - authExpressions.push( + if (provider.hasUserPools) { + totalAuthExpressions.push( iff( - and([not(ref('isStaticAuthorized')), methodCall(ref('util.isNullOrEmpty'), methodCall(ref('ctx.stash.get'), str('authFilter')))]), - ref('util.unauthorized()'), + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...generateAuthFilter(cognitoDynamicRoles, fields), ...staticRuleExpression(cognitoStaticGroupRoles)]), ), ); - return iff(equals(ref('util.authType()'), str(provider)), compoundExpression(authExpressions)); } - return null; -}; - -export const generateAuthExpressionForQueries = (roles: Array, fields: ReadonlyArray): string => { - const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles } = splitRoles(roles); - const totalAuthExpressions = Array(); - const cognitoAuthExpression = jwtQueryAuthExpression(COGNITO_AUTH_TYPE, fields, cognitoStaticGroupRoles, cognitoDynamicRoles); - const oidcAuthExpression = jwtQueryAuthExpression(OIDC_AUTH_TYPE, fields, oidcStaticGroupRoles, oidcDynamicRoles); - if (cognitoAuthExpression) totalAuthExpressions.push(cognitoAuthExpression); - if (oidcAuthExpression) totalAuthExpressions.push(oidcAuthExpression); - return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); -}; - -// Field Read VTL Functions -export const generateDynamicAuthReadExpression = (roles: Array) => { - const ownerExpression = new Array(); - const dynamicGroupExpression = new Array(); - const ownerRuleLoop = [ - iff( - not(ref('isFieldAuthorized')), - forEach(ref('ownerRole'), ref('dynamicRoles.owner'), [ - set(ref('ownerEntity'), ref('ownerRole.entity')), - set(ref('ownerClaim'), ref('ownerRole.claim')), - iff( - methodCall(ref('util.isList'), ref('ownerEntity')), - forEach(ref('allowedOwner'), ref('ownerEntity'), [ - iff( - equals(ref('allowedOwner'), ref('ownerClaim')), - compoundExpression([ - set(ref('isFieldAuthorized'), bool(true)), - // break from inner loop - str('#break'), - ]), - ), - ]), - ), - iff( - methodCall(ref('util.isString'), ref('ownerEntity')), - iff(equals(ref('ownerEntity'), ref('ownerClaim')), set(ref('isFieldAuthorized'), bool(true))), - ), - iff(ref('isFieldAuthorized'), str('#break')), - ]), - ), - ]; - const dynamicGroupLoop = [ - iff( - not(ref('isFieldAuthorized')), - forEach(ref('dynamicGroupRole'), ref('dynamicRoles.dynamicGroups'), [ - set(ref('dynamicGroupEntity'), ref('dynamicGroupRole.entity')), - set(ref('dynamicGroupClaim'), ref('dynamicGroupRole.claim')), - forEach(ref('userGroup'), ref('dynamicGroupClaim'), [ - iff( - methodCall(ref('util.isList'), ref('dynamicGroupEntity')), - iff( - methodCall(ref('dynamicGroupEntity.contains'), ref('userGroup')), - compoundExpression([ - set(ref('isFieldAuthorized'), bool(true)), - // break from inner loop - str('#break'), - ]), - ), - ), - iff( - methodCall(ref('util.isString'), ref('dynamicGroupEntity')), - iff(equals(ref('dynamicGroupEntity'), ref('userGroup')), set(ref('isFieldAuthorized'), bool(true))), - ), - ]), - iff(ref('isFieldAuthorized'), str('#break')), - ]), - ), - ]; - - for (let role of roles) { - if (role.strategy === 'owner') { - ownerExpression.push( - obj({ - entity: methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity}`), list([])), - claim: getOwnerExpression(role.claim!), - }), - ); - } - if (role.strategy === 'groups') { - dynamicGroupExpression.push( - obj({ - entity: methodCall(ref('util.defaultIfNull'), ref(`ctx.source.${role.entity}`), list([])), - claim: getIdentityClaimExp(str(role.claim!), list([])), - }), - ); - } - } - return compoundExpression([ - set(ref('dynamicRoles'), obj({ owner: list(ownerExpression), dynamicGroups: list(dynamicGroupExpression) })), - ...(ownerExpression.length > 0 ? ownerRuleLoop : []), - ...(dynamicGroupExpression.length > 0 ? dynamicGroupLoop : []), - ]); -}; - -/** - * if the apiKey and iam providers are not listed for those operations/field then they are denied - * @param allowed can be used for query/field resolvers - * @returns - */ -export const apiKeyReadExpression = (allowed: boolean = false): Expression => { - return iff(equals(ref('util.authType()'), str(API_KEY_AUTH_TYPE)), set(ref('isStaticGroupAuthorized'), bool(allowed))); -}; - -export const jwtFieldAuthExpression = ( - provider: string, - staticRoles: Array, - dynamicRoles: Array, -): Expression | null => { - const authExpressions = new Array(); - if (staticRoles.length > 0) { - authExpressions.push(...staticRuleExpression(staticRoles)); - } - if (dynamicRoles.length > 0) { - authExpressions.push(iff(not(ref('isFieldAuthorized')), generateDynamicAuthReadExpression(dynamicRoles))); - } - if (authExpressions.length > 0) { - authExpressions.push(iff(not(ref('isFieldAuthorized')), ref('util.unauthorized()'))); - return iff(equals(ref('util.authType()'), str(provider)), compoundExpression(authExpressions)); - } - return null; -}; - -export const generateAuthExpressionForField = (roles: Array): string => { - const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, apiKeyRoles } = splitRoles(roles); - const totalAuthExpressions = Array(); - const canApiRead = apiKeyRoles.length > 0 ? true : false; - totalAuthExpressions.push(apiKeyReadExpression(canApiRead)); - const cognitoAuthExpression = jwtFieldAuthExpression(COGNITO_AUTH_TYPE, cognitoStaticGroupRoles, cognitoDynamicRoles); - const oidcAuthExpression = jwtFieldAuthExpression(OIDC_AUTH_TYPE, oidcStaticGroupRoles, oidcDynamicRoles); - if (cognitoAuthExpression) totalAuthExpressions.push(cognitoAuthExpression); - if (oidcAuthExpression) totalAuthExpressions.push(oidcAuthExpression); - return printBlock('Field Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); -}; - -/** - * This is the response resolver for fields to protect subscriptions - * @param subscriptionsEnabled - * @returns - */ -export const generateFieldAuthResponse = (operation: string, fieldName: string, subscriptionsEnabled: boolean): string => { - if (subscriptionsEnabled) { - return printBlock('Checking for allowed operations which can return this field')( - compoundExpression([ - set(ref('operation'), methodCall(ref('util.defaultIfNull'), ref('context.source.operation'), nul())), - ifElse(equals(ref('operation'), str(operation)), toJson(nul()), toJson(ref(`context.source.${fieldName}`))), - ]), + if (provider.hasOIDC) { + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...generateAuthFilter(oidcDynamicRoles, fields), ...staticRuleExpression(oidcStaticGroupRoles)]), + ), ); } - return print(toJson(ref(`context.source.${fieldName}`))); + totalAuthExpressions.push( + iff( + and([not(ref(IS_AUTHORIZED_FLAG)), methodCall(ref('util.isNullOrEmpty'), methodCall(ref('ctx.stash.get'), str('authFilter')))]), + ref('util.unauthorized()'), + ), + ); + return printBlock('Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); }; diff --git a/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts b/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts new file mode 100644 index 00000000000..71e39096681 --- /dev/null +++ b/packages/amplify-graphql-auth-transformer/src/resolvers/subscriptions.ts @@ -0,0 +1,71 @@ +import { + bool, + compoundExpression, + equals, + Expression, + iff, + methodCall, + not, + ref, + set, + str, + list, + nul, + toJson, + obj, + printBlock, +} from 'graphql-mapping-template'; +import { COGNITO_AUTH_TYPE, ConfiguredAuthProviders, IS_AUTHORIZED_FLAG, OIDC_AUTH_TYPE, RoleDefinition, splitRoles } from '../utils'; +import { staticGroupRoleExpression, getInputFields, getOwnerClaim, apiKeyExpression, iamExpression } from './helpers'; + +const dynamicRoleExpression = (roles: Array): Array => { + const ownerExpression = new Array(); + // we only check against owner rules which are not list fields + roles.forEach((role, idx) => { + if (role.strategy === 'owner') { + ownerExpression.push( + iff( + not(ref(IS_AUTHORIZED_FLAG)), + compoundExpression([ + set(ref(`ownerEntity${idx}`), methodCall(ref('util.defaultIfNull'), ref(`ctx.args.${role.entity!}`), nul())), + set(ref(`ownerClaim${idx}`), getOwnerClaim(role.claim!)), + iff(equals(ref(`ownerClaim${idx}`), ref(`ownerClaim${idx}`)), set(ref(IS_AUTHORIZED_FLAG), bool(true))), + ]), + ), + ); + } + }); + + return [...(ownerExpression.length > 0 ? ownerExpression : [])]; +}; + +export const generateAuthExpressionForSubscriptions = (providers: ConfiguredAuthProviders, roles: Array): string => { + const { cognitoStaticGroupRoles, cognitoDynamicRoles, oidcStaticGroupRoles, oidcDynamicRoles, iamRoles, apiKeyRoles } = splitRoles(roles); + const totalAuthExpressions: Array = [ + getInputFields(), + set(ref(IS_AUTHORIZED_FLAG), bool(false)), + set(ref('allowedFields'), list([])), + ]; + if (providers.hasApiKey) { + totalAuthExpressions.push(apiKeyExpression(apiKeyRoles)); + } + if (providers.hasIAM) { + totalAuthExpressions.push(iamExpression(iamRoles, providers.hasAdminUIEnabled)); + } + if (providers.hasUserPools) + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(COGNITO_AUTH_TYPE)), + compoundExpression([...staticGroupRoleExpression(cognitoStaticGroupRoles), ...dynamicRoleExpression(cognitoDynamicRoles)]), + ), + ); + if (providers.hasOIDC) + totalAuthExpressions.push( + iff( + equals(ref('util.authType()'), str(OIDC_AUTH_TYPE)), + compoundExpression([...staticGroupRoleExpression(oidcStaticGroupRoles), ...dynamicRoleExpression(oidcDynamicRoles)]), + ), + ); + totalAuthExpressions.push(iff(not(ref(IS_AUTHORIZED_FLAG)), ref('util.unauthorized()'))); + return printBlock('Create Authorization Steps')(compoundExpression([...totalAuthExpressions, toJson(obj({}))])); +}; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/constants.ts b/packages/amplify-graphql-auth-transformer/src/utils/constants.ts index 67082b0bf57..ed5bb2fee96 100644 --- a/packages/amplify-graphql-auth-transformer/src/utils/constants.ts +++ b/packages/amplify-graphql-auth-transformer/src/utils/constants.ts @@ -21,6 +21,14 @@ export const COGNITO_AUTH_TYPE = 'User Pool Authorization'; export const OIDC_AUTH_TYPE = 'Open ID Connect Authorization'; export const IAM_AUTH_TYPE = 'IAM Authorization'; export const API_KEY_AUTH_TYPE = 'API Key Authorization'; +// resolver refs +export const IS_AUTHORIZED_FLAG = 'isAuthorized'; +export const ALLOWED_FIELDS = 'allowedFields'; +export const NULL_ALLOWED_FIELDS = 'nullAllowedFields'; +export const DENIED_FIELDS = 'deniedFields'; // iam parameter names export const IAM_AUTH_ROLE_PARAMETER = 'authRoleName'; export const IAM_UNAUTH_ROLE_PARAMETER = 'unauthRoleName'; +// Admin Roles +export const ADMIN_ROLE = '_Full-access/CognitoIdentityCredentials'; +export const MANAGE_ROLE = '_Manage-only/CognitoIdentityCredentials'; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts b/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts index c6fd46764f0..8b4d729894e 100644 --- a/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts +++ b/packages/amplify-graphql-auth-transformer/src/utils/definitions.ts @@ -10,7 +10,7 @@ export interface RolesByProvider { cognitoDynamicRoles: Array; oidcStaticGroupRoles: Array; oidcDynamicRoles: Array; - iamRoles?: Array; + iamRoles: Array; apiKeyRoles: Array; } @@ -33,6 +33,9 @@ export interface RoleDefinition { static: boolean; claim?: string; entity?: string; + // specific to mutations + allowedFields?: Array; + nullAllowedFields?: Array; } export interface AuthDirective { @@ -46,6 +49,7 @@ export interface ConfiguredAuthProviders { hasUserPools: boolean; hasOIDC: boolean; hasIAM: boolean; + hasAdminUIEnabled: boolean; } export interface AuthTransformerConfig { diff --git a/packages/amplify-graphql-auth-transformer/src/utils/index.ts b/packages/amplify-graphql-auth-transformer/src/utils/index.ts index 98cf2b7b1da..e3649c5c60e 100644 --- a/packages/amplify-graphql-auth-transformer/src/utils/index.ts +++ b/packages/amplify-graphql-auth-transformer/src/utils/index.ts @@ -1,8 +1,8 @@ import { ModelDirectiveConfiguration, SubscriptionLevel } from '@aws-amplify/graphql-model-transformer'; -import { AppSyncAuthConfiguration, AppSyncAuthMode, DirectiveWrapper } from '@aws-amplify/graphql-transformer-core'; +import { AppSyncAuthMode, DirectiveWrapper } from '@aws-amplify/graphql-transformer-core'; import { DirectiveNode } from 'graphql'; import { toCamelCase, plurality } from 'graphql-transformer-common'; -import { AuthProvider, AuthRule } from './definitions'; +import { AuthProvider, AuthRule, AuthTransformerConfig, ConfiguredAuthProviders, RoleDefinition, RolesByProvider } from './definitions'; export * from './constants'; export * from './definitions'; @@ -10,6 +10,16 @@ export * from './validations'; export * from './schema'; export * from './iam'; +export const splitRoles = (roles: Array): RolesByProvider => { + return { + cognitoStaticGroupRoles: roles.filter(r => r.static && r.provider === 'userPools'), + cognitoDynamicRoles: roles.filter(r => !r.static && r.provider === 'userPools'), + oidcStaticGroupRoles: roles.filter(r => r.static && r.provider === 'oidc'), + oidcDynamicRoles: roles.filter(r => !r.static && r.provider === 'oidc'), + iamRoles: roles.filter(r => r.provider === 'iam'), + apiKeyRoles: roles.filter(r => r.provider === 'apiKey'), + }; +}; /** * Ensure the following defaults * - provider @@ -54,7 +64,7 @@ export const getModelConfig = (directive: DirectiveNode, typeName: string): Mode delete: toCamelCase(['delete', typeName]), }, subscriptions: { - level: SubscriptionLevel.public, + level: SubscriptionLevel.on, onCreate: [toCamelCase(['onCreate', typeName])], onDelete: [toCamelCase(['onDelete', typeName])], onUpdate: [toCamelCase(['onUpdate', typeName])], @@ -67,10 +77,10 @@ export const getModelConfig = (directive: DirectiveNode, typeName: string): Mode return options; }; -export const getConfiguredAuthProviders = (authConfig: AppSyncAuthConfiguration) => { +export const getConfiguredAuthProviders = (config: AuthTransformerConfig): ConfiguredAuthProviders => { const providers = [ - authConfig.defaultAuthentication.authenticationType, - ...authConfig.additionalAuthenticationProviders.map(p => p.authenticationType), + config.authConfig.defaultAuthentication.authenticationType, + ...config.authConfig.additionalAuthenticationProviders.map(p => p.authenticationType), ]; const getAuthProvider = (authType: AppSyncAuthMode): AuthProvider => { switch (authType) { @@ -84,13 +94,15 @@ export const getConfiguredAuthProviders = (authConfig: AppSyncAuthConfiguration) return 'oidc'; } }; - - return { - default: getAuthProvider(authConfig.defaultAuthentication.authenticationType), - onlyDefaultAuthProviderConfigured: authConfig.additionalAuthenticationProviders.length === 0, + const hasIAM = providers.some(p => p === 'AWS_IAM'); + const configuredProviders: ConfiguredAuthProviders = { + default: getAuthProvider(config.authConfig.defaultAuthentication.authenticationType), + onlyDefaultAuthProviderConfigured: config.authConfig.additionalAuthenticationProviders.length === 0, + hasAdminUIEnabled: hasIAM && config.addAwsIamAuthInOutputSchema, hasApiKey: providers.some(p => p === 'API_KEY'), hasUserPools: providers.some(p => p === 'AMAZON_COGNITO_USER_POOLS'), hasOIDC: providers.some(p => p === 'OPENID_CONNECT'), - hasIAM: providers.some(p => p === 'AWS_IAM'), + hasIAM, }; + return configuredProviders; }; diff --git a/packages/amplify-graphql-auth-transformer/src/utils/schema.ts b/packages/amplify-graphql-auth-transformer/src/utils/schema.ts index 87240d17918..ebad4659c04 100644 --- a/packages/amplify-graphql-auth-transformer/src/utils/schema.ts +++ b/packages/amplify-graphql-auth-transformer/src/utils/schema.ts @@ -1,11 +1,26 @@ -import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { ModelDirectiveConfiguration, SubscriptionLevel } from '@aws-amplify/graphql-model-transformer'; +import { QueryFieldType, MutationFieldType, TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; import { ObjectTypeDefinitionNode, FieldDefinitionNode, DirectiveNode, NamedTypeNode } from 'graphql'; -import { blankObjectExtension, extendFieldWithDirectives, extensionWithDirectives } from 'graphql-transformer-common'; +import { + blankObjectExtension, + extendFieldWithDirectives, + extensionWithDirectives, + isListType, + makeInputValueDefinition, + makeNamedType, + plurality, + toCamelCase, +} from 'graphql-transformer-common'; +import { RoleDefinition } from './definitions'; export const collectFieldNames = (object: ObjectTypeDefinitionNode): Array => { return object.fields!.map((field: FieldDefinitionNode) => field.name.value); }; +export const fieldIsList = (fields: ReadonlyArray, fieldName: string) => { + return fields.some(field => field.name.value === fieldName && isListType(field.type)); +}; + export const extendTypeWithDirectives = (ctx: TransformerContextProvider, typeName: string, directives: Array): void => { let objectTypeExtension = blankObjectExtension(typeName); objectTypeExtension = extensionWithDirectives(objectTypeExtension, directives); @@ -34,6 +49,27 @@ export const addDirectivesToField = ( } }; +export const addSubscriptionArguments = ( + ctx: TransformerContextProvider, + operationName: string, + subscriptionRoles: Array, +) => { + let subscription = ctx.output.getSubscription()!; + let createField: FieldDefinitionNode = subscription!.fields!.find(field => field.name.value === operationName) as FieldDefinitionNode; + const subcriptionArgumentList = subscriptionRoles.map(role => { + return makeInputValueDefinition(role.entity!, makeNamedType('String')); + }); + createField = { + ...createField, + arguments: subcriptionArgumentList, + }; + subscription = { + ...subscription, + fields: subscription!.fields!.map(field => (field.name.value === operationName ? createField : field)), + }; + ctx.output.putType(subscription); +}; + export const addDirectivesToOperation = ( ctx: TransformerContextProvider, typeName: string, @@ -59,3 +95,105 @@ export const addDirectivesToOperation = ( } } }; + +export const getQueryFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ fieldName: string; typeName: string; type: QueryFieldType }> => { + const fields: Set<{ fieldName: string; typeName: string; type: QueryFieldType }> = new Set(); + if (modelDirectiveConfig?.queries?.get) { + fields.add({ + typeName: 'Query', + fieldName: modelDirectiveConfig.queries.get, + type: QueryFieldType.GET, + }); + } + + if (modelDirectiveConfig?.queries?.list) { + fields.add({ + typeName: 'Query', + fieldName: modelDirectiveConfig.queries.list, + type: QueryFieldType.LIST, + }); + } + // check if this API is sync enabled and then if the model is sync enabled + // fields.add({ + // typeName: 'Query', + // fieldName: camelCase(`sync ${typeName}`), + // type: QueryFieldType.SYNC, + // }); + return fields; +}; + +export const getMutationFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ fieldName: string; typeName: string; type: MutationFieldType }> => { + // Todo: get fields names from the directives + const getMutationType = (type: string): MutationFieldType => { + switch (type) { + case 'create': + return MutationFieldType.CREATE; + case 'update': + return MutationFieldType.UPDATE; + case 'delete': + return MutationFieldType.DELETE; + default: + throw new Error('Unknown mutation type'); + } + }; + + const fieldNames: Set<{ fieldName: string; typeName: string; type: MutationFieldType }> = new Set(); + for (let [mutationType, mutationName] of Object.entries(modelDirectiveConfig?.mutations || {})) { + if (mutationName) { + fieldNames.add({ + typeName: 'Mutation', + fieldName: mutationName, + type: getMutationType(mutationType), + }); + } + } + + return fieldNames; +}; + +export const getSubscriptionFieldNames = ( + modelDirectiveConfig: ModelDirectiveConfiguration, +): Set<{ + fieldName: string; + typeName: string; +}> => { + const fields: Set<{ + fieldName: string; + typeName: string; + }> = new Set(); + + if (modelDirectiveConfig?.subscriptions?.level === SubscriptionLevel.on) { + if (modelDirectiveConfig?.subscriptions?.onCreate && modelDirectiveConfig.mutations?.create) { + for (const fieldName of modelDirectiveConfig.subscriptions.onCreate) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + + if (modelDirectiveConfig?.subscriptions?.onUpdate && modelDirectiveConfig.mutations?.update) { + for (const fieldName of modelDirectiveConfig.subscriptions.onUpdate) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + + if (modelDirectiveConfig?.subscriptions?.onDelete && modelDirectiveConfig.mutations?.delete) { + for (const fieldName of modelDirectiveConfig.subscriptions.onDelete) { + fields.add({ + typeName: 'Subscription', + fieldName: fieldName, + }); + } + } + } + + return fields; +}; diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap index 1f2df489f80..8e1d282bc57 100644 --- a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap +++ b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-index-transformer.test.ts.snap @@ -13,7 +13,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -128,10 +128,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -205,6 +206,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -217,7 +219,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -377,6 +379,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -412,11 +415,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -676,6 +679,33 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -692,7 +722,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -807,10 +837,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -884,6 +915,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -896,7 +928,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1056,6 +1088,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -1091,11 +1124,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -1277,6 +1310,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -1293,7 +1353,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1408,10 +1468,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1485,6 +1546,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -1497,7 +1559,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1657,6 +1719,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -1692,11 +1755,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -1994,6 +2057,33 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -2229,7 +2319,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2329,10 +2419,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2404,6 +2495,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -2416,7 +2508,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2561,10 +2653,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"createdAt\\": $util.dynamodb.toDynamoDB($ctx.args.createdAt) @@ -2603,11 +2696,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -2736,7 +2829,7 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -2967,6 +3060,33 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -2983,7 +3103,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.addContentToCategory.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.addContentToCategory.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3098,6 +3218,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3200,6 +3321,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3214,7 +3336,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3321,6 +3443,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3335,7 +3458,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3435,6 +3558,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3537,6 +3661,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3601,10 +3726,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteContentFromCategory.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteContentFromCategory.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3678,10 +3804,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3753,10 +3880,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3828,6 +3956,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3892,6 +4021,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -4037,6 +4167,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -4049,7 +4180,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateItem.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateItem.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -4201,6 +4332,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -4213,7 +4345,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -4358,6 +4490,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -4503,6 +4636,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -4576,16 +4710,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getBlog.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.getItem.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getItem.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"orderId\\": $util.dynamodb.toDynamoDB($ctx.args.orderId), \\"status#createdAt\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.status}#\${ctx.args.createdAt}\\") @@ -4624,16 +4758,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getItem.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"createdAt\\": $util.dynamodb.toDynamoDB($ctx.args.createdAt) @@ -4672,11 +4806,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -4713,11 +4847,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTodo.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -5196,7 +5330,7 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", - "Query.listItems.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listItems.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.orderId) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'orderId'.\\", \\"InvalidArgumentsError\\") #end @@ -5357,7 +5491,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -5666,5 +5800,131 @@ $util.toJson($QueryRequest)", $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)", + "Subscription.onCreateBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateBlog.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateContentCategory.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateContentCategory.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateItem.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTodo.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteBlog.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteContentCategory.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteContentCategory.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteItem.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTodo.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateBlog.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateBlog.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateItem.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateItem.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTodo.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTodo.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap index 0f5f7e090d5..555328e335c 100644 --- a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap +++ b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap @@ -13,7 +13,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -120,10 +120,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -195,6 +196,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -207,7 +209,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -359,10 +361,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.kind}#\${ctx.args.other}\\") @@ -401,16 +404,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -571,6 +574,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -587,7 +617,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -687,10 +717,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -762,6 +793,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -774,7 +806,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -919,10 +951,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.kind) @@ -961,16 +994,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") #end @@ -1085,6 +1118,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -1101,7 +1161,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1200,10 +1260,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1274,6 +1335,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -1286,7 +1348,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1430,10 +1492,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -1471,16 +1534,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -1552,6 +1615,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -1568,7 +1658,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1668,10 +1758,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1743,6 +1834,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -1755,7 +1847,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -1900,10 +1992,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.status), \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.lastStatus) @@ -1942,16 +2035,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.status) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'status'.\\", \\"InvalidArgumentsError\\") #end @@ -2066,6 +2159,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -2082,7 +2202,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2182,10 +2302,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2257,6 +2378,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -2271,7 +2393,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testCreate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2371,10 +2493,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testDelete.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2446,6 +2569,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -2458,7 +2582,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testUpdate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2603,6 +2727,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -2615,7 +2740,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -2760,10 +2885,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -2801,16 +2927,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -2882,7 +3008,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.testGet.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.testGet.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) @@ -2921,16 +3047,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.testGet.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.testList.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.testList.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -3045,6 +3171,33 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; @@ -3061,7 +3214,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3160,10 +3313,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3234,6 +3388,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3248,7 +3403,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testCreate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testCreate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3348,10 +3503,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.testDelete.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testDelete.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3423,6 +3579,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3435,7 +3592,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.testUpdate.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.testUpdate.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3580,6 +3737,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -3592,7 +3750,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -3736,10 +3894,11 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.getTest.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getTest.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) })) @@ -3777,16 +3936,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getTest.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.listTests.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTests.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -3858,7 +4017,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.testGet.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.testGet.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) @@ -3897,16 +4056,16 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.testGet.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end ## [End] Get Response template. **", - "Query.testList.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.testList.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -4021,5 +4180,32 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; diff --git a/packages/amplify-graphql-index-transformer/src/resolvers.ts b/packages/amplify-graphql-index-transformer/src/resolvers.ts index 525a022cd40..98678c2f26d 100644 --- a/packages/amplify-graphql-index-transformer/src/resolvers.ts +++ b/packages/amplify-graphql-index-transformer/src/resolvers.ts @@ -505,7 +505,7 @@ function addIndexToResolverSlot(resolver: TransformerResolverProvider, lines: st const res = resolver as any; res.addToSlot( - 'postAuth', + 'preAuth', MappingTemplate.s3MappingTemplateFromString( lines.join('\n') + '\n{}', `${res.typeName}.${res.fieldName}.{slotName}.{slotIndex}.req.vtl`, diff --git a/packages/amplify-graphql-model-transformer/src/definitions.ts b/packages/amplify-graphql-model-transformer/src/definitions.ts index e1aafe51b0e..77d4eb31c58 100644 --- a/packages/amplify-graphql-model-transformer/src/definitions.ts +++ b/packages/amplify-graphql-model-transformer/src/definitions.ts @@ -13,4 +13,4 @@ export const BOOLEAN_FUNCTIONS = new Set(['attributeExists', 'attributeT export const ATTRIBUTE_TYPES = ['binary', 'binarySet', 'bool', 'list', 'map', 'number', 'numberSet', 'string', 'stringSet', '_null']; - +export const OPERATION_KEY = '__operation'; diff --git a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts index e206a23ee77..54a73ef3919 100644 --- a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts +++ b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts @@ -155,7 +155,6 @@ export class ModelTransformer extends TransformerModelBase implements Transforme `'${definition.name.value}' is a reserved type name and currently in use within the default schema element.`, ); } - // todo: get model configuration with default values and store it in the map const typeName = definition.name.value; const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive); @@ -170,7 +169,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme delete: toCamelCase(['delete', typeName]), }, subscriptions: { - level: SubscriptionLevel.public, + level: SubscriptionLevel.on, onCreate: [toCamelCase(['onCreate', typeName])], onDelete: [toCamelCase(['onDelete', typeName])], onUpdate: [toCamelCase(['onUpdate', typeName])], @@ -218,7 +217,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme generateResolvers = (context: TransformerContextProvider): void => { for (let type of this.typesWithModelDirective) { - const def = context.output.getObject(type); + const def = context.output.getObject(type)!; // add the table const tableLogicalName = `${def!.name.value}Table`; const tableName = context.resourceHelper.generateResourceName(def!.name.value); @@ -355,6 +354,30 @@ export class ModelTransformer extends TransformerModelBase implements Transforme resolver.mapToStack(stack); context.resolvers.addResolver(mutation.typeName, mutation.fieldName, resolver); } + + const subscriptionLevel = this.modelDirectiveConfig.get(def.name.value)?.subscriptions?.level; + // in order to create subscription resolvers the level needs to be on + if (subscriptionLevel === SubscriptionLevel.on) { + const subscriptionFields = this.getSubscriptionFieldNames(context, def!); + for (let subscription of subscriptionFields.values()) { + let resolver; + switch (subscription.type) { + case SubscriptionFieldType.ON_CREATE: + resolver = this.generateOnCreateResolver(context, def, subscription.typeName, subscription.fieldName); + break; + case SubscriptionFieldType.ON_UPDATE: + resolver = this.generateOnUpdateResolver(context, def, subscription.typeName, subscription.fieldName); + break; + case SubscriptionFieldType.ON_DELETE: + resolver = this.generateOnDeleteResolver(context, def, subscription.typeName, subscription.fieldName); + break; + default: + throw new Error('Unkown subscription field type'); + } + resolver.mapToStack(stack); + context.resolvers.addResolver(subscription.typeName, subscription.fieldName, resolver); + } + } } }; @@ -412,7 +435,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme fieldName, dataSource, MappingTemplate.s3MappingTemplateFromString(generateUpdateRequestTemplate(typeName), `${typeName}.${fieldName}.req.vtl`), - MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(), `${typeName}.${fieldName}.res.vtl`), + MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(true), `${typeName}.${fieldName}.res.vtl`), ); // Todo: get the slot index from the resolver to keep the name unique and show the order of functions resolver.addToSlot( @@ -440,7 +463,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme fieldName, dataSource, MappingTemplate.s3MappingTemplateFromString(generateDeleteRequestTemplate(), `${typeName}.${fieldName}.req.vtl`), - MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(), `${typeName}.${fieldName}.res.vtl`), + MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(true), `${typeName}.${fieldName}.res.vtl`), ); } return this.resolverMap[resolverKey]; @@ -739,7 +762,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme fieldName, dataSource, MappingTemplate.s3MappingTemplateFromString(generateCreateRequestTemplate(type.name.value), `${typeName}.${fieldName}.req.vtl`), - MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(), `${typeName}.${fieldName}.res.vtl`), + MappingTemplate.s3MappingTemplateFromString(generateDefaultResponseMappingTemplate(true), `${typeName}.${fieldName}.res.vtl`), ); this.resolverMap[resolverKey] = resolver; resolver.addToSlot( diff --git a/packages/amplify-graphql-model-transformer/src/index.ts b/packages/amplify-graphql-model-transformer/src/index.ts index aa9b0c46e43..10267d40d30 100644 --- a/packages/amplify-graphql-model-transformer/src/index.ts +++ b/packages/amplify-graphql-model-transformer/src/index.ts @@ -1,3 +1,4 @@ export { ModelTransformer, ModelDirectiveConfiguration, SubscriptionLevel } from './graphql-model-transformer'; +export { OPERATION_KEY } from './definitions'; export * from './graphql-types'; export * from './resolvers'; diff --git a/packages/amplify-graphql-model-transformer/src/resolvers/common.ts b/packages/amplify-graphql-model-transformer/src/resolvers/common.ts index 2d470bd82e2..c2cf1c8fdfd 100644 --- a/packages/amplify-graphql-model-transformer/src/resolvers/common.ts +++ b/packages/amplify-graphql-model-transformer/src/resolvers/common.ts @@ -15,7 +15,10 @@ import { ifElse, printBlock, toJson, + qref, + str, } from 'graphql-mapping-template'; +import { OPERATION_KEY } from '../definitions'; /** * Helper method to generate code that converts DynamoDB condition object to condition @@ -57,13 +60,19 @@ export const generateConditionSlot = (inputConditionObjectName: string, conditio /** * Generate common response template used by most of the resolvers. + * Append operation if response is coming from a mutation, this is to protect field resolver for subscriptions */ -export const generateDefaultResponseMappingTemplate = (): string => { - const statements: Expression[] = [ - ifElse(ref('ctx.error'), methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type')), toJson(ref('ctx.result'))), - ]; - - return printBlock('Get ResponseTemplate')(compoundExpression(statements)); +export const generateDefaultResponseMappingTemplate = (mutation = false): string => { + const setOperation = mutation ? [qref(methodCall(ref('ctx.result.put'), str(OPERATION_KEY), str('Mutation')))] : []; + return printBlock('Get ResponseTemplate')( + compoundExpression([ + ifElse( + ref('ctx.error'), + methodCall(ref('util.error'), ref('ctx.error.message'), ref('ctx.error.type')), + compoundExpression([...setOperation, toJson(ref('ctx.result'))]), + ), + ]), + ); }; /** diff --git a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap index d06564b1739..437eae3cd37 100644 --- a/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap +++ b/packages/amplify-graphql-relational-transformer/src/__tests__/__snapshots__/amplify-graphql-has-many-transformer.test.ts.snap @@ -8175,7 +8175,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -8275,6 +8275,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8377,6 +8378,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8479,6 +8481,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8581,6 +8584,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8595,7 +8599,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createPost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createPost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -8694,6 +8698,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8796,6 +8801,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -8898,6 +8904,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9000,6 +9007,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9102,6 +9110,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9116,7 +9125,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9223,6 +9232,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9237,7 +9247,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9344,6 +9354,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9358,7 +9369,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.createUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9370,7 +9381,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { ## [End] Set the primary key. ** {}", - "Mutation.createUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.createUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9485,10 +9496,11 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9560,6 +9572,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9624,6 +9637,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9688,6 +9702,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9752,10 +9767,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deletePost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deletePost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -9826,6 +9842,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9890,6 +9907,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -9954,6 +9972,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10018,6 +10037,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10082,10 +10102,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10157,10 +10178,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10232,10 +10254,11 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Mutation.deleteUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10246,7 +10269,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { })) ## [End] Set the primary key. ** {}", - "Mutation.deleteUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.deleteUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10320,6 +10343,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10332,7 +10356,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateChild.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateChild.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -10477,6 +10501,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10622,6 +10647,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10767,6 +10793,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10912,6 +10939,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -10924,7 +10952,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updatePost.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updatePost.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11068,6 +11096,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11213,6 +11242,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11358,6 +11388,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11503,6 +11534,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11648,6 +11680,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11660,7 +11693,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateTest1.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateTest1.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11812,6 +11845,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11824,7 +11858,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateUser.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUser.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -11976,6 +12010,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -11988,7 +12023,7 @@ $util.toJson({ \\"payload\\": {} }) ## [End] Initialization default values. **", - "Mutation.updateUserModel.postAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUserModel.preAuth.1.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -12000,7 +12035,7 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { ## [End] Set the primary key. ** {}", - "Mutation.updateUserModel.postAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** + "Mutation.updateUserModel.preAuth.2.req.vtl": "## [Start] Merge default values and inputs. ** #set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) $util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) ## [End] Merge default values and inputs. ** @@ -12160,6 +12195,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -12565,7 +12601,7 @@ $util.error($ctx.error.message, $ctx.error.type) #else $util.toJson($ctx.result) #end", - "Query.getChild.postAuth.1.req.vtl": "## [Start] Set the primary key. ** + "Query.getChild.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"name\\": $util.dynamodb.toDynamoDB($ctx.args.name) @@ -12575,92 +12611,168 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getChild.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getChild.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getChild.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getComment.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getComment.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getComment.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getFriendship.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getFriendship.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getFriendship.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getParent.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getParent.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getParent.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getPost.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getPost.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"title\\": $util.dynamodb.toDynamoDB($ctx.args.title) })) @@ -12669,92 +12781,168 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getPost.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPost.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getPost.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getPostAuthor.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPostAuthor.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getPostAuthor.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getPostModel.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getPostModel.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getPostModel.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", +## [End] Get Response template. **", "Query.getTest.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getTest.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getTest1.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getTest1.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"email#name\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.email}#\${ctx.args.name}\\") @@ -12764,26 +12952,45 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getTest1.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getTest1.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getTest1.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getUser.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getUser.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"name#surname\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.name}#\${ctx.args.surname}\\") @@ -12793,26 +13000,45 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getUser.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUser.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getUser.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.getUserModel.postAuth.1.req.vtl": "## [Start] Set the primary key. ** +## [End] Get Response template. **", + "Query.getUserModel.preAuth.1.req.vtl": "## [Start] Set the primary key. ** $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), \\"rollNumber\\": $util.dynamodb.toDynamoDB($ctx.args.rollNumber) @@ -12822,26 +13048,45 @@ $util.qr($ctx.stash.metadata.put(\\"modelObjectKey\\", { "Query.getUserModel.req.vtl": "## [Start] Get Request template. ** #set( $GetRequest = { \\"version\\": \\"2018-05-29\\", - \\"operation\\": \\"GetItem\\" + \\"operation\\": \\"Query\\" } ) #if( $ctx.stash.metadata.modelObjectKey ) - #set( $key = $ctx.stash.metadata.modelObjectKey ) + #set( $expression = \\"\\" ) + #set( $expressionValues = {} ) + #foreach( $item in $ctx.stash.metadata.modelObjectKey.entrySet() ) + #set( $expression = \\"$expression$item.key = :$item.key AND \\" ) + $util.qr($expressionValues.put(\\":$item.key\\", $item.value)) + #end + #set( $expression = $expression.replaceAll(\\"AND $\\", \\"\\") ) + #set( $query = { + \\"expression\\": $expression, + \\"expressionValues\\": $expressionValues +} ) #else - #set( $key = { - \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) + #set( $query = { + \\"expression\\": \\"id = :id\\", + \\"expressionValues\\": { + \\":id\\": $util.dynamodb.toDynamoDBJson($ctx.args.id) + } } ) #end -$util.qr($GetRequest.put(\\"key\\", $key)) +$util.qr($GetRequest.put(\\"query\\", $query)) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + $util.qr($GetRequest.put(\\"filter\\", $ctx.stash.get(\\"authFilter\\"))) +#end $util.toJson($GetRequest) ## [End] Get Request template. **", - "Query.getUserModel.res.vtl": "## [Start] Get ResponseTemplate. ** -#if( $ctx.error ) - $util.error($ctx.error.message, $ctx.error.type) + "Query.getUserModel.res.vtl": "## [Start] Get Response template. ** +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) + $util.toJson($ctx.result.items[0]) #else - $util.toJson($ctx.result) + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() + #end + $util.toJson(null) #end -## [End] Get ResponseTemplate. **", - "Query.listChildren.postAuth.1.req.vtl": "## [Start] Set query expression for key ** +## [End] Get Response template. **", + "Query.listChildren.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -12912,8 +13157,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -12953,8 +13210,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -12994,8 +13263,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13035,8 +13316,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13076,8 +13369,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13117,8 +13422,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13149,7 +13466,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.listPosts.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listPosts.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") #end @@ -13177,8 +13494,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13209,7 +13538,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.listTest1s.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listTest1s.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13326,8 +13655,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13367,8 +13708,20 @@ $util.toJson($ListRequest) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13399,7 +13752,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.listUserModels.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listUserModels.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13470,8 +13823,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13502,7 +13867,7 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", - "Query.listUsers.postAuth.1.req.vtl": "## [Start] Set query expression for key ** + "Query.listUsers.preAuth.1.req.vtl": "## [Start] Set query expression for key ** #if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") #end @@ -13619,8 +13984,20 @@ $util.qr($ctx.stash.put(\\"modelQueryExpression\\", $modelQueryExpression)) #if( $context.args.nextToken ) #set( $ListRequest.nextToken = $context.args.nextToken ) #end -#if( $context.args.filter ) - #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) +#if( !$util.isNullOrEmpty($ctx.stash.get(\\"authFilter\\")) ) + #set( $filter = $ctx.stash.get(\\"authFilter\\") ) + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = [{ + \\"and\\": [$filter, $ctx.args.filter] +}] ) + #end +#else + #if( !$util.isNullOrEmpty($ctx.args.filter) ) + #set( $filter = $ctx.args.filter ) + #end +#end +#if( !$util.isNullOrEmpty($filter) ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($filter)) ) #if( !$util.isNullOrBlank($filterExpression.expression) ) #if( $filterEpression.expressionValues.size() == 0 ) $util.qr($filterEpression.remove(\\"expressionValues\\")) @@ -13651,6 +14028,330 @@ $util.toJson($ListRequest) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", + "Subscription.onCreateChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateChild.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateComment.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateFriendship.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateParent.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreatePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostAuthor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreatePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostEditor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreatePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePostModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest1.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUser.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onCreateUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateUserModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteChild.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteComment.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteFriendship.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteParent.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeletePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostAuthor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeletePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostEditor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeletePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePostModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest1.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUser.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteUserModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateChild.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateChild.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateComment.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateComment.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateFriendship.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateFriendship.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateParent.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateParent.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdatePostAuthor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostAuthor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdatePostEditor.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostEditor.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdatePostModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePostModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest1.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest1.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateUser.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUser.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateUserModel.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateUserModel.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", "Test.otherParts.req.vtl": "#if( $util.isNull($ctx.source.id) ) #set( $result = { \\"items\\": [] diff --git a/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap b/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap index 1b94f14ecfa..8b5d2edaaf5 100644 --- a/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap +++ b/packages/amplify-graphql-searchable-transformer/src/__tests__/__snapshots__/amplify-graphql-searchable-transformer.tests.ts.snap @@ -692,6 +692,7 @@ $util.toJson($PutObject) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -756,6 +757,7 @@ $util.toJson($DeleteRequest) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -901,6 +903,7 @@ $util.toJson($UpdateItem) #if( $ctx.error ) $util.error($ctx.error.message, $ctx.error.type) #else + $util.qr($ctx.result.put(\\"__operation\\", \\"Mutation\\")) $util.toJson($ctx.result) #end ## [End] Get ResponseTemplate. **", @@ -936,11 +939,11 @@ $util.qr($GetRequest.put(\\"query\\", $query)) $util.toJson($GetRequest) ## [End] Get Request template. **", "Query.getPost.res.vtl": "## [Start] Get Response template. ** -#if( !$$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +#if( !$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) $util.toJson($ctx.result.items[0]) #else - #if( $$ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) -$util.unauthorized + #if( $ctx.result.items.isEmpty() && $ctx.result.scannedCount == 1 ) +$util.unauthorized() #end $util.toJson(null) #end @@ -1090,6 +1093,33 @@ $util.toJson({ \\"nextToken\\": $nextToken, \\"aggregateItems\\": $aggregateValues })", + "Subscription.onCreatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreatePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeletePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeletePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdatePost.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdatePost.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", } `; diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts index ded8e6cd888..544438499a3 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/datasource.ts @@ -23,4 +23,8 @@ export class TransformerDataSourceManager implements TransformerDataSourceManage collectDataSources = (): Readonly> => { return this.dataSourceMap; }; + + has = (name: string): boolean => { + return this.dataSourceMap.has(name); + }; } diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts index c1472951639..8350f8be702 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transformer-datasource-provider.ts @@ -22,6 +22,7 @@ export type DataSourceInstance = ITable | CfnDomain | HttpDataSource | IFunction export interface TransformerDataSourceManagerProvider { add(type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, dataSourceInstance: DataSourceInstance): void; get(type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode): DataSourceInstance; + has(name: string): boolean; } export interface DataSourceProvider extends BackedDataSource {} diff --git a/packages/graphql-mapping-template/src/print.ts b/packages/graphql-mapping-template/src/print.ts index bd5a90f853c..b871264e156 100644 --- a/packages/graphql-mapping-template/src/print.ts +++ b/packages/graphql-mapping-template/src/print.ts @@ -115,7 +115,7 @@ function printReference(node: ReferenceNode): string { } function printQuietReference(node: QuietReferenceNode, indent: string = ''): string { - const val = typeof node.value === 'string' ? node.value : printExpr(node.value) + const val = typeof node.value === 'string' ? node.value : printExpr(node.value); return `${indent}$util.qr(${val})`; }