-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
761a934
commit 025938e
Showing
9 changed files
with
194 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from typing import Generic, TypeVar | ||
from graphql.type.definition import GraphQLResolveInfo | ||
|
||
T = TypeVar("T") | ||
|
||
|
||
class TypedResolverInfo(GraphQLResolveInfo, Generic[T]): | ||
""" | ||
TypedResolverInfo adds type safety to the `context` field of Ariadne's | ||
GraphQLResolveInfo by letting us declare the expected structure. | ||
Example usage: | ||
class CoverageAnalyticsContext(Protocol): | ||
repository: Optional[Repository] | ||
def resolve_percent_covered( | ||
coverage_analytics: CoverageAnalytics, info: TypedResolverInfo[CoverageAnalyticsContext] | ||
) -> Optional[float]: | ||
repository = info.context.repository # we are able to use IDE autocomplete here | ||
return repository.recent_coverage if repository else None | ||
""" | ||
context: T |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import unittest | ||
from typing import Optional, Protocol, Any | ||
from graphql import GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString, GraphQLResolveInfo | ||
|
||
from graphql_api.context import TypedResolverInfo | ||
|
||
|
||
class MockContextProtocol(Protocol): | ||
repository: Optional[str] | ||
|
||
|
||
class MockContext(MockContextProtocol): | ||
def __init__(self, repository: Optional[str] = None): | ||
self.repository = repository | ||
|
||
|
||
def create_mock_graphql_resolve_info(context: MockContext) -> TypedResolverInfo[MockContext]: | ||
""" | ||
Helper function to create a mock TypedResolverInfo object for testing. | ||
""" | ||
mock_field_name = "mockField" | ||
mock_field_nodes = [] # Normally, this would be a list of AST nodes, empty for simplicity | ||
mock_return_type = GraphQLString | ||
mock_parent_type = GraphQLObjectType( | ||
name="MockParent", | ||
fields={"mockField": GraphQLField(GraphQLString)} | ||
) | ||
mock_path = [] # Path should be a valid list, not None | ||
mock_schema = GraphQLSchema(query=mock_parent_type) | ||
mock_fragments = {} | ||
mock_root_value = None | ||
mock_operation = None | ||
mock_variable_values = {} | ||
mock_is_awaitable = False | ||
|
||
# Create a mock instance of TypedResolverInfo | ||
info = TypedResolverInfo( | ||
field_name=mock_field_name, | ||
field_nodes=mock_field_nodes, | ||
return_type=mock_return_type, | ||
parent_type=mock_parent_type, | ||
path=mock_path, | ||
schema=mock_schema, | ||
fragments=mock_fragments, | ||
root_value=mock_root_value, | ||
operation=mock_operation, | ||
variable_values=mock_variable_values, | ||
context=context, # The mock context | ||
is_awaitable=mock_is_awaitable, | ||
) | ||
|
||
return info | ||
|
||
|
||
class TestTypedResolverInfoGenerics(unittest.TestCase): | ||
def test_typed_resolver_info_stores_context(self): | ||
# Create a concrete context instance with repository data | ||
mock_context = MockContext(repository="Test Repository") | ||
|
||
# Create a TypedResolverInfo with the mock context | ||
typed_info = create_mock_graphql_resolve_info(mock_context) | ||
|
||
# Assert that context is stored and accessible with correct typing | ||
self.assertEqual(typed_info.context.repository, "Test Repository") | ||
|
||
def test_typed_resolver_info_handles_optional_none(self): | ||
# Create a concrete context instance with repository as None | ||
mock_context = MockContext(repository=None) | ||
|
||
# Create a TypedResolverInfo with the mock context | ||
typed_info = create_mock_graphql_resolve_info(mock_context) | ||
|
||
# Assert that context repository is None | ||
self.assertIsNone(typed_info.context.repository) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.