-
Notifications
You must be signed in to change notification settings - Fork 30
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
feat: Add graphql coverage analytics type #806
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
✅ All tests successful. No failed tests found. Additional details and impacted files@@ Coverage Diff @@
## main #806 +/- ##
================================================
+ Coverage 96.24000 96.25000 +0.01000
================================================
Files 812 814 +2
Lines 18602 18656 +54
================================================
+ Hits 17904 17958 +54
Misses 698 698
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Codecov ReportAll modified and coverable lines are covered by tests ✅ ✅ All tests successful. No failed tests found. 📢 Thoughts on this report? Let us know! |
❌ 1 Tests Failed:
View the top 1 failed tests by shortest run time
To view individual test run time comparison to the main branch, go to the Test Analytics Dashboard |
Test Failures Detected: Due to failing tests, we cannot provide coverage reports at this time. ❌ Failed Test Results:Completed 2309 tests with View the full list of failed testspytest
|
089c129
to
bb298be
Compare
2914a80
to
99c57ee
Compare
@@ -864,7 +864,7 @@ def test_fetch_is_github_rate_limited_but_errors( | |||
|
|||
assert data["me"]["owner"]["repository"]["isGithubRateLimited"] is None | |||
|
|||
mock_log_warning.assert_called_once_with( | |||
mock_log_warning.assert_any_call( |
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 saw warningLog getting called twice and causing the test to fail so loosened the requirement... Made it so it doesn't need to be the only warning log and as long as the rate limit error was logged we're good
|
||
from .helper import GraphQLTestHelper | ||
|
||
|
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.
All the tests in this file are basically just copied over from test_repository_measurements.py
plus small fixes to reflect the new nesting of the coverageAnalytics
type.
""" | ||
CoverageAnalytics is information related to a repo's test coverage | ||
""" | ||
type CoverageAnalytics { |
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.
# CoverageAnalyticsProps is information passed from parent resolver (repository) | ||
# to the coverage analytics resolver | ||
@dataclass | ||
class CoverageAnalyticsProps: |
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 propose this pattern of declaring the static type of the "parent" that is the first argument to any ariadne resolver with name of <currentType>Props
. Borrows the concept/name from React that indicates it is a collection of strongly-typed stuff passed directly from parent to child. Elsewhere in this repo it wasn't strongly typed and/or was ambiguously named/constructed. It doesn't seem like there's a strong ariadne community convention either.. so we could try this..
The parent (repository) passes this information as below, and the return type (CoverageAnalyticsProps) is clear and discoverable
# example model
repository: {
coverageAnalytics: {
percentCovered: 12
}
}
#####
# type declaration for the return value of the "parent" that will be the first (0th) positional argument to any ariadne field resolver of the "child"
class CoverageAnalyticsProps:
repository: Repository
#####
# repository.coverageAnalytics resolver (see its return type is `CoverageAnalyticsProps`)
@repository_bindable.field("coverageAnalytics")
def resolve_coverage_analytics(repository: Repository, info: GraphQLResolveInfo) -> CoverageAnalyticsProps:
return CoverageAnalyticsProps(
repository=repository,
)
#####
# coverageAnalytics.percentCovered resolver (see its 0th arg type is `CoverageAnalyticsProps`)
@coverage_analytics_bindable.field("percentCovered")
def resolve_percent_covered(parent: CoverageAnalyticsProps, info: GraphQLResolveInfo) -> Optional[float]:
return parent.repository.recent_coverage if parent else None
One downside (honestly, benefit?) of this approach is we lose the "magic" of Ariadne's inferred/default resolvers where if a field resolver is not explicitly provided, Ariadne tries to infer one by matching the field name with the parent object's attributes. As in if I had passed repository
to the child directly instead of CoverageAnalyticsProps.repository
, I could have gotten some field matching "for free" without having to write my own resolvers (this is not something I think I want 😆 )
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 was thinking there would be a configuration option to disable default resolvers, but for the life of me I can not find any way to do it on the Ariadne docs.
What you proposed sounds like a good alternative to me, though I'm imagining it would take a bit for devs to build the muscle memory to add follow this guideline.
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.
what would you propose for all our existing patterns? I can see a world where we do this for a few models but never go around to cleaning up the rest.
For that reason, I'd say we should probably look to create a proposal outside of this review and gain consensus / context across the team so anyone who happens to miss this review is still informed with the new pattern going forward, and we have a gameplan and bandwidth allocated to actually do this migration across the board as well.
Off the top of your head, would you expect this to take a single engineer a week, a sprint, etc?
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.
Thanks so much for your input here and in our Applications Weekly meeting!!
As we discussed, I'll merge this as a trial run of this pattern and we can adjust as we mature team opinions on this.
re: How long to do migrations across the board - uhhh I'd guess it can be on the order of a sprint depending on how familiar the given engineer is already with api
, but I'd be more concerned that it's risky, so I'd want to be more sure we want this before touching all those.
Great suggestion there - I spun off this ticket to track some consensus investigation work
[EDIT / update] - It seems like the GraphQL convention is to have the parent take the type of what the graphql resolver would return for that whole parent (and not the db model as we seem to do it in a lot of places). We have the option of going for something like that too.
66e70e4
to
64fe169
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.
Type checks fail because the 500+ line file I touched (repository.py) is missing a bunch of types. I didn't want to touch that side-quest with this right now
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.
All these type annotations and GQL field descriptions are beautiful 💯
# CoverageAnalyticsProps is information passed from parent resolver (repository) | ||
# to the coverage analytics resolver | ||
@dataclass | ||
class CoverageAnalyticsProps: |
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 was thinking there would be a configuration option to disable default resolvers, but for the life of me I can not find any way to do it on the Ariadne docs.
What you proposed sounds like a good alternative to me, though I'm imagining it would take a bit for devs to build the muscle memory to add follow this guideline.
Description
Restructure GraphQL schema to move coverage related fields in the Repository type into one level of nesting. This is one batch of fields being refactored in the project.
Cleanup of the duped fields will happen after the frontend is integrated to the new fields (#2282)
BEFORE
AFTER
This is the GraphQL query
Tested in staging by confirming the old fields and new fields return the same data for a given query.
Closes codecov/engineering-team#2280