Skip to content
This repository was archived by the owner on Feb 6, 2025. It is now read-only.

Commit 3a4e431

Browse files
author
Selene Chew
committed
Convert SQLAlchemySchemaInfo to a dataclass
1 parent 44c63aa commit 3a4e431

File tree

2 files changed

+85
-46
lines changed

2 files changed

+85
-46
lines changed

graphql_compiler/schema/schema_info.py

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -226,56 +226,62 @@ def _create_sql_schema_info(
226226
)
227227

228228

229-
# Complete schema information sufficient to compile GraphQL queries to SQLAlchemy
230-
#
231-
# It describes the tables that correspond to each type (object type or interface type),
232-
# and gives instructions on how to perform joins for each vertex field. The property fields on each
233-
# type are implicitly mapped to columns with the same name on the corresponding table.
234-
#
235-
# NOTES:
236-
# - RootSchemaQuery is a special type that does not need a corresponding table.
237-
# - Builtin types like __Schema, __Type, etc. don't need corresponding tables.
238-
# - Builtin fields like _x_count do not need corresponding columns.
239-
SQLAlchemySchemaInfo = namedtuple(
240-
"SQLAlchemySchemaInfo",
241-
(
242-
# GraphQLSchema
243-
"schema",
244-
# optional dict of GraphQL interface or type -> GraphQL union.
245-
# Used as a workaround for GraphQL's lack of support for
246-
# inheritance across "types" (i.e. non-interfaces), as well as a
247-
# workaround for Gremlin's total lack of inheritance-awareness.
248-
# The key-value pairs in the dict specify that the "key" type
249-
# is equivalent to the "value" type, i.e. that the GraphQL type or
250-
# interface in the key is the most-derived common supertype
251-
# of every GraphQL type in the "value" GraphQL union.
252-
# Recursive expansion of type equivalence hints is not performed,
253-
# and only type-level correctness of this argument is enforced.
254-
# See README.md for more details on everything this parameter does.
255-
# *****
256-
# Be very careful with this option, as bad input here will
257-
# lead to incorrect output queries being generated.
258-
# *****
259-
"type_equivalence_hints",
260-
# sqlalchemy.engine.interfaces.Dialect, specifying the dialect we are compiling for
261-
# (e.g. sqlalchemy.dialects.mssql.dialect()).
262-
"dialect",
263-
# dict mapping every graphql object type or interface type name in the schema to
264-
# a sqlalchemy table. Column types that do not exist for this dialect are not allowed.
265-
# All tables are expected to have primary keys.
266-
"vertex_name_to_table",
267-
# dict mapping every graphql object type or interface type name in the schema to:
268-
# dict mapping every vertex field name at that type to a JoinDescriptor. The
269-
# tables the join is to be performed on are not specified. They are inferred from
270-
# the schema and the tables dictionary.
271-
"join_descriptors",
272-
),
273-
)
229+
@dataclass
230+
class SQLAlchemySchemaInfo:
231+
"""Complete schema information sufficient to compile GraphQL queries to SQLAlchemy.
232+
233+
It describes the tables that correspond to each type (object type or interface type),
234+
and gives instructions on how to perform joins for each vertex field. The property fields on
235+
each type are implicitly mapped to columns with the same name on the corresponding table.
236+
237+
Notes:
238+
- RootSchemaQuery is a special type that does not need a corresponding table.
239+
- Builtin types like __Schema, __Type, etc. don't need corresponding tables.
240+
- Builtin fields like _x_count do not need corresponding columns.
241+
TODO: This class is essentially the same as SQLSchemaInfo. SQLSchemaInfo is part of an
242+
incomplete refactor started in
243+
https://github.com/kensho-technologies/graphql-compiler/pull/714
244+
SQLAlchemySchemaInfo is currently used to compile GraphQL to SQL while CommonSchemaInfo
245+
is currently used to compile GraphQL to match, gremlin, and cypher.
246+
"""
247+
248+
schema: GraphQLSchema
249+
250+
# Optional dict of GraphQL interface or type -> GraphQL union.
251+
# Used as a workaround for GraphQL's lack of support for
252+
# inheritance across "types" (i.e. non-interfaces), as well as a
253+
# workaround for Gremlin's total lack of inheritance-awareness.
254+
# The key-value pairs in the dict specify that the "key" type
255+
# is equivalent to the "value" type, i.e. that the GraphQL type or
256+
# interface in the key is the most-derived common supertype
257+
# of every GraphQL type in the "value" GraphQL union.
258+
# Recursive expansion of type equivalence hints is not performed,
259+
# and only type-level correctness of this argument is enforced.
260+
# See README.md for more details on everything this parameter does.
261+
# *****
262+
# Be very careful with this option, as bad input here will
263+
# lead to incorrect output queries being generated.
264+
# *****
265+
type_equivalence_hints: Optional[TypeEquivalenceHintsType]
266+
267+
# Specifying the SQL Dialect.
268+
dialect: Dialect
269+
270+
# Mapping every GraphQL object or interface type name in the schema to the corresponding
271+
# SQLAlchemy table. Column types that do not exist for this dialect are not allowed.
272+
# All tables are expected to have primary keys.
273+
vertex_name_to_table: Dict[str, sqlalchemy.Table]
274+
275+
# Mapping every GraphQL object or interface type name in the schema to:
276+
# dict mapping every vertex field name at that type to a JoinDescriptor. The
277+
# tables the join is to be performed on are not specified. They are inferred from
278+
# the schema and the tables dictionary.
279+
join_descriptors: Dict[str, Dict[str, JoinDescriptor]]
274280

