Skip to content

Commit

Permalink
Remove pipelineFunction references in favor of resolvers (#8729)
Browse files Browse the repository at this point in the history
* feat(graphql-transformer-core): replace pipelineResolvers directory for resolvers

* test(amplify-provider-awscloudformation): update tests to match use of resolver dir

* feat(amplify-provider-awscloudformation): use disable flags when loading user generated transformers
  • Loading branch information
danielleadams authored Nov 9, 2021
1 parent df4408c commit a0295aa
Show file tree
Hide file tree
Showing 26 changed files with 241 additions and 259 deletions.
28 changes: 5 additions & 23 deletions packages/amplify-e2e-tests/src/__tests__/resolvers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('user created resolvers', () => {
it('adds the overwritten resolver to the build', async () => {
const resolverName = 'Query.listTodos.req.vtl';
const resolver = '$util.unauthorized()';
const generatedResolverPath = join(projectDir, 'amplify', 'backend', 'api', apiName, 'build', 'pipelineFunctions', resolverName);
const generatedResolverPath = join(projectDir, 'amplify', 'backend', 'api', apiName, 'build', 'resolvers', resolverName);

await addApiWithoutSchema(projectDir, { apiName });
await updateApiSchema(projectDir, apiName, 'simple_model.graphql');
Expand All @@ -55,26 +55,8 @@ describe('user created resolvers', () => {
const resolverReq = '$util.unauthorized()';
const resolverRes = '$util.toJson({})';

const generatedReqResolverPath = join(
projectDir,
'amplify',
'backend',
'api',
apiName,
'build',
'pipelineFunctions',
resolverReqName,
);
const generatedResResolverPath = join(
projectDir,
'amplify',
'backend',
'api',
apiName,
'build',
'pipelineFunctions',
resolverResName,
);
const generatedReqResolverPath = join(projectDir, 'amplify', 'backend', 'api', apiName, 'build', 'resolvers', resolverReqName);
const generatedResResolverPath = join(projectDir, 'amplify', 'backend', 'api', apiName, 'build', 'resolvers', resolverResName);
const stackPath = join(projectDir, 'amplify', 'backend', 'api', apiName, 'build', 'stacks', 'CustomResources.json');

const Resources = {
Expand All @@ -90,7 +72,7 @@ describe('user created resolvers', () => {
FieldName: 'commentsForTodo',
RequestMappingTemplateS3Location: {
'Fn::Sub': [
's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/Query.commentsForTodo.req.vtl',
's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.commentsForTodo.req.vtl',
{
S3DeploymentBucket: {
Ref: 'S3DeploymentBucket',
Expand All @@ -103,7 +85,7 @@ describe('user created resolvers', () => {
},
ResponseMappingTemplateS3Location: {
'Fn::Sub': [
's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/Query.commentsForTodo.res.vtl',
's3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.commentsForTodo.res.vtl',
{
S3DeploymentBucket: {
Ref: 'S3DeploymentBucket',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,6 @@ test('Test simple model with AdminUI enabled should add IAM policy only for fiel
]);
// should throw unauthorized if it's not signed by the admin ui iam role
['Mutation.createPost.auth.1.req.vtl', 'Mutation.updatePost.auth.1.res.vtl', 'Mutation.deletePost.auth.1.res.vtl'].forEach(r => {
expect(out.pipelineFunctions[r]).toMatchSnapshot();
expect(out.resolvers[r]).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test('test single auth model is enabled with conflict resolution', () => {
expect(out.schema).toContain(
`syncPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String, lastSync: AWSTimestamp): ModelPostConnection`,
);
expect(out.pipelineFunctions['Query.syncPosts.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Query.syncPosts.auth.1.req.vtl']).toMatchSnapshot();
});

test('test multi auth model with conflict resolution', () => {
Expand Down Expand Up @@ -66,5 +66,5 @@ test('test multi auth model with conflict resolution', () => {
expect(out.schema).toContain(
`syncPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String, lastSync: AWSTimestamp): ModelPostConnection @aws_iam @aws_cognito_user_pools`,
);
expect(out.pipelineFunctions['Query.syncPosts.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Query.syncPosts.auth.1.req.vtl']).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ test('subscriptions are only generated if the respective mutation operation exis
expect(out.rootStack.Resources[ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual(
'AMAZON_COGNITO_USER_POOLS',
);
expect(out.pipelineFunctions['Salary.secret.res.vtl']).toContain('#if( $operation == "Mutation" )');
expect(out.resolvers['Salary.secret.res.vtl']).toContain('#if( $operation == "Mutation" )');

expect(out.pipelineFunctions['Mutation.createSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
expect(out.pipelineFunctions['Mutation.updateSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
expect(out.pipelineFunctions['Mutation.deleteSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
expect(out.resolvers['Mutation.createSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
expect(out.resolvers['Mutation.updateSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
expect(out.resolvers['Mutation.deleteSalary.res.vtl']).toContain('$util.qr($ctx.result.put("__operation", "Mutation"))');
});

test('per-field @auth without @model', () => {
Expand All @@ -61,7 +61,7 @@ test('per-field @auth without @model', () => {
const resources = out.rootStack.Resources;
const authPolicyIdx = Object.keys(out.rootStack.Resources).find(r => r.includes('AuthRolePolicy'));
expect(resources[authPolicyIdx]).toMatchSnapshot();
expect(out.pipelineFunctions['Query.listContext.req.vtl']).toContain(
expect(out.resolvers['Query.listContext.req.vtl']).toContain(
'#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"Allowed"}] )',
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,12 @@ describe('schema generation directive tests', () => {
// Check that resolvers containing the authMode check block
const authStepSnippet = '## [Start] Authorization Steps. **';

expect(out.pipelineFunctions['Query.getPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.pipelineFunctions['Query.listPosts.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.pipelineFunctions['Mutation.updatePost.auth.1.res.vtl']).toContain(authStepSnippet);
expect(out.pipelineFunctions['Mutation.deletePost.auth.1.res.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Query.getPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Query.listPosts.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Mutation.createPost.auth.1.req.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Mutation.updatePost.auth.1.res.vtl']).toContain(authStepSnippet);
expect(out.resolvers['Mutation.deletePost.auth.1.res.vtl']).toContain(authStepSnippet);
});

test(`Operation fields are getting the directive added, when type has the @auth only for allowed operations`, () => {
Expand Down Expand Up @@ -419,10 +419,10 @@ describe('schema generation directive tests', () => {
// Check that resolvers containing the authMode check block
const authModeCheckSnippet = '## [Start] Field Authorization Steps. **';
// resolvers to check is all other resolvers other than protected
expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.id.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.title.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet);
});

test(`'groups' @auth at field level is propagated to type and the type related operations`, () => {
Expand All @@ -446,10 +446,10 @@ describe('schema generation directive tests', () => {
const authModeCheckSnippet = '## [Start] Field Authorization Steps. **';

// resolvers to check is all other resolvers other than protected
expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.id.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.title.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.createdAt.req.vtl']).toContain(authModeCheckSnippet);
expect(out.resolvers['Post.updatedAt.req.vtl']).toContain(authModeCheckSnippet);
});

test(`'groups' @auth at field level is propagated to type and the type related operations, also default provider for read`, () => {
Expand All @@ -472,10 +472,10 @@ describe('schema generation directive tests', () => {
const groupCheckSnippet = '#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"admin"}] )';

// resolvers to check is all other resolvers other than protected by the group rule
expect(out.pipelineFunctions['Post.id.req.vtl']).toContain(groupCheckSnippet);
expect(out.pipelineFunctions['Post.title.req.vtl']).toContain(groupCheckSnippet);
expect(out.pipelineFunctions['Post.createdAt.req.vtl']).toContain(groupCheckSnippet);
expect(out.pipelineFunctions['Post.updatedAt.req.vtl']).toContain(groupCheckSnippet);
expect(out.resolvers['Post.id.req.vtl']).toContain(groupCheckSnippet);
expect(out.resolvers['Post.title.req.vtl']).toContain(groupCheckSnippet);
expect(out.resolvers['Post.createdAt.req.vtl']).toContain(groupCheckSnippet);
expect(out.resolvers['Post.updatedAt.req.vtl']).toContain(groupCheckSnippet);
});

test(`Nested types without @model not getting directives applied for iam, and no policy is generated`, () => {
Expand Down Expand Up @@ -618,11 +618,11 @@ describe('iam checks', () => {
});
const out = transformer.transform(schema);
expect(out).toBeDefined();
const createResolver = out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl'];
const createResolver = out.resolvers['Mutation.createPost.auth.1.req.vtl'];
expect(createResolver).toContain(
`#if( ($ctx.identity.userArn == $ctx.stash.authRole) || ($ctx.identity.cognitoIdentityPoolId == \"${identityPoolId}\" && $ctx.identity.cognitoIdentityAuthType == \"authenticated\") )`,
);
const queryResolver = out.pipelineFunctions['Query.listPosts.auth.1.req.vtl'];
const queryResolver = out.resolvers['Query.listPosts.auth.1.req.vtl'];
expect(queryResolver).toContain(
`#if( ($ctx.identity.userArn == $ctx.stash.authRole) || ($ctx.identity.cognitoIdentityPoolId == \"${identityPoolId}\" && $ctx.identity.cognitoIdentityAuthType == \"authenticated\") )`,
);
Expand All @@ -636,9 +636,9 @@ describe('iam checks', () => {
});
const out = transformer.transform(schema);
expect(out).toBeDefined();
const createResolver = out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl'];
const createResolver = out.resolvers['Mutation.createPost.auth.1.req.vtl'];
expect(createResolver).toContain(`#if( $ctx.identity.userArn == $ctx.stash.unauthRole )`);
const queryResolver = out.pipelineFunctions['Query.listPosts.auth.1.req.vtl'];
const queryResolver = out.resolvers['Query.listPosts.auth.1.req.vtl'];
expect(queryResolver).toContain(`#if( $ctx.identity.userArn == $ctx.stash.unauthRole )`);
});

Expand All @@ -650,7 +650,7 @@ describe('iam checks', () => {
});
const out = transformer.transform(schema);
expect(out).toBeDefined();
const createResolver = out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl'];
const createResolver = out.resolvers['Mutation.createPost.auth.1.req.vtl'];
expect(createResolver).toContain(`#set( $adminRoles = [\"helloWorldFunction\",\"echoMessageFunction\"] )`);
expect(createResolver).toMatchSnapshot();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ test('ownerfield where the field is a list', () => {
expect(out.rootStack.Resources[ResourceConstants.RESOURCES.GraphQLAPILogicalID].Properties.AuthenticationType).toEqual(
'AMAZON_COGNITO_USER_POOLS',
);
expect(out.pipelineFunctions['Mutation.createPost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.pipelineFunctions['Mutation.updatePost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.pipelineFunctions['Mutation.deletePost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.pipelineFunctions['Query.getPost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.pipelineFunctions['Query.listPosts.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Mutation.createPost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Mutation.updatePost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Mutation.deletePost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Query.getPost.auth.1.req.vtl']).toMatchSnapshot();
expect(out.resolvers['Query.listPosts.auth.1.req.vtl']).toMatchSnapshot();
});

test('ownerfield with subscriptions', () => {
Expand Down Expand Up @@ -90,13 +90,13 @@ test('ownerfield with subscriptions', () => {
expect(out.schema).toContain('onDeletePost(postOwner: String)');

// expect logic in the resolvers to check for postOwner args as an allowed owner
expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )',
);
expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )',
);
expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.postOwner, null) )',
);
});
Expand Down Expand Up @@ -134,24 +134,24 @@ test('multiple owner rules with subscriptions', () => {
expect(out.schema).toContain('onDeletePost(owner: String, editor: String)');

// expect logic in the resolvers to check for owner args as an allowedOwner
expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )',
);
expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )',
);
expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity0 = $util.defaultIfNull($ctx.args.owner, null) )',
);

// expect logic in the resolvers to check for editor args as an allowedOwner
expect(out.pipelineFunctions['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onCreatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )',
);
expect(out.pipelineFunctions['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onUpdatePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )',
);
expect(out.pipelineFunctions['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
expect(out.resolvers['Subscription.onDeletePost.auth.1.req.vtl']).toContain(
'#set( $ownerEntity1 = $util.defaultIfNull($ctx.args.editor, null) )',
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ test('auth logic is enabled on owner/static rules in es request', () => {
const out = transformer.transform(validSchema);
// expect response resolver to contain auth logic for owner rule
expect(out).toBeDefined();
expect(out.pipelineFunctions['Query.searchComments.auth.1.req.vtl']).toContain(
expect(out.resolvers['Query.searchComments.auth.1.req.vtl']).toContain(
'"terms": [$util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____"))],',
);
// expect response resolver to contain auth logic for group rule
expect(out.pipelineFunctions['Query.searchComments.auth.1.req.vtl']).toContain(
expect(out.resolvers['Query.searchComments.auth.1.req.vtl']).toContain(
'#set( $staticGroupRoles = [{"claim":"cognito:groups","entity":"writer"}] )',
);
});
Expand Down Expand Up @@ -104,7 +104,7 @@ test('auth logic is enabled for iam/apiKey auth rules', () => {
}
// expect the searchbable types to have the auth directives for total providers
// expect the allowed fields for agg to exclude secret
expect(out.pipelineFunctions['Query.searchPosts.auth.1.req.vtl']).toContain(
expect(out.resolvers['Query.searchPosts.auth.1.req.vtl']).toContain(
`#set( $allowedAggFields = ["createdAt","updatedAt","id","content"] )`,
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,10 @@ test('it generates the expected resources', () => {
FunctionVersion: '2018-05-29',
Name: 'InvokeEchofunctionLambdaDataSource',
RequestMappingTemplateS3Location: {
'Fn::Join': [
'',
['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/pipelineFunctions/InvokeEchofunctionLambdaDataSource.req.vtl'],
],
'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/resolvers/InvokeEchofunctionLambdaDataSource.req.vtl']],
},
ResponseMappingTemplateS3Location: {
'Fn::Join': [
'',
['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/pipelineFunctions/InvokeEchofunctionLambdaDataSource.res.vtl'],
],
'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/resolvers/InvokeEchofunctionLambdaDataSource.res.vtl']],
},
}),
);
Expand All @@ -118,11 +112,11 @@ test('it generates the expected resources', () => {
},
RequestMappingTemplate: anything(),
ResponseMappingTemplateS3Location: {
'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/pipelineFunctions/Query.echo.res.vtl']],
'Fn::Join': ['', ['s3://', { Ref: anything() }, '/', { Ref: anything() }, '/resolvers/Query.echo.res.vtl']],
},
}),
);
expect(out.pipelineFunctions).toMatchSnapshot();
expect(out.resolvers).toMatchSnapshot();
});

test('two @function directives for the same lambda should produce a single datasource, single role and two resolvers', () => {
Expand Down
Loading

0 comments on commit a0295aa

Please sign in to comment.