Skip to content

Commit 1918457

Browse files
committed
fix(directives): improve serialization of directive arguments using print_ast
Signed-off-by: JD Alvarez <8550265+jdacoello@users.noreply.github.com>
1 parent 30af8a0 commit 1918457

File tree

2 files changed

+111
-8
lines changed

2 files changed

+111
-8
lines changed

src/s2dm/exporters/utils/directive.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
GraphQLUnionType,
1616
IntValueNode,
1717
)
18-
from graphql.language.ast import StringValueNode
18+
from graphql.language.printer import print_ast
1919

2020
GRAPHQL_TYPE_DEFINITION_PATTERN = r"^(type|interface|input|enum|union|scalar)\s+(\w+)"
2121

@@ -104,13 +104,8 @@ def format_directive_from_ast(directive_node: Any) -> str:
104104
args_list = []
105105
for arg_node in directive_node.arguments:
106106
arg_name = arg_node.name.value
107-
if hasattr(arg_node.value, "value"):
108-
if isinstance(arg_node.value, StringValueNode):
109-
arg_value = f'"{arg_node.value.value}"'
110-
else:
111-
arg_value = str(arg_node.value.value)
112-
else:
113-
arg_value = str(arg_node.value)
107+
# Use graphql-core's print_ast to properly serialize all value types
108+
arg_value = print_ast(arg_node.value)
114109
args_list.append(f"{arg_name}: {arg_value}")
115110
args_str = f"({', '.join(args_list)})"
116111

tests/test_schema_loader.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,111 @@ def test_reference_directive_with_all_standard_locations(spec_directory: Path) -
151151
assert 'enum TestEnum @reference(source: "enums.graphql")' in result
152152
assert 'scalar TestScalar @reference(source: "scalars.graphql")' in result
153153
assert 'input TestInput @reference(source: "inputs.graphql")' in result
154+
155+
156+
def test_compose_preserves_directive_with_list_arguments() -> None:
157+
"""Test that directives with list arguments are serialized correctly."""
158+
schema_str = """
159+
directive @vspec(element: String, metadata: [KeyValue]) on ENUM | ENUM_VALUE
160+
161+
input KeyValue {
162+
key: String!
163+
value: String!
164+
}
165+
166+
enum LengthUnit @vspec(element: "QUANTITY_KIND", metadata: [{key: "quantity", value: "length"}]) {
167+
MILLIMETER @vspec(element: "UNIT", metadata: [{key: "unit", value: "mm"}])
168+
CENTIMETER @vspec(element: "UNIT", metadata: [{key: "unit", value: "cm"}])
169+
METER @vspec(element: "UNIT", metadata: [{key: "unit", value: "m"}])
170+
}
171+
172+
type Query { field: String }
173+
"""
174+
175+
schema = build_schema(schema_str)
176+
source_map: dict[str, str] = {}
177+
178+
result = print_schema_with_directives_preserved(schema, source_map)
179+
180+
# Verify list arguments are properly serialized, not as "ListValueNode at X:Y"
181+
assert 'enum LengthUnit @vspec(element: "QUANTITY_KIND", metadata: [{key: "quantity", value: "length"}])' in result
182+
assert 'MILLIMETER @vspec(element: "UNIT", metadata: [{key: "unit", value: "mm"}])' in result
183+
assert 'CENTIMETER @vspec(element: "UNIT", metadata: [{key: "unit", value: "cm"}])' in result
184+
assert 'METER @vspec(element: "UNIT", metadata: [{key: "unit", value: "m"}])' in result
185+
186+
# Ensure no AST node representations leaked into output
187+
assert "ListValueNode" not in result
188+
assert "ObjectValueNode" not in result
189+
190+
191+
def test_compose_preserves_directive_with_nested_objects() -> None:
192+
"""Test that directives with nested object structures are serialized correctly."""
193+
schema_str = """
194+
directive @metadata(config: ConfigInput) on OBJECT
195+
196+
input ConfigInput {
197+
settings: SettingsInput
198+
name: String
199+
}
200+
201+
input SettingsInput {
202+
enabled: Boolean
203+
count: Int
204+
}
205+
206+
type TestType @metadata(config: {settings: {enabled: true, count: 42}, name: "test"}) {
207+
field: String
208+
}
209+
210+
type Query { field: String }
211+
"""
212+
213+
schema = build_schema(schema_str)
214+
source_map: dict[str, str] = {}
215+
216+
result = print_schema_with_directives_preserved(schema, source_map)
217+
218+
# Verify nested objects are properly serialized
219+
assert '@metadata(config: {settings: {enabled: true, count: 42}, name: "test"})' in result
220+
assert "ObjectValueNode" not in result
221+
222+
223+
def test_compose_preserves_directive_with_various_scalar_types() -> None:
224+
"""Test that directives with different scalar types are serialized correctly."""
225+
schema_str = """
226+
directive @config(
227+
name: String
228+
count: Int
229+
ratio: Float
230+
enabled: Boolean
231+
status: StatusEnum
232+
) on OBJECT
233+
234+
enum StatusEnum {
235+
ACTIVE
236+
INACTIVE
237+
}
238+
239+
type TestType @config(name: "test", count: 123, ratio: 3.14, enabled: true, status: ACTIVE) {
240+
field: String
241+
}
242+
243+
type Query { field: String }
244+
"""
245+
246+
schema = build_schema(schema_str)
247+
source_map: dict[str, str] = {}
248+
249+
result = print_schema_with_directives_preserved(schema, source_map)
250+
251+
# Verify all scalar types are properly serialized
252+
assert '@config(name: "test", count: 123, ratio: 3.14, enabled: true, status: ACTIVE)' in result
253+
# Ensure strings are properly quoted
254+
assert 'name: "test"' in result
255+
# Ensure numbers are not quoted
256+
assert "count: 123" in result
257+
assert "ratio: 3.14" in result
258+
# Ensure booleans are lowercase
259+
assert "enabled: true" in result
260+
# Ensure enums are not quoted
261+
assert "status: ACTIVE" in result

0 commit comments

Comments
 (0)