Skip to content
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

Count field definition #11

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

Tedsterh
Copy link

I have added a new direct called fieldCount that uses an hasMany or manyToMany directive to access the other table instead, it also has a field argument like these connections so you can pass a field for it to return on, if none are provided it generates one similar to the hasOne directive.

I am not too sure how the resolvers work so not sure if it maps to the field properly or not just yet, any help and tips would be amazing!

Copy link
Owner

@multimeric multimeric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • In general, if you have copied code here from the amplify codebase, try to import it wherever possible, instead of copying it
  • Don't commit handler.zip
  • Please add some tests that assert the lambda behaviour and the CDK stack function correctly.

@@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "es2019", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why increase the target?

index.ts Outdated Show resolved Hide resolved
index.ts Outdated

export default class CountTransformer
extends TransformerPluginBase
implements TransformerPluginProvider
{
models: ObjectTypeDefinitionNode[];
fields: FieldCountDirectiveConfiguration[];

// directive @fieldCount(type: CountType!) on
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

index.ts Outdated Show resolved Hide resolved
index.ts Outdated

export default class CountTransformer
extends TransformerPluginBase
implements TransformerPluginProvider
{
models: ObjectTypeDefinitionNode[];
fields: FieldCountDirectiveConfiguration[];

// directive @fieldCount(type: CountType!) on
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.

// Allow the lambda to access this table
funcRole.addToPolicy(
new iam.PolicyStatement({
actions: ["dynamodb:Scan"],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Query

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, which bit in the policy needed to be query?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well unless I'm mistaken, we need the lambda to be able to query the index. We could scan, but that wouldn't be efficient. So add dynamodb:Query.

index.ts Outdated
qref(`$ctx.stash.put("fieldName", "${config.resolverFieldName}")`),
];

const authModes = [
Copy link
Owner

@multimeric multimeric Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you import this code from the AuthTransformer module? I assume it's copied from there.

@multimeric
Copy link
Owner

The zip file is still in the pull request.

@multimeric
Copy link
Owner

Also, I believe more changes will be needed in the lambda resolver, to handle an index. We will want to Query it, for one.

@multimeric
Copy link
Owner

Oh, I just realised what you're doing. You're not trying to add a count for secondary indexes like I thought (?). So the query stuff is not relevant.

@Tedsterh
Copy link
Author

Yes sorry, I'm just creating a count field for list connections, I thought I had remove the .zip so i'll go back and do that now.

@Tedsterh
Copy link
Author

Tedsterh commented Mar 21, 2022

I think we can get the name of the index generated by @hasMany so that we can scan the index rather than filter it, not sure which would be more efficient?

Or we could force an index field so that we don't have to try and find it ourselves, like they do on the @hasMany if you want to use your own index.

@multimeric
Copy link
Owner

Hmm I have to admit I've never actually used @hasMany. Can you show me an example schema for one? We should use the index if it generates one.

@Tedsterh
Copy link
Author

Tedsterh commented Mar 21, 2022

You can use it with a custom index or let it generate one, both do the same thing effectively I'm just not sure how we could easy access the generated one from our directive.

type Post @model {
  id: ID!
  title: String!
  comments: [Comment] @hasMany
}

type Comment @model {
  id: ID!
  content: String!
}

Or with a custom index:

type Post @model {
  id: ID!
  title: String!
  comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
}

type Comment @model {
  id: ID!
  postID: ID! @index(name: "byPost", sortKeyFields: ["content"])
  content: String!
}

We could then require them to pass the index name which in this case is byPost

@multimeric
Copy link
Owner

So at a high level, you want to be able to getPost(id: 35){ commentCount }, which will give you the number of associated comments?

For @hasMany, I think we will want to always use a Dynamo Query for these kinds of requests, since they are always associated with an index. It will certainly be much faster. A good way to check what kind of Dynamo call we should use it to look at the resolvers generated by Amplify for Post{ comments }. We will broadly want to replicate this, only inside the lambda, and using the COUNT attribute.

@Tedsterh
Copy link
Author

Tedsterh commented Mar 21, 2022

Yes that's what I'm thinking, the hope is because we are querying it directly too I can use a filter on it, to filter out certain comments from a total in this case.

I'll look at using an index then and checkout their resolvers for the query, I've not used dynamo queries before.

Should I make a new handler or just edit the current one to accept an optional index name?

@multimeric
Copy link
Owner

Definitely use the same handler. Most of the code will remain the same and we don't want to duplicate everything. You can pass the info like the index name in through a new key in the event (alongside context, tableName etc).

@multimeric
Copy link
Owner

Yeah so dynamo's Query can do everything Scan can do, including applying arbitrary filters. But there's the restriction that you can only Query one partition, not the entire table. But we can just copy what Amplify does, like I said.

@Tedsterh
Copy link
Author

This is how they do it for the posts one they have a postID passed into the query that they can access

#if( !$util.isNull($ctx.args. postID) )
  #set( $modelQueryExpression.expression = "#postID = : postID" )
  #set( $modelQueryExpression.expressionNames = {
  "#postID": "postID"
} )
  #set( $modelQueryExpression.expressionValues = {
  ": postID": {
      "S": "$ctx.args. postID"
  }
} )

do you know what's in the context, is there a way to access the primary key of the model that the field is in?

I have found out how to use the indexes we simply pass in the index name IndexName: event.indexName, and then it uses the expressionNames and expressionValues to do the actual filtering.

@multimeric
Copy link
Owner

Okay, so you'll have to write a resolver for the @hasMany fields that passes this info into the lambda. Check out the AppSync docs for resolvers, they're quite helpful: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html. But if there's anything you aren't able to do with them, we can push it to the lambda.

@Tedsterh
Copy link
Author

According to the docs we should be able to do something like ctx.source.id so we could either figure out the name of the primary key in the generate stuff or we could just have an argument passed into the directive again, I think the primary key would be better?

@multimeric
Copy link
Owner

multimeric commented Mar 22, 2022

What I would suggest is actually checking for the existence of the @hasMany directive, and if it's present, extracting out the index information from that, possibly using some functions from the relational transformer module. I don't think AppSync inherently knows what the primary key is for a table, and even if it did, it may not be the index the user selected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants