Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/nexusx/sdl_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,15 @@ def _generate_type(self, entity: type[SQLModel]) -> str:
# Check if it's a relationship (references another entity)
gql_type = self._type_hint_to_graphql(hint, entity, field_name)
if gql_type:
fields.append(f" {field_name}: {gql_type}")
# Paginated list fields declare limit/offset args to match
# the introspection path — without them, SDL-only clients
# cannot tell the field is paginated.
if self._is_paginated_relationship(entity, field_name):
fields.append(
f" {field_name}(limit: Int, offset: Int = 0): {gql_type}"
)
else:
fields.append(f" {field_name}: {gql_type}")

# Build type definition with optional description
type_def = f"type {entity.__name__} {{\n{chr(10).join(fields)}\n}}"
Expand Down
19 changes: 18 additions & 1 deletion tests/test_pagination_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,24 @@ def test_paginated_list_renders_as_result_type(self):
assert "items: [SortedKid!]!" in sdl

parent_block = sdl.split("type MixedParent {", 1)[1].split("}", 1)[0]
assert "sorted_kids: SortedKidResult!" in parent_block
# Field type is SortedKidResult! (non-null). Field may declare args
# — that's covered by test_paginated_field_exposes_limit_offset_args.
assert ": SortedKidResult!" in parent_block

def test_paginated_field_exposes_limit_offset_args(self):
"""Issue #85: paginated list field must declare limit/offset args in SDL.

SDL and introspection must agree — without args in SDL, any client
that reads the schema via SDL (codegen tools, AI agents, GraphiQL
alternatives) cannot tell the field is paginated.
"""
registry = _make_registry()
sdl = SDLGenerator(MIXED_ENTITIES).generate(
enable_pagination=True, loader_registry=registry
)

parent_block = sdl.split("type MixedParent {", 1)[1].split("}", 1)[0]
assert "sorted_kids(limit: Int, offset: Int = 0): SortedKidResult!" in parent_block

def test_non_paginated_list_renders_as_plain_list(self):
"""raw_kids (no order_by) → [RawKid!]! with no pagination args."""
Expand Down