Skip to content

Commit c8aa548

Browse files
committed
fix(s2dm): Use the correct output type
Problem appeared when field connects to type produced from vspec struct and it was resolving always to String due to name shortening missmatches. Signed-off-by: JD Alvarez <8550265+jdacoello@users.noreply.github.com>
1 parent f406288 commit c8aa548

File tree

2 files changed

+106
-19
lines changed

2 files changed

+106
-19
lines changed

src/vss_tools/exporters/s2dm/type_builders.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ def create_struct_types(
370370
fields = {}
371371
for prop_fqn, prop_row in properties.iterrows():
372372
field_name = convert_name_for_graphql_schema(prop_row["name"], GraphQLElementType.FIELD, S2DM_CONVERSIONS)
373-
base_type = _get_graphql_type_for_property(prop_row, struct_types)
373+
base_type = _get_graphql_type_for_property(prop_row, struct_types, short_name_mapping)
374374
fields[field_name] = GraphQLField(GraphQLNonNull(base_type), description=prop_row.get("description", ""))
375375

376376
field_path = build_field_path(type_name, field_name)
@@ -402,38 +402,70 @@ def create_struct_types(
402402
return struct_types
403403

404404

405-
def _get_graphql_type_for_property(prop_row: pd.Series, struct_types: dict[str, GraphQLObjectType]) -> Any:
405+
def _get_graphql_type_for_property(
406+
prop_row: pd.Series, struct_types: dict[str, GraphQLObjectType], short_name_mapping: dict[str, str] | None = None
407+
) -> Any:
406408
"""Map VSS property datatype to GraphQL type."""
407409
datatype = prop_row.get("datatype", "string")
408-
return resolve_datatype_to_graphql(datatype, struct_types)
410+
return resolve_datatype_to_graphql(datatype, struct_types, short_name_mapping)
409411

410412

411-
def resolve_datatype_to_graphql(datatype: str, types_registry: dict[str, Any]) -> Any:
413+
def resolve_datatype_to_graphql(
414+
datatype: str, types_registry: dict[str, Any], short_name_mapping: dict[str, str] | None = None
415+
) -> Any:
412416
"""
413417
Resolve a VSS datatype string to its corresponding GraphQL type.
414418
415419
Args:
416420
datatype: VSS datatype string (e.g., "uint8", "MyStruct", "MyStruct[]")
417421
types_registry: Dictionary of custom types
422+
short_name_mapping: Optional mapping from FQN to short type names
418423
419424
Returns:
420425
Corresponding GraphQL type
426+
427+
Raises:
428+
ValueError: If datatype references a struct that doesn't exist in types_registry
421429
"""
422-
if datatype.endswith("[]"):
423-
base_datatype = datatype[:-2]
430+
is_array = datatype.endswith("[]")
431+
base_datatype = datatype[:-2] if is_array else datatype
432+
433+
# First check if it's a primitive type
434+
if base_datatype in VSS_DATATYPE_MAP:
435+
base_type = VSS_DATATYPE_MAP[base_datatype]
436+
if is_array:
437+
return GraphQLList(GraphQLNonNull(base_type))
438+
return base_type
439+
440+
# Not a primitive, so it should be a struct type
441+
# Try to resolve using short_name_mapping first if available
442+
struct_type_name = None
443+
if short_name_mapping and base_datatype in short_name_mapping:
444+
struct_type_name = short_name_mapping[base_datatype]
445+
else:
446+
# Fall back to FQN conversion
424447
struct_type_name = convert_name_for_graphql_schema(base_datatype, GraphQLElementType.TYPE, S2DM_CONVERSIONS)
425448

426-
if struct_type_name in types_registry:
427-
return GraphQLList(GraphQLNonNull(types_registry[struct_type_name]))
428-
429-
base_type = VSS_DATATYPE_MAP.get(base_datatype, GraphQLString)
430-
return GraphQLList(GraphQLNonNull(base_type))
431-
432-
struct_type_name = convert_name_for_graphql_schema(datatype, GraphQLElementType.TYPE, S2DM_CONVERSIONS)
433449
if struct_type_name in types_registry:
434-
return types_registry[struct_type_name]
435-
436-
return VSS_DATATYPE_MAP.get(datatype, GraphQLString)
450+
struct_type = types_registry[struct_type_name]
451+
if is_array:
452+
return GraphQLList(GraphQLNonNull(struct_type))
453+
return struct_type
454+
455+
# Type not found - this is a translation error
456+
available_structs = [
457+
name for name in types_registry.keys() if not name.endswith("_Enum") and not name.endswith("UnitEnum")
458+
]
459+
log.error(
460+
f"Failed to resolve datatype '{datatype}' to GraphQL type. "
461+
f"This datatype is not a primitive and doesn't match any struct type in the registry. "
462+
f"Available struct types: {available_structs}. "
463+
f"Check that the struct is defined in your types vspec file."
464+
)
465+
raise ValueError(
466+
f"Cannot resolve datatype '{datatype}': not a primitive type and not found in types registry. "
467+
f"Available struct types: {available_structs}"
468+
)
437469

438470

439471
def create_object_type(
@@ -520,7 +552,7 @@ def get_fields() -> dict[str, GraphQLField]:
520552
if pd.notna(leaf_row.get("deprecation")) and leaf_row.get("deprecation").strip():
521553
vspec_comments["field_deprecated"][field_path] = leaf_row["deprecation"]
522554

523-
field_type = get_graphql_type_for_leaf(leaf_row, types_registry)
555+
field_type = get_graphql_type_for_leaf(leaf_row, types_registry, short_name_mapping)
524556
unit_args = _get_unit_args(leaf_row, unit_enums)
525557
fields[field_name] = GraphQLField(field_type, args=unit_args, description=leaf_row.get("description", ""))
526558

@@ -657,7 +689,9 @@ def _get_unit_args(leaf_row: pd.Series, unit_enums: dict[str, GraphQLEnumType])
657689
return {"unit": GraphQLArgument(type_=unit_enum, default_value=unit)}
658690

659691

660-
def get_graphql_type_for_leaf(leaf_row: pd.Series, types_registry: dict[str, Any] | None = None) -> Any:
692+
def get_graphql_type_for_leaf(
693+
leaf_row: pd.Series, types_registry: dict[str, Any] | None = None, short_name_mapping: dict[str, str] | None = None
694+
) -> Any:
661695
"""Map VSS leaf to GraphQL type."""
662696
if types_registry:
663697
try:
@@ -675,6 +709,6 @@ def get_graphql_type_for_leaf(leaf_row: pd.Series, types_registry: dict[str, Any
675709
datatype = leaf_row.get("datatype", "string")
676710

677711
if types_registry:
678-
return resolve_datatype_to_graphql(datatype, types_registry)
712+
return resolve_datatype_to_graphql(datatype, types_registry, short_name_mapping)
679713

680714
return VSS_DATATYPE_MAP.get(datatype, GraphQLString)

tests/test_s2dm_structs.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,59 @@ def test_primitive_property_in_struct(self, struct_trees):
269269
assert isinstance(z_property_type, GraphQLNonNull)
270270
assert z_property_type.of_type == VSS_DATATYPE_MAP["double"]
271271

272+
def test_signal_with_struct_datatype_short_names(self, struct_trees):
273+
"""Test that signals correctly reference structs when using short names (default mode)."""
274+
tree, data_type_tree = struct_trees
275+
schema, _, _, _ = generate_s2dm_schema(tree, data_type_tree, use_short_names=True)
276+
277+
# Get the TestRoot type
278+
root_type = schema.type_map["TestRoot"] # Short name mode
279+
280+
# Get struct type names (should be short names)
281+
parent_struct_name = "ParentStruct"
282+
nested_struct_name = "NestedStruct"
283+
284+
# Verify struct types exist with short names
285+
assert parent_struct_name in schema.type_map
286+
assert nested_struct_name in schema.type_map
287+
288+
# Check fields
289+
assert isinstance(root_type, GraphQLObjectType)
290+
fields = root_type.fields
291+
292+
# ParentStructSensor should reference ParentStruct (short name)
293+
assert "parentStructSensor" in fields
294+
parent_sensor_type = fields["parentStructSensor"].type
295+
assert (
296+
parent_sensor_type == schema.type_map[parent_struct_name]
297+
), f"Expected ParentStruct but got {parent_sensor_type}"
298+
299+
# NestedStructSensor should reference NestedStruct (short name)
300+
assert "nestedStructSensor" in fields
301+
nested_sensor_type = fields["nestedStructSensor"].type
302+
assert (
303+
nested_sensor_type == schema.type_map[nested_struct_name]
304+
), f"Expected NestedStruct but got {nested_sensor_type}"
305+
306+
def test_struct_in_property_short_names(self, struct_trees):
307+
"""Test that struct properties correctly reference other structs with short names."""
308+
tree, data_type_tree = struct_trees
309+
schema, _, _, _ = generate_s2dm_schema(tree, data_type_tree, use_short_names=True)
310+
311+
# Get ParentStruct type (short name)
312+
parent_struct_type = schema.type_map["ParentStruct"]
313+
nested_struct_type = schema.type_map["NestedStruct"]
314+
315+
# Check that x_property and y_property reference NestedStruct
316+
assert isinstance(parent_struct_type, GraphQLObjectType)
317+
fields = parent_struct_type.fields
318+
319+
# x_property should reference NestedStruct
320+
assert "xProperty" in fields
321+
x_property_type = fields["xProperty"].type
322+
assert isinstance(x_property_type, GraphQLNonNull)
323+
assert x_property_type.of_type == nested_struct_type, f"Expected NestedStruct but got {x_property_type.of_type}"
324+
272325

273326
class TestS2DMStructsModular:
274327
"""Test class for modular output with struct support."""

0 commit comments

Comments
 (0)