aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
89 stars 79 forks source link

Unable to create custom vtl req|res for mutation/query #1749

Open hackrx opened 2 years ago

hackrx commented 2 years ago

Before opening, please confirm:

How did you install the Amplify CLI?

npm

If applicable, what version of Node.js are you using?

No response

Amplify CLI Version

7.6.2

What operating system are you using?

Mac

Amplify Categories

api

Amplify Commands

push

Describe the bug

I migrated to V2, and my all custom resolvers broke, I tried different ways for deployment, but the doc is unclear about how to write custom resolvers for custom query/mutation. I followed this guide, and pasted the code in cdk-stack.ts, but it is failing with this error:


CREATE_FAILED customresolver                                                             AWS::AppSync::Resolver     Sat Dec 11 2021 03:44:06 GMT+0530 (India Standard Time) GraphQL API apiblogbackendGraphQLAPIIdOutput not found. (Service: AWSAppSync; Status Code: 404; Error Code: NotFoundException; Request ID: 833d526c-d0dc-436f-be31-d70287b8b9e7; Proxy: null)
CREATE_FAILED amplify-blogbackend-testapis-162852-customcheckUserName-1D711132QG812 AWS::CloudFormation::Stack Sat Dec 11 2021 03:44:07 GMT+0530 (India Standard Time) The following resource(s) failed to create: [customresolver]. 

cdk-stack.ts

import * as cdk from '@aws-cdk/core';
import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper';
import * as appsync from '@aws-cdk/aws-appsync';
import { AmplifyDependentResourcesAttributes } from '../../types/amplify-dependent-resources-ref';

const requestVTL =`
## direct run get query for userName: 
{
  "version": "2018-05-29",
  "operation": "GetItem",
  "key":  {
  "id": $util.dynamodb.toDynamoDBJson($ctx.args.userName)
}
}
`
const responseVTL =`
#if( $ctx.error )
    $util.error($ctx.error.message, $ctx.error.type)
    #elseif($util.isNull($ctx.result) )
    #set($exists = false)
#else
    #set($exists = true)
#end
$util.toJson({
"doesExist" : $exists
})
`

export class cdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps, amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps) {
    super(scope, id, props);
    /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
    new cdk.CfnParameter(this, 'env', {
      type: 'String',
      description: 'Current Amplify CLI env name',
    });

    // Access other Amplify Resources 
    const retVal:AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(this, 
      amplifyResourceProps.category, 
      amplifyResourceProps.resourceName, 
      [{
        category: "api",
        resourceName: "blogbackend"
      }]
    );

    const resolver = new appsync.CfnResolver(this, "custom-resolver", {
      apiId: retVal.api.blogbackend.GraphQLAPIIdOutput,
      fieldName: "checkUserName", 
      typeName: "Query", // Query | Mutation | Subscription
      requestMappingTemplate: requestVTL,
      responseMappingTemplate: responseVTL,
      dataSourceName: "UserNameTable" // DataSource name
    })
  }
}

Expected behavior

Push should work, and the req & res, should be mapped with the query in appsync console. I have more questions, like what if I want pipeline resolvers for a query/mutation, how can I acheive this with this, how can I mention first req+res & second template then other (in short, how can I achieve template channing)?

Reproduction steps

  1. Follow the guide, and create a custom query with custom resolver.

GraphQL schema(s)

```graphql # Put schemas below this line type UserName @model(subscriptions: { level: off }) @aws_cognito_user_pools { id: String! @aws_cognito_user_pools @aws_iam doesExist: Boolean @aws_cognito_user_pools @aws_iam userID: String! @aws_iam # restricting to only iam admin access. } type Query { checkUserName(userName: String!): UserName @aws_cognito_user_pools @aws_iam } ```

Log output

``` # Put your logs below this line ```

Additional information

No response

aryaman-titan commented 2 years ago

Any updates on this issue? @akshbhu @josefaidt I'm having similar issues regarding custom VTL

hackrx commented 2 years ago

Hello @akshbhu, it's almost 7 days, my team is not able to work on the custom resolvers, can you please review this issue?

josefaidt commented 2 years ago

Hey @hackrx @aryaman-titan :wave: apologies for the delay! This is something we are actively investigating

hackrx commented 2 years ago

