-
-
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 a way to filter fields #3274
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #3274 +/- ##
==========================================
- Coverage 96.62% 96.59% -0.03%
==========================================
Files 485 486 +1
Lines 30292 30320 +28
Branches 3746 3749 +3
==========================================
+ Hits 29270 29288 +18
- Misses 833 839 +6
- Partials 189 193 +4 |
CodSpeed Performance ReportMerging #3274 will degrade performances by 21.72%Comparing Summary
Benchmarks breakdown
|
I like the idea. You might want to pass the parent type and the field name to the function as well to enable more advanced filtering. |
I like the idea of using the field's
I think a schema config for that doesn't exclude the possibility of having that Another option (not sure how good it is) for this specifically would be to have a |
Great idea! Defining filters / overrides I think is a great way to make Schema generation more customizable. See this comment here for a somewhat related aspect on when to apply FieldExtensions [1]. I like @bellini666's remark on using the field metadata, perhaps we could have multiple levels of filtering (Schema level, field level) where field level takes precedence over global filtering to allow overriding global rules on a per field basis. |
This is a great PoC on schema filtering. Ultimately, I believe the final filtering should not happen inside the schema generator itself, but rather in some extension, following @bellini666's hint on FieldExtensions. Dynamically building schemas from the same type source is not an approach I'd recommend, there should rather be a possibility to dynamically filter the schemas for your different consumers. Everything else introduces additional complexity to maintaining the system. However, I see the use case for this and believe we should enable schema filtering in some way or the other. |
How do you see that working?
I'm not sure about the difference about the two approaches? |
The key difference is that you are operating on the schema and the same API endpoint, but introspection and access to types and fields depends on your role during access of the schema. |
Right, but that's more difficult to implement right, no? We either need to create the schema dynamically per request (which could be expensive, but maybe we can cache it), or we need to customise the introspection to not show these fields, plus also prevent them from being called :) |
Hi there, I'm original requester of this functionality in the discord thread, so I thought I'd share some of our use-case context, particularly around the dynamic building versus filtering. We're attempting to show a subset of our API/types to external integrators, with just the data we're happy to show them (and so generally have a smaller integration surface area). We then broadly extend those graphql types for our own front-end use so we don't have to maintain duplicate python classes. In-fact, we actually have another superset schema on top of that of data/mutations we show/use in internal admin tooling. Being able to compose an entirely different schema feels quite useful because we can (and I think prefer to) expose it on a different endpoint - in the case of the internal admin tooling we limit access to that endpoint to only our own tooling. The schema itself being more static (it's likely 80% of our schema for example will appear/disappear based on this) feels like it would play with other tooling a bit more neatly. For example we use Hopefully that's useful context, and do I appreciate it's our specific use-case you're trying to adapt to the general case. |
@jonfinerty thanks for the context! I'll add another reference for inspiration: https://graphql-ruby.org/authorization/visibility.html I'll probably take a day off next week to think about this more 😊 |
@patrick91 great source from the ruby docs, this one is more aligned with my suggestion of modifying the field visibility instead of managing two separate schemas. You would still be able to host your schema on separate endpoints, just injecting some endpoint information into the visibility-determining extension. I prefer this approach for several reasons: first, it leaves schema generation untouched. Secondly, and most importantly, we avoid having future issues asking for swapping out certain types in some of the different sub-schema. This is an antipattern by design and we should avoid enabling users to build towards this kind of scenario. I think the main concept we need to extract from this "user story" is the ability to modify the visibility of your schema for different audiences. And to achieve this, my favored approach is to have a single schema underneath that can be re-used, similar to what is described in the ruby docs. Of course it's more expensive, so I'm opening to discuss. |
Have you also seen Apollo Federation contracts? To me dynamic schemas sounds harder to think about, because a request can change the schema (and people could get crazy with dynamic filters), I wonder if there's a middle ground. The ruby library also allows types to define a visibility (which in turns hides any field using that type if it is hidden), do we also want to do that? (Also they do it for arguments) Also this PR approach has a centralised approach, do we prefer to move the check on the fields/types? Or shall we do both?
Is this because we are going to hack our solution on top of GraphQL core? I'd think having a new instance of the schema (if we could do it fast enough) would be better, as it reduces the number of places we need to touch to make sure all works well 😊
Do you have an example of this? |
b48b1bc
to
08d2fd3
Compare
@erik I think I'm ok with merging this PR as is, it's not as flexible as it could be, but I think it unblocks @jonfinerty without us having to commit to a big API change/addition 😊 @jkimbo just noticed your comment 🤦♂️, we already pass the full object type so we should be able complex filters 😊 For the dynamic filtering we can chat here -> #3343 |
Reminder for self: mention the hook in the docs before merging |
Thanks for adding the Here's a preview of the changelog: This release adds a new method @strawberry.type
class User:
name: str
email: str = strawberry.field(metadata={"tags": ["internal"]})
@strawberry.type
class Query:
user: User
def public_field_filter(field: StrawberryField) -> bool:
return "internal" not in field.metadata.get("tags", [])
class PublicSchema(strawberry.Schema):
def get_fields(
self, type_definition: StrawberryObjectDefinition
) -> List[StrawberryField]:
return list(filter(public_field_filter, type_definition.fields))
schema = PublicSchema(query=Query) The schema here would only have the Here's the tweet text:
|
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.
LGTM :)
This is a POC for allowing to filter/extend fields from the GraphQL schema, it came from this discord conversation: https://discord.com/channels/689806334337482765/1180117357943853116/1180117357943853116
I think it would be cool to allow filtering, but not sure if this is the nicest way (I don't like having to pass that function around), maybe we could think about using the schema config?
/cc @erikwrede @bellini666 @jkimbo for opinions 😊