275281

276282
def make_sqlalchemy_schema_info(
277283
schema: GraphQLSchema,
278-
type_equivalence_hints: TypeEquivalenceHintsType,
284+
type_equivalence_hints: Optional[TypeEquivalenceHintsType],
279285
dialect: Dialect,
280286
vertex_name_to_table: Dict[str, sqlalchemy.Table],
281287
join_descriptors: Dict[str, Dict[str, JoinDescriptor]],

graphql_compiler/tests/schema_generation_tests/test_sqlalchemy_schema_generation.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,26 @@ def test_table_vertex_representation_with_non_default_name(self) -> None:
112112

113113
def test_represent_supported_fields(self) -> None:
114114
table1_graphql_object = self.schema_info.schema.get_type("Table1")
115+
# mypy complained even with self.assertIsInstance(table1_graphql_object, GraphQLObjectType)
116+
# so performing a manual check.
117+
if not isinstance(table1_graphql_object, GraphQLObjectType):
118+
raise AssertionError(
119+
f"table1_graphql_object expected to be GraphQLObjectType, but was of type "
120+
f"{type(table1_graphql_object)}"
121+
)
115122
self.assertEqual(
116123
table1_graphql_object.fields["column_with_supported_type"].type, GraphQLString
117124
)
118125

119126
def test_ignored_fields_not_supported(self) -> None:
120127
table1_graphql_object = self.schema_info.schema.get_type("Table1")
128+
# mypy complained even with self.assertIsInstance(table1_graphql_object, GraphQLObjectType)
129+
# so performing a manual check.
130+
if not isinstance(table1_graphql_object, GraphQLObjectType):
131+
raise AssertionError(
132+
f"table1_graphql_object expected to be GraphQLObjectType, but was of type "
133+
f"{type(table1_graphql_object)}"
134+
)
121135
self.assertTrue("column_with_non_supported_type" not in table1_graphql_object.fields)
122136

123137
def test_warn_when_type_is_not_supported(self) -> None:
@@ -140,11 +154,30 @@ def test_do_not_support_sql_tz_aware_datetime_types(self) -> None:
140154

141155
def test_mssql_scalar_type_representation(self) -> None:
142156
table1_graphql_object = self.schema_info.schema.get_type("Table1")
157+
# mypy complained even with self.assertIsInstance(table1_graphql_object, GraphQLObjectType)
158+
# so performing a manual check.
159+
if not isinstance(table1_graphql_object, GraphQLObjectType):
160+
raise AssertionError(
161+
f"table1_graphql_object expected to be GraphQLObjectType, but was of type "
162+
f"{type(table1_graphql_object)}"
163+
)
143164
self.assertEqual(table1_graphql_object.fields["column_with_mssql_type"].type, GraphQLInt)
144165

145166
def test_direct_sql_edge_representation(self) -> None:
146167
table1_graphql_object = self.schema_info.schema.get_type("Table1")
147168
arbitrarily_named_graphql_object = self.schema_info.schema.get_type("ArbitraryObjectName")
169+
# mypy complained even with self.assertIsInstance(table1_graphql_object, GraphQLObjectType)
170+
# so performing a manual check.
171+
if not isinstance(table1_graphql_object, GraphQLObjectType):
172+
raise AssertionError(
173+
f"table1_graphql_object expected to be GraphQLObjectType, but was of type "
174+
f"{type(table1_graphql_object)}"
175+
)
176+
if not isinstance(arbitrarily_named_graphql_object, GraphQLObjectType):
177+
raise AssertionError(
178+
f"arbitrarily_named_graphql_object expected to be GraphQLObjectType, but was of "
179+
f"type {type(arbitrarily_named_graphql_object)}"
180+
)
148181
self.assertEqual(
149182
table1_graphql_object.fields["out_test_edge"].type.of_type.name, "ArbitraryObjectName"
150183
)

0 commit comments

Comments
 (0)