-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to override Data Resolver functions in AmplifyGraphqlApi ? #2467
Comments
const graphApi = new AmplifyGraphqlApi(stack, "GraphqlApi", {
functionSlots: [...getCreateSlots],
definition: graphQLDefinition,
apiName: "GraphqlApi",
translationBehavior: {},
transformerPlugins: [],
authorizationModes: {
apiKeyConfig: {
expires: Duration.days(365),
description: "API Key for GraphQL API - " + stack.stage,
},
defaultAuthorizationMode: "OPENID_CONNECT",
oidcConfig: {
oidcIssuerUrl: process.env.CLERK_ISSUER_BASE_URL!,
oidcProviderName: "Clerk",
tokenExpiryFromAuth: Duration.millis(0),
tokenExpiryFromIssue: Duration.millis(0),
},
},
});
const MutationUpdateQuestionDataResolverFn = new Asset(
stack,
"ModifiedResolvers",
{
path: path.join("stacks/resolvers/Mutation.updateQuestion.req.vtl"),
}
);
graphApi.resources.cfnResources.cfnFunctionConfigurations[
"MutationUpdateQuestionDataResolverFn"
].requestMappingTemplateS3Location =
MutationUpdateQuestionDataResolverFn.s3ObjectUrl; This is what I am using but it requires management overhead. It would be great if we can use the same slot behavior to override the data resolvers too. |
Hi @harshit9715 - can you provide more context on the type of updates you'd like to override? |
Sure! I am trying to implement atomic counters for certain fields. type Question @model {
id: ID!
content: String!
upvoteCount: Int! @default("0")
downvoteCount: Int! @default("0")
viewCount: Int! @default("0")
} Now the vtl template does not support atomic counters out of the box but then I noticed the generated code is very close to what I need. Basically, if there is a fieldName ending with Count, i am overriding its behaviour on update mutation. Instead of using TLDR;The changed lines from this #if( $util.isNull($entry.value) )
#set( $discard = $expRemove.add("#$entryKeyAttributeName") )
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
#else
$util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
$util.qr($expValues.put(":$entryKeyAttributeName", $util.dynamodb.toDynamoDB($entry.value)))
#end To this #if( $util.isNull($entry.value) )
#set( $discard = $expRemove.add("#$entryKeyAttributeName") )
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
#else
#if ( $entry.key.endsWith("Count"))
#if ( $entry.value != 1 && $entry.value != -1) ## this condition makes sure that if the client sent anything other than +1 or -1, then no change on the value will occur.
#set( $entry.value = 0 )
#end
$util.qr($expAdd.put("#$entryKeyAttributeName", ":$entryKeyAttributeName")) ## NOTICE THE expAdd USED ON THIS LINE.
#else
$util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
#end
## $util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
$util.qr($expValues.put(":$entryKeyAttributeName", $util.dynamodb.toDynamoDB($entry.value)))
#end Full codeHere is what was generated for Mutation.updateQuestion.req.vtl ## [Start] Mutation Update resolver. **
#set( $args = $util.defaultIfNull($ctx.stash.transformedArgs, $ctx.args) )
## Set the default values to put request **
#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) )
## copy the values from input **
$util.qr($mergedValues.putAll($util.defaultIfNull($args.input, {})))
## set the typename **
## Initialize the vars for creating ddb expression **
#set( $expNames = {} )
#set( $expValues = {} )
#set( $expSet = {} )
#set( $expAdd = {} )
#set( $expRemove = [] )
#if( $ctx.stash.metadata.modelObjectKey )
#set( $Key = $ctx.stash.metadata.modelObjectKey )
#else
#set( $Key = {
"id": $util.dynamodb.toDynamoDB($args.input.id)
} )
#end
## Model key **
#if( $ctx.stash.metadata.modelObjectKey )
#set( $keyFields = [] )
#foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() )
$util.qr($keyFields.add("$entry.key"))
#end
#else
#set( $keyFields = ["id"] )
#end
#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() )
#if( !$util.isNull($ctx.stash.metadata.dynamodbNameOverrideMap) && $ctx.stash.metadata.dynamodbNameOverrideMap.containsKey("$entry.key") )
#set( $entryKeyAttributeName = $ctx.stash.metadata.dynamodbNameOverrideMap.get("$entry.key") )
#else
#set( $entryKeyAttributeName = $entry.key )
#end
#if( $util.isNull($entry.value) )
#set( $discard = $expRemove.add("#$entryKeyAttributeName") )
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
#else
$util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
$util.qr($expValues.put(":$entryKeyAttributeName", $util.dynamodb.toDynamoDB($entry.value)))
#end
#end
#set( $expression = "" )
#if( !$expSet.isEmpty() )
#set( $expression = "SET" )
#foreach( $entry in $expSet.entrySet() )
#set( $expression = "$expression $entry.key = $entry.value" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#if( !$expAdd.isEmpty() )
#set( $expression = "$expression ADD" )
#foreach( $entry in $expAdd.entrySet() )
#set( $expression = "$expression $entry.key $entry.value" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#if( !$expRemove.isEmpty() )
#set( $expression = "$expression REMOVE" )
#foreach( $entry in $expRemove )
#set( $expression = "$expression $entry" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#set( $update = {} )
$util.qr($update.put("expression", "$expression"))
#if( !$expNames.isEmpty() )
$util.qr($update.put("expressionNames", $expNames))
#end
#if( !$expValues.isEmpty() )
$util.qr($update.put("expressionValues", $expValues))
#end
## Begin - key condition **
#if( $ctx.stash.metadata.modelObjectKey )
#set( $keyConditionExpr = {} )
#set( $keyConditionExprNames = {} )
#foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() )
$util.qr($keyConditionExpr.put("keyCondition$velocityCount", {
"attributeExists": true
}))
$util.qr($keyConditionExprNames.put("#keyCondition$velocityCount", "$entry.key"))
#end
$util.qr($ctx.stash.conditions.add($keyConditionExpr))
#else
$util.qr($ctx.stash.conditions.add({
"id": {
"attributeExists": true
}
}))
#end
## End - key condition **
#if( $args.condition )
$util.qr($ctx.stash.conditions.add($args.condition))
#end
## Start condition block **
#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 )
#set( $mergedConditions = {
"and": $ctx.stash.conditions
} )
#set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) )
#if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 )
#set( $Conditions = {
"expression": $Conditions.expression,
"expressionNames": $Conditions.expressionNames
} )
#end
## End condition block **
#end
#set( $UpdateItem = {
"version": "2018-05-29",
"operation": "UpdateItem",
"key": $Key,
"update": $update
} )
#if( $Conditions )
#if( $keyConditionExprNames )
$util.qr($Conditions.expressionNames.putAll($keyConditionExprNames))
#end
$util.qr($UpdateItem.put("condition", $Conditions))
#end
$util.toJson($UpdateItem)
## [End] Mutation Update resolver. ** and here is what I override the template with. ## [Start] Mutation Update resolver. **
#set( $args = $util.defaultIfNull($ctx.stash.transformedArgs, $ctx.args) )
## Set the default values to put request **
#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) )
## copy the values from input **
$util.qr($mergedValues.putAll($util.defaultIfNull($args.input, {})))
## set the typename **
## Initialize the vars for creating ddb expression **
#set( $expNames = {} )
#set( $expValues = {} )
#set( $expSet = {} )
#set( $expAdd = {} )
#set( $expRemove = [] )
#if( $ctx.stash.metadata.modelObjectKey )
#set( $Key = $ctx.stash.metadata.modelObjectKey )
#else
#set( $Key = {
"id": $util.dynamodb.toDynamoDB($args.input.id)
} )
#end
## Model key **
#if( $ctx.stash.metadata.modelObjectKey )
#set( $keyFields = [] )
#foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() )
$util.qr($keyFields.add("$entry.key"))
#end
#else
#set( $keyFields = ["id"] )
#end
#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() )
#if( !$util.isNull($ctx.stash.metadata.dynamodbNameOverrideMap) && $ctx.stash.metadata.dynamodbNameOverrideMap.containsKey("$entry.key") )
#set( $entryKeyAttributeName = $ctx.stash.metadata.dynamodbNameOverrideMap.get("$entry.key") )
#else
#set( $entryKeyAttributeName = $entry.key )
#end
#if( $util.isNull($entry.value) )
#set( $discard = $expRemove.add("#$entryKeyAttributeName") )
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
#else
#if ( $entry.key.endsWith("Count"))
#if ( $entry.value != 1 && $entry.value != -1)
#set( $entry.value = 0 )
#end
$util.qr($expAdd.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
#else
$util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
#end
## $util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
$util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
$util.qr($expValues.put(":$entryKeyAttributeName", $util.dynamodb.toDynamoDB($entry.value)))
#end
#end
#set( $expression = "" )
#if( !$expSet.isEmpty() )
#set( $expression = "SET" )
#foreach( $entry in $expSet.entrySet() )
#set( $expression = "$expression $entry.key = $entry.value" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#if( !$expAdd.isEmpty() )
#set( $expression = "$expression ADD" )
#foreach( $entry in $expAdd.entrySet() )
#set( $expression = "$expression $entry.key $entry.value" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#if( !$expRemove.isEmpty() )
#set( $expression = "$expression REMOVE" )
#foreach( $entry in $expRemove )
#set( $expression = "$expression $entry" )
#if( $foreach.hasNext() )
#set( $expression = "$expression," )
#end
#end
#end
#set( $update = {} )
$util.qr($update.put("expression", "$expression"))
#if( !$expNames.isEmpty() )
$util.qr($update.put("expressionNames", $expNames))
#end
#if( !$expValues.isEmpty() )
$util.qr($update.put("expressionValues", $expValues))
#end
## Begin - key condition **
#if( $ctx.stash.metadata.modelObjectKey )
#set( $keyConditionExpr = {} )
#set( $keyConditionExprNames = {} )
#foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() )
$util.qr($keyConditionExpr.put("keyCondition$velocityCount", {
"attributeExists": true
}))
$util.qr($keyConditionExprNames.put("#keyCondition$velocityCount", "$entry.key"))
#end
$util.qr($ctx.stash.conditions.add($keyConditionExpr))
#else
$util.qr($ctx.stash.conditions.add({
"id": {
"attributeExists": true
}
}))
#end
## End - key condition **
#if( $args.condition )
$util.qr($ctx.stash.conditions.add($args.condition))
#end
## Start condition block **
#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 )
#set( $mergedConditions = {
"and": $ctx.stash.conditions
} )
#set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) )
#if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 )
#set( $Conditions = {
"expression": $Conditions.expression,
"expressionNames": $Conditions.expressionNames
} )
#end
## End condition block **
#end
#set( $UpdateItem = {
"version": "2018-05-29",
"operation": "UpdateItem",
"key": $Key,
"update": $update
} )
#if( $Conditions )
#if( $keyConditionExprNames )
$util.qr($Conditions.expressionNames.putAll($keyConditionExprNames))
#end
$util.qr($UpdateItem.put("condition", $Conditions))
#end
$util.toJson($UpdateItem)
## [End] Mutation Update resolver. ** |
Here is a quick video. Screen.Recording.2024-04-17.at.7.01.39.PM.mov |
And I realised that the construct can use the local template if it is available. Here is the updated code. const resolverConfig = graphApi.resources.cfnResources.cfnFunctionConfigurations[functionName];
if (!resolverConfig) return;
resolverConfig.requestMappingTemplateS3Location = undefined;
resolverConfig.requestMappingTemplate = fs.readFileSync(
path.join("stacks/resolvers", filePath),
"utf8"
); |
Hi @harshit9715, this is intended workflow for overriding when using the construct directly. I'll leave this open as a feature request. |
Amplify CLI Version
12.11.0
Question
I am using the CDK construct for building the graphql schema.
I want to know if there is a way of overriding some of the auto-generated resolvers?
This option is available through amplify cli. we just need to place the edited resolvers in the amplify/backend/api//resolvers folder with the same name.
The text was updated successfully, but these errors were encountered: