-
-
Notifications
You must be signed in to change notification settings - Fork 543
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
Add metadata (the graphql.core Node
) to GraphQLField
and GraphQLObject
.
#3182
base: main
Are you sure you want to change the base?
Conversation
Thanks for adding the Here's a preview of the changelog: Augment the codegen Here's the tweet text:
|
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #3182 +/- ##
=======================================
Coverage 96.40% 96.41%
=======================================
Files 498 498
Lines 31137 31220 +83
Branches 3815 3822 +7
=======================================
+ Hits 30017 30100 +83
+ Misses 914 912 -2
- Partials 206 208 +2 |
3ca2fe1
to
faa6754
Compare
CodSpeed Performance ReportMerging #3182 will not alter performanceComparing Summary
|
35577c3
to
590d320
Compare
strawberry/codegen/types.py
Outdated
@@ -32,6 +34,7 @@ class GraphQLField: | |||
alias: Optional[str] | |||
type: GraphQLType | |||
default_value: Optional[GraphQLArgumentValue] = None | |||
definition_node: Optional[Node] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think it would be possible to use the strawberry type here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do both...
The query is the part that is controlled by the client (they may not control the upstream strawberry type). I actually have a branch that I haven't quite gotten working that tries to build a client from only a graphql schema (then the Python client could be generated against things that don't even use strawberry as the graphql provider).
But... It would be nice to have access to the strawberry type where available. That's probably easier to work with than digging through the graphql AST...
f7631c6
to
eb211c8
Compare
pyproject.toml
Outdated
@@ -1,7 +1,7 @@ | |||
[tool.poetry] | |||
name = "strawberry-graphql" | |||
packages = [ { include = "strawberry" } ] | |||
version = "0.211.1" | |||
version = "0.211.1.dev1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
version = "0.211.1.dev1" | |
version = "0.211.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, that wasn't supposed to make it into the PR 😅
strawberry/codegen/query_codegen.py
Outdated
variable.name, | ||
None, | ||
variable_type, | ||
definition_node=variable_definition, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this have strawberry_field
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm ... originally, I didn't think so ... but now that I think about it a little more, possibly ...
This represents the input variables to the operation. I guess this is represented by the query/mutation operation object?
Let me see what I can cook up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for more context, I'd like to keep as abstracted as possible 😊 we might change the internal representation (ie moving away from GraphQL) in future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So ... the more I dig into this, the more that this feels weird (or at least really complex). For example, you could have a query like:
query ($skipTitle: Boolean!, $val: Int!) {
queryPost(val: $val) {
id
title @skip(if: $skipTitle)
text
}
}
In this case, the $skipTitle
variable field really has nothing to do with any of the types in the backend schema definition. The val
field presumably does, but it's buried in the Query.query_post
field's base_resolver (I guess you'd introspect the wrapped_func on that resolver to understand the backend metadata about this thing?)
For now, I think that I'll make the graphql_type
on the {type}Variables
point at the Query
or Mutation
class and then users can crawl that class however they want in their plugins. If we ever see more concrete use-cases, we can make better decisions on how we want to set these pieces of metadata to support those use-cases at that point.
Does that sound reasonable to you?
strawberry/codegen/types.py
Outdated
definition_node: Optional[Node] = None | ||
strawberry_type: Optional[StrawberryType] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about the naming here :) I think I'd make something like this:
definition_node: Optional[Node] = None | |
strawberry_type: Optional[StrawberryType] = None | |
graphql_node: Optional[Node] = None | |
graphql_type: Optional[StrawberryType] = None |
what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'm not picky about the naming here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's do the change then 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:face_palm: That's a little embarassing 😅
eb211c8
to
c0921ac
Compare
It looks like pyright released a new version yesterday -- I'm guessing that's why all the pyright tests are now failing :). womp womp. |
c0921ac
to
2295a61
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
almost ready to go :)
# (e.g. the ``Query`` or ``Mutation`` class and then we'll let consumers sort | ||
# out how they want to use this metadata). | ||
if variables_type.graphql_type is not None: | ||
raise ValueError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same q for the one above) when could this happen?
if it is just a defensive check, let's add a nocover comment 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is defensive to make sure we didn't mess something up.
These are the {Type}Variables
objects that I mentioned in the other comments. Basically, at the client level, it's just a dataclass that describes the inputs to a strawberry resolver (rather than being an actual type on the strawberry/python side).
In that case, I made the decision to just set the graphql node to the Operation
node and I set the graphql type to the query/mutation type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think that it's a little more complex ... It maps to the variables in the operation
query FooQuery($id: Int, $fetchBar: Boolean) {
foo(id: $id) {
bar @skip(if: $fetchBar) {
x
y
}
}
}
The client is going to generate an object:
class FooQueryVariables:
id: int
fetch_bar: bool
This object doesn't map to any class/field on the strawberry/server side.
variable.name, | ||
None, | ||
variable_type, | ||
graphql_node=variable_definition, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use named arguments here?
and could we find where the variable is used and attach that field here? Might not probably worth, but maybe we can leave a comment about that? (The current comment is a bit unclear to me)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to update the comment to make it more clear. These are for the {Type}Variables
objects which generally correspond to something like:
class Query:
@strawberry.field
def resolver_function(self, id: int, x: float) -> ReturnType:
...
The client is going to make a:
class QueryResolverFunctionVariables:
id: int
x: float
type of class (depending on the query generated). But in this case, there isn't a StrawberryField
that maps 1-to-1 with the id
parameter.
strawberry/codegen/types.py
Outdated
definition_node: Optional[Node] = None | ||
strawberry_type: Optional[StrawberryType] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's do the change then 😊
tests/codegen/conftest.py
Outdated
@@ -120,6 +130,14 @@ def get_person_or_animal(self) -> Union[Person, Animal]: | |||
p_or_a.age = 7 | |||
return p_or_a | |||
|
|||
@strawberry.field | |||
def get_person_with_inputs(self, name: str, age: int = 72) -> Person: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def get_person_with_inputs(self, name: str, age: int = 72) -> Person: | |
def get_person_with_inputs(self, name: str, age: int = 72) -> Person: # pragma: no cover |
7aab7e1
to
50dd99c
Compare
75a1c7d
to
06d3afb
Compare
… this metadata do specialize the creation (e.g. adding a `@defaultValue` directive to add a default value to the generated objects).
796bd90
to
85bb731
Compare
@patrick91 -- I believe that this is ready for another look when you have a few moments. Thanks! |
Clients can use this metadata do specializilation during the creation of client types.
Description
GraphQL is a very flexible language where custom directives can be added for a large number of purposes. It's possible that in some cases, a custom directive on a query object or field could be used to impact what types are emitted on a client. e.g. a client might want to make fields nullable if they have a
@skip
or@include
directive. Since directives can be as diverse as the people who make them, we should allow that information to be passed to the client generator plugin which can then be used or ignored as necessary.Since it's unclear what these custom directives would do or how someone might use them in the general case, for now, I'm just passing the whole graphql node for the field or type through. In my concrete use-case, I have reasons to want to supply default values to the generated types from the client -- e.g.:
Rather than
which is what gets generated by my plugin currently.
My graphql would look something like:
And this is fine as long as the server is taught to ignore the directive (or the directive is stripped before the query actually gets issued which is how my plugin actually works).
Types of Changes
Issues Fixed or Closed by This PR
Checklist