Hey @josefaidt any updates on this, please? deadlines are near, we are not able to write custom pipeline resolvers 😢 .

samjett247 commented 2 years ago

Hey @josefaidt any updates on this, please? deadlines are near, we are not able to write custom pipeline resolvers 😢 .

Followed the same steps and seeing the same issues with CLI version 7.6.5

akshbhu commented 2 years ago

Hi All

There is been a bug in docs I guess. When accessing Cfn parameters, we need to access via Ref. I have marked this line after comment section.

For below cdk code

export class cdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps, amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps) {
    super(scope, id, props);
    /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
    new cdk.CfnParameter(this, 'env', {
      type: 'String',
      description: 'Current Amplify CLI env name',
    });

    // Access other Amplify Resources 
    const retVal:AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(this, 
      amplifyResourceProps.category, 
      amplifyResourceProps.resourceName, 
      [{
        category: "api",
        resourceName: "blogbackend"
      }]
    );

    const resolver = new appsync.CfnResolver(this, "custom-resolver", {
// Here you have access parameter via Ref since its an cdk parameter passed from root stack. Previously the ApiId is the variable Name which is wrong , it should be variable value as below

      apiId: cdk.Fn.ref(retVal.api.blogbackend.GraphQLAPIIdOutput),
      fieldName: "checkUserName", 
      typeName: "Query", // Query | Mutation | Subscription
      requestMappingTemplate: requestVTL,
      responseMappingTemplate: responseVTL,
      dataSourceName: "UserNameTable" // DataSource name
    })
  }
}
akshbhu commented 2 years ago

Marking this as bug in the docs to access CFN parameters in custom cdk code. https://docs.amplify.aws/cli/graphql/custom-business-logic/#vtl-resolver

samjett247 commented 2 years ago

This is still not working for me. Now I get the following errors on build. It may be my specific resolver though - I will try to get some more visibility.

CREATE_FAILED      submitApqptask AWS::AppSync::Resolver Tue Jan 04 2022 16:35:12 GMT-0800 (Pacific Standard Time) Only one resolver is allowed per field. (Service: AWSAppSync; Status Code: 400; Error Code: BadRequestException; Request ID: 56fb4a81-341a-401a-b01e-21dec4ebd64b; Proxy: null)
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_FAILED amplify-inappsbasecampapqpbe-dev-160458-customMyCustomResolvers-1MSSHOHXXEAV8 AWS::CloudFormation::Stack Tue Jan 04 2022 16:35:12 GMT-0800 (Pacific Standard Time) The following resource(s) failed to create: [submitApqptask]. 
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_FAILED               customMyCustomResolvers                 AWS::CloudFormation::Stack Tue Jan 04 2022 16:35:16 GMT-0800 (Pacific Standard Time) Embedded stack arn:aws:cloudformation:us-east-2:780291850317:stack/amplify-inappsbasecampapqpbe-dev-160458-customMyCustomResolvers-1MSSHOHXXEAV8/54367940-6dbf-11ec-a5bc-06b8ec01a01c was not successfully created: The following resource(s) failed to create: [submitApqptask]. 

It seems the main issue is: Only one resolver is allowed per field.. Any advice @akshbhu ?

samjett247 commented 2 years ago

I was trying to write a custom resolver for a mutation field that I had already defined the @auth() directive for. So I think it tried to create my custom resolver on top of the authorization resolver that was already created, and no luck there.

I removed the auth directive and implemented the auth logic in the custom resolver and good to go now.

However, I didn't realize that appsync would generate a resolver for my custom mutation if I just added an auth directive... Now I wonder why I even need to create a custom resolver? Why not just Add an @auth directive to my custom mutation, then overwrite the generated .res.vtl and .req.vtl files?

austinrausch commented 2 years ago
apiId: cdk.Fn.ref(retVal.api.blogbackend.GraphQLAPIIdOutput),

@akshbhu This worked for me. My custom resolver was created after updating this line and running 'amplify push'. Thanks!

Next, is there any documentation regarding deploying pipeline resolvers (and functions) using this same CDK method?

InnovateWithEric commented 2 years ago

The approach for doing custom business logic in resolvers with GQL v2 trans is described here https://docs.amplify.aws/cli/graphql/custom-business-logic/#override-amplify-generated-resolvers