diff --git a/README.md b/README.md
index e1c24392..2bc61e76 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,7 @@ Generates GraphQL types, inputs, queries and resolvers directly from SQLAlchemy
- [Mapping SQLAlchemy Models](#mapping-sqlalchemy-models)
- [Resolver Generation](#resolver-generation)
- [Pagination](#pagination)
+- [Ordering](#ordering)
- [Filtering](#filtering)
- [Aggregations](#aggregations)
- [Mutations](#mutations)
@@ -567,9 +568,11 @@ def farms(self) -> str:
Strawchemy supports offset-based pagination out of the box.
-Pagination example:
+Pagination examples
-Enable pagination on fields:
+### Field-Level Pagination
+
+Enable pagination on specific fields:
```python
from strawchemy.schema.pagination import DefaultOffsetPagination
@@ -577,10 +580,13 @@ from strawchemy.schema.pagination import DefaultOffsetPagination
@strawberry.type
class Query:
- # Enable pagination with default settings
+ # Enable pagination with default settings (limit=100, offset=0)
users: list[UserType] = strawchemy.field(pagination=True)
- # Customize pagination defaults
- users_custom_pagination: list[UserType] = strawchemy.field(pagination=DefaultOffsetPagination(limit=20))
+
+ # Customize pagination defaults for this specific field
+ users_custom: list[UserType] = strawchemy.field(
+ pagination=DefaultOffsetPagination(limit=20, offset=10)
+ )
```
In your GraphQL queries, you can use the `offset` and `limit` parameters:
@@ -594,10 +600,40 @@ In your GraphQL queries, you can use the `offset` and `limit` parameters:
}
```
-You can also enable pagination for nested relationships:
+### Config-Level Pagination
+
+Enable pagination globally for all list fields:
```python
-@strawchemy.type(User, include="all", child_pagination=True)
+from strawchemy import Strawchemy, StrawchemyConfig
+
+strawchemy = Strawchemy(
+ StrawchemyConfig(
+ "postgresql",
+ pagination="all", # Enable on all list fields
+ pagination_default_limit=100, # Default limit
+ pagination_default_offset=0, # Default offset
+ )
+)
+
+
+@strawchemy.type(User, include="all")
+class UserType:
+ pass
+
+
+@strawberry.type
+class Query:
+ # This field automatically has pagination enabled
+ users: list[UserType] = strawchemy.field()
+```
+
+### Type-level pagination
+
+Enable pagination for nested relationships from a specific type:
+
+```python
+@strawchemy.type(User, include="all", paginate="all")
class UserType:
pass
```
@@ -619,6 +655,118 @@ Then in your GraphQL queries:
+## Ordering
+
+Strawchemy provides flexible ordering capabilities for query results.
+
+
+Ordering examples
+
+### Field-Level Ordering
+
+Define ordering inputs and use them on specific fields:
+
+```python
+# Create order by input
+@strawchemy.order(User, include="all")
+class UserOrderBy:
+ pass
+
+
+@strawberry.type
+class Query:
+ users: list[UserType] = strawchemy.field(order_by=UserOrderBy)
+```
+
+Query with ordering:
+
+```graphql
+{
+ users(orderBy: [{ name: ASC }, { createdAt: DESC }]) {
+ id
+ name
+ createdAt
+ }
+}
+```
+
+Available ordering options:
+
+- `ASC` - Ascending order
+- `DESC` - Descending order
+- `ASC_NULLS_FIRST` - Ascending with nulls first
+- `ASC_NULLS_LAST` - Ascending with nulls last
+- `DESC_NULLS_FIRST` - Descending with nulls first
+- `DESC_NULLS_LAST` - Descending with nulls last
+
+### Type-Level Ordering
+
+Enable ordering automatically on a type:
+
+```python
+@strawchemy.type(User, include="all", order="all")
+class UserType:
+ pass
+```
+
+This automatically generates and applies an order by input for all fields using this type.
+
+### Config-Level Ordering
+
+Enable ordering globally for all list fields:
+
+```python
+from strawchemy import Strawchemy, StrawchemyConfig
+
+strawchemy = Strawchemy(
+ StrawchemyConfig(
+ "postgresql",
+ order_by="all", # Enable ordering on all list fields
+ )
+)
+
+
+@strawchemy.type(User, include="all")
+class UserType:
+ pass
+
+
+@strawberry.type
+class Query:
+ # This field automatically has ordering enabled
+ users: list[UserType] = strawchemy.field()
+```
+
+With this configuration, all list fields will automatically have an `orderBy` argument without needing to specify it per
+field.
+
+### Nested Relationship Ordering
+
+Order nested relationships:
+
+```python
+@strawchemy.type(User, include="all", order="all")
+class UserType:
+ pass
+```
+
+Query with nested ordering:
+
+```graphql
+{
+ users(orderBy: [{ name: ASC }]) {
+ id
+ name
+ posts(orderBy: [{ title: ASC }]) {
+ id
+ title
+ }
+ }
+}
+```
+
+
+
## Filtering
Strawchemy provides powerful filtering capabilities.
@@ -1950,18 +2098,20 @@ Configuration is made by passing a `StrawchemyConfig` to the `Strawchemy` instan
### Configuration Options
-| Option | Type | Default | Description |
-|----------------------------|-------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
-| `dialect` | `SupportedDialect` | | Database dialect to use. Supported dialects are "postgresql", "mysql", "sqlite". |
-| `session_getter` | `Callable[[Info], Session]` | `default_session_getter` | Function to retrieve SQLAlchemy session from strawberry `Info` object. By default, it retrieves the session from `info.context.session`. |
-| `auto_snake_case` | `bool` | `True` | Automatically convert snake cased names to camel case in GraphQL schema. |
-| `repository_type` | `type[Repository] \| StrawchemySyncRepository` | `StrawchemySyncRepository` | Repository class to use for auto resolvers. |
-| `filter_overrides` | `OrderedDict[tuple[type, ...], type[SQLAlchemyFilterBase]]` | `None` | Override default filters with custom filters. This allows you to provide custom filter implementations for specific column types. |
-| `execution_options` | `dict[str, Any]` | `None` | SQLAlchemy execution options for repository operations. These options are passed to the SQLAlchemy `execution_options()` method. |
-| `pagination_default_limit` | `int` | `100` | Default pagination limit when `pagination=True`. |
-| `pagination` | `bool` | `False` | Enable/disable pagination on list resolvers by default. |
-| `default_id_field_name` | `str` | `"id"` | Name for primary key fields arguments on primary key resolvers. |
-| `deterministic_ordering` | `bool` | `True` | Force deterministic ordering for list resolvers. |
+| Option | Type | Default | Description |
+|-----------------------------|-------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
+| `dialect` | `SupportedDialect` | | Database dialect to use. Supported dialects are "postgresql", "mysql", "sqlite". |
+| `session_getter` | `Callable[[Info], Session]` | `default_session_getter` | Function to retrieve SQLAlchemy session from strawberry `Info` object. By default, it retrieves the session from `info.context.session`. |
+| `auto_snake_case` | `bool` | `True` | Automatically convert snake cased names to camel case in GraphQL schema. |
+| `repository_type` | `type[Repository] \| StrawchemySyncRepository` | `StrawchemySyncRepository` | Repository class to use for auto resolvers. |
+| `filter_overrides` | `OrderedDict[tuple[type, ...], type[SQLAlchemyFilterBase]]` | `None` | Override default filters with custom filters. This allows you to provide custom filter implementations for specific column types. |
+| `execution_options` | `dict[str, Any]` | `None` | SQLAlchemy execution options for repository operations. These options are passed to the SQLAlchemy `execution_options()` method. |
+| `default_id_field_name` | `str` | `"id"` | Name for primary key fields arguments on primary key resolvers. |
+| `deterministic_ordering` | `bool` | `True` | Force deterministic ordering for list resolvers. |
+| `pagination` | `Literal["all"] \| None` | `None` | Enable/disable pagination on list resolvers by default. Set to `"all"` to enable pagination on all list fields. |
+| `order_by` | `Literal["all"] \| None` | `None` | Enable/disable order by on list resolvers by default. Set to `"all"` to enable ordering on all list fields. |
+| `pagination_default_limit` | `int` | `100` | Default pagination limit when `pagination=True`. |
+| `pagination_default_offset` | `int` | `0` | Default pagination offset when `pagination=True`. |
### Example
@@ -1980,8 +2130,10 @@ strawchemy = Strawchemy(
"postgresql",
session_getter=get_session_from_context,
auto_snake_case=True,
- pagination=True,
+ pagination="all",
pagination_default_limit=50,
+ pagination_default_offset=0,
+ order_by="all",
default_id_field_name="pk",
)
)
diff --git a/examples/testapp/testapp/types.py b/examples/testapp/testapp/types.py
index 27d9f3d1..f1b6c940 100644
--- a/examples/testapp/testapp/types.py
+++ b/examples/testapp/testapp/types.py
@@ -20,7 +20,7 @@ class TicketOrder: ...
class TicketFilter: ...
-@strawchemy.type(Ticket, include="all", filter_input=TicketFilter, order_by=TicketOrder, override=True)
+@strawchemy.type(Ticket, include="all", filter_input=TicketFilter, order=TicketOrder, override=True)
class TicketType: ...
@@ -55,7 +55,7 @@ class ProjectOrder: ...
class ProjectFilter: ...
-@strawchemy.type(Project, include="all", filter_input=ProjectFilter, order_by=ProjectOrder, override=True)
+@strawchemy.type(Project, include="all", filter_input=ProjectFilter, order=ProjectOrder, override=True)
class ProjectType: ...
diff --git a/src/strawchemy/config/base.py b/src/strawchemy/config/base.py
index f8e7b9cd..a555892e 100644
--- a/src/strawchemy/config/base.py
+++ b/src/strawchemy/config/base.py
@@ -10,6 +10,7 @@
from strawchemy.utils.strawberry import default_session_getter
if TYPE_CHECKING:
+ from strawchemy.dto.types import FieldIterable, IncludeFields
from strawchemy.repository.typing import AnySessionGetter, FilterMap
from strawchemy.typing import AnyRepositoryType, SupportedDialect
@@ -43,14 +44,24 @@ class StrawchemyConfig:
"""Override default filters with custom filters."""
execution_options: dict[str, Any] | None = None
"""SQLAlchemy execution options for strawberry operations."""
- pagination_default_limit: int = 100
- """Default pagination limit when `pagination=True`."""
- pagination: bool = False
- """Enable/disable pagination on list resolvers."""
default_id_field_name: str = "id"
"""Name for primary key fields arguments on primary key resolvers."""
deterministic_ordering: bool = True
"""Force deterministic ordering for list resolvers."""
+ include: IncludeFields = "all"
+ """Globally included fields."""
+ exclude: FieldIterable | None = None
+ """Globally included fields."""
+ pagination: IncludeFields | None = None
+ """Enable/disable pagination on list resolvers."""
+ order_by: IncludeFields | None = None
+ """Enable/disable order by on list resolvers."""
+ distinct_on: IncludeFields | None = None
+ """Enable/disable order by onelist resolvers."""
+ pagination_default_limit: int = 100
+ """Default pagination limit when `pagination=True`."""
+ pagination_default_offset: int = 0
+ """Default pagination offset when `pagination=True`."""
inspector: SQLAlchemyGraphQLInspector = field(init=False)
diff --git a/src/strawchemy/dto/base.py b/src/strawchemy/dto/base.py
index b78f55bb..e73b1403 100644
--- a/src/strawchemy/dto/base.py
+++ b/src/strawchemy/dto/base.py
@@ -32,7 +32,7 @@
DTOMissing,
DTOSkip,
DTOUnset,
- ExcludeFields,
+ FieldIterable,
IncludeFields,
Purpose,
PurposeConfig,
@@ -392,17 +392,26 @@ def should_exclude_field(
explictly_excluded = node.is_root and field.model_field_name in dto_config.exclude
explicitly_included = node.is_root and field.model_field_name in dto_config.include
- # Exclude fields not present in init if purpose is write
- if dto_config.purpose is Purpose.WRITE and not explicitly_included:
- explictly_excluded = explictly_excluded or not field.init
+ globally_excluded = field.model_field_name in dto_config.global_exclude
+ globally_included = field.model_field_name in dto_config.global_include
+
if dto_config.include == "all" and not explictly_excluded:
- explicitly_included = True
+ explicitly_included = globally_included = True
+
+ if dto_config.global_include == "all" and not globally_excluded:
+ globally_included = True
excluded = dto_config.purpose not in field.allowed_purposes
+
+ # Exclude fields not present in init if purpose is write
+ if dto_config.purpose is Purpose.WRITE and not (explicitly_included or globally_included):
+ excluded = excluded or not field.init
+
if node.is_root:
excluded = excluded or (explictly_excluded or not explicitly_included)
else:
- excluded = excluded or explictly_excluded
+ excluded = excluded or (globally_excluded or not globally_included)
+
return not has_override and excluded
def _resolve_basic_type(self, field: DTOFieldDefinition[ModelT, ModelFieldT], dto_config: DTOConfig) -> Any:
@@ -626,6 +635,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
**kwargs: Any,
) -> type[DTOBaseT]:
"""Build a Data transfer object (DTO) from an SQAlchemy model."""
@@ -640,7 +650,7 @@ def factory(
if dto_config.scope == "global":
self._scoped_dto_names[self._scoped_cache_key(model, dto_config)] = name
- if (dto := self._dto_cache.get(cache_key)) or (dto := self._dto_cache.get(scoped_cache_key)):
+ if not no_cache and ((dto := self._dto_cache.get(cache_key)) or (dto := self._dto_cache.get(scoped_cache_key))):
return self.backend.copy(dto, name) if node.is_root else dto
dto = self._factory(
@@ -685,7 +695,7 @@ def decorator(
model: type[ModelT],
purpose: Purpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
diff --git a/src/strawchemy/dto/strawberry.py b/src/strawchemy/dto/strawberry.py
index a4c545b5..4ce86910 100644
--- a/src/strawchemy/dto/strawberry.py
+++ b/src/strawchemy/dto/strawberry.py
@@ -129,7 +129,9 @@ class _Key(Generic[T]):
string.
"""
- separator: str = ":"
+ __slots__ = ("_key",)
+
+ separator: ClassVar[str] = ":"
def __init__(self, components: Sequence[T | str] | str | None = None) -> None:
self._key: str = ""
diff --git a/src/strawchemy/dto/types.py b/src/strawchemy/dto/types.py
index 9722d3c6..07abe4e1 100644
--- a/src/strawchemy/dto/types.py
+++ b/src/strawchemy/dto/types.py
@@ -5,9 +5,9 @@
import dataclasses
from dataclasses import dataclass, field
from enum import Enum
-from typing import TYPE_CHECKING, Any, Literal, TypeAlias, final, get_type_hints
+from typing import TYPE_CHECKING, Any, Literal, TypeAlias, final, get_type_hints, overload
-from typing_extensions import override
+from typing_extensions import Self, TypeIs, override
from strawchemy.utils.annotation import get_annotations
@@ -23,15 +23,17 @@
"DTOScope",
"DTOSkip",
"DTOUnset",
- "ExcludeFields",
+ "FieldIterable",
"IncludeFields",
"Purpose",
"PurposeConfig",
+ "cast_include_fields",
+ "is_fields_iterable",
)
DTOScope: TypeAlias = Literal["global", "dto"]
-IncludeFields: TypeAlias = "list[str] | set[str] | Literal['all']"
-ExcludeFields: TypeAlias = "list[str] | set[str]"
+FieldIterable: TypeAlias = "list[str] | set[str] | frozenset[str] | tuple[str, ...]"
+IncludeFields: TypeAlias = "FieldIterable | Literal['all']"
@final
@@ -160,8 +162,12 @@ class DTOConfig:
"""Configure the DTO for "read" or "write" operations."""
include: IncludeFields = field(default_factory=set)
"""Explicitly include fields from the generated DTO."""
- exclude: ExcludeFields = field(default_factory=set)
+ global_include: IncludeFields = field(default_factory=set)
+ """Explicitly include fields from the generated DTO and all its children."""
+ exclude: FieldIterable = field(default_factory=set)
"""Explicitly exclude fields from the generated DTO. Implies `include="all"`."""
+ global_exclude: FieldIterable = field(default_factory=set)
+ """Explicitly exclude fields from the generated DTO and all its children. Implies `global_include="all"`."""
partial: bool | None = None
"""Make all field optional."""
partial_default: Any = None
@@ -182,14 +188,44 @@ def __post_init__(self) -> None:
if self.include and self.include != "all" and self.exclude:
msg = "When using `exclude` you must set `include='all' or leave it unset`"
raise ValueError(msg)
+ if self.global_include and self.global_include != "all" and self.global_exclude:
+ msg = "When using `global_exclude` you must set `global_include='all' or leave it unset`"
+ raise ValueError(msg)
+ if self.global_exclude:
+ self.global_include = "all"
if self.exclude:
self.include = "all"
+ @classmethod
+ def from_include(cls, include: IncludeFields | None = None, purpose: Purpose = Purpose.READ) -> Self:
+ """Create a DTOConfig from an include specification.
+
+ Factory method for creating a DTOConfig with a simplified interface, converting
+ an `IncludeFields` specification into a complete configuration object. This is
+ useful for building configs when only the include/exclude specification matters.
+
+ Args:
+ include: The field inclusion specification. Can be:
+ - None: Include no fields (converted to empty set)
+ - "all": Include all fields
+ - list or set of field names: Include only these specific fields
+ Defaults to None.
+ purpose: The purpose of the DTO being configured (READ, WRITE, or COMPLETE).
+ Defaults to Purpose.READ.
+
+ Returns:
+ A new DTOConfig instance with the specified include and purpose settings.
+ All other configuration parameters use their defaults.
+ """
+ return cls(purpose, include=set() if include is None else include)
+
def copy_with(
self,
purpose: Purpose | type[DTOUnset] = DTOUnset,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ global_include: IncludeFields | None = None,
+ exclude: FieldIterable | None = None,
+ global_exclude: FieldIterable | None = None,
partial: bool | None | type[DTOUnset] = DTOUnset,
unset_sentinel: Any | type[DTOUnset] = DTOUnset,
type_overrides: Mapping[Any, Any] | type[DTOUnset] = DTOUnset,
@@ -206,11 +242,18 @@ def copy_with(
if include is None and exclude is None:
include, exclude = self.include, self.exclude
else:
- include = include or []
- exclude = exclude or []
+ include = include or set()
+ exclude = exclude or set()
+ if global_include is None and global_exclude is None:
+ global_include, global_exclude = self.global_include, self.global_exclude
+ else:
+ global_include = global_include or set()
+ global_exclude = global_exclude or set()
return DTOConfig(
include=include,
exclude=exclude,
+ global_include=global_include,
+ global_exclude=global_exclude,
purpose=self.purpose if purpose is DTOUnset else purpose,
partial=self.partial if partial is DTOUnset else partial,
unset_sentinel=self.unset_sentinel if unset_sentinel is DTOUnset else unset_sentinel,
@@ -265,3 +308,52 @@ def alias(self, name: str) -> str | None:
if self.alias_generator is not None:
return self.alias_generator(name)
return None
+
+ def is_field_included(self, name: str) -> bool:
+ """Check if a field should be included based on this configuration.
+
+ This method is used during DTO factory operations to determine which fields
+ from the source model should be included in the generated DTO.
+
+ Args:
+ name: The field name to check for inclusion.
+
+ Returns:
+ True if the field should be included based on the include/exclude rules,
+ False otherwise.
+ """
+ if self.include == "all":
+ return name not in self.exclude
+ if self.global_include == "all":
+ return name not in self.global_exclude
+
+ included = set(self.include) | set(self.global_include)
+ excluded = set(self.exclude) | set(self.global_exclude)
+ return name in included and name not in excluded
+
+
+@overload
+def cast_include_fields(value: Literal["all"]) -> Literal["all"]: ...
+
+
+@overload
+def cast_include_fields(value: frozenset[str] | set[str] | list[str] | tuple[str, ...] | None) -> frozenset[str]: ...
+
+
+def cast_include_fields(value: IncludeFields | None) -> frozenset[str] | Literal["all"]:
+ match value:
+ case None:
+ return frozenset()
+ case "all":
+ return "all"
+ case _:
+ return frozenset(value)
+
+
+def is_fields_iterable(value: Any) -> TypeIs[IncludeFields | FieldIterable | None]:
+ """Test the given value is suitable to be used as either `include` or `exclude` in a DTOConfig."""
+ if value == "all" or value is None:
+ return True
+ if isinstance(value, str):
+ return False
+ return isinstance(value, (frozenset, set, list, tuple))
diff --git a/src/strawchemy/dto/utils.py b/src/strawchemy/dto/utils.py
index cd53c789..af815036 100644
--- a/src/strawchemy/dto/utils.py
+++ b/src/strawchemy/dto/utils.py
@@ -19,7 +19,7 @@
DTOConfig,
DTOFieldConfig,
DTOScope,
- ExcludeFields,
+ FieldIterable,
IncludeFields,
Purpose,
PurposeConfig,
@@ -45,7 +45,9 @@
def config(
purpose: Purpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
+ global_include: IncludeFields | None = None,
+ global_exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -58,6 +60,10 @@ def config(
config.exclude = exclude
if include:
config.include = include
+ if global_include:
+ config.global_include = global_include
+ if global_exclude:
+ config.global_exclude = global_exclude
if type_map:
config.type_overrides = type_map
if aliases:
diff --git a/src/strawchemy/mapper.py b/src/strawchemy/mapper.py
index 5769cded..928c2ed1 100644
--- a/src/strawchemy/mapper.py
+++ b/src/strawchemy/mapper.py
@@ -2,7 +2,7 @@
import dataclasses
from functools import cached_property, partial
-from typing import TYPE_CHECKING, Any, TypeVar, overload
+from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
from strawberry.annotation import StrawberryAnnotation
from strawberry.schema.config import StrawberryConfig
@@ -25,7 +25,7 @@
UpsertConflictFieldsEnumDTOBackend,
)
from strawchemy.schema.field import StrawchemyField
-from strawchemy.schema.mutation import types
+from strawchemy.schema.mutation import types as mutation_types
from strawchemy.schema.mutation.field_builder import MutationFieldBuilder
from strawchemy.schema.mutation.fields import (
StrawchemyCreateMutationField,
@@ -33,7 +33,6 @@
StrawchemyUpdateMutationField,
StrawchemyUpsertMutationField,
)
-from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.utils.registry import StrawberryRegistry
if TYPE_CHECKING:
@@ -44,7 +43,9 @@
from strawberry.extensions.field_extension import FieldExtension
from strawberry.types.arguments import StrawberryArgument
+ from strawchemy.dto.types import IncludeFields
from strawchemy.repository.typing import QueryHookCallable
+ from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.transpiler.hook import QueryHook
from strawchemy.typing import AnyRepositoryType, FilterStatementCallable, MappedGraphQLDTO, SupportedDialect
from strawchemy.validation.base import ValidationProtocol
@@ -53,7 +54,6 @@
T = TypeVar("T", bound="DeclarativeBase")
-_TYPES_NS = TYPING_NS | vars(types)
__all__ = ("Strawchemy",)
@@ -83,6 +83,8 @@ class Strawchemy:
pydantic (PydanticMapper): A mapper for generating Pydantic models.
"""
+ _types_namespace: ClassVar[dict[str, Any]] = TYPING_NS | vars(mutation_types)
+
def __init__(
self,
config: StrawchemyConfig | SupportedDialect,
@@ -134,8 +136,13 @@ def __init__(
self.aggregate = partial(self._aggregation_factory.type, mode="aggregate_type")
self.upsert_update_fields = self._enum_factory.input
self.upsert_conflict_fields = self._upsert_conflict_factory.input
- # Initialize mutation field builder
- self._mutation_builder = MutationFieldBuilder(self.config, self._annotation_namespace)
+ self._mutation_builder = MutationFieldBuilder(
+ config=self.config,
+ registry_namespace_getter=self._annotation_namespace,
+ order_by_factory=self._order_by_factory,
+ filter_factory=self._filter_factory,
+ distinct_on_factory=self._distinct_on_enum_factory,
+ )
# Register common types
self.registry.register_enum(OrderByEnum, "OrderByEnum")
@@ -147,7 +154,7 @@ def _annotation_namespace(self) -> dict[str, Any]:
Returns:
A dictionary representing the annotation namespace.
"""
- return self.registry.namespace("object") | _TYPES_NS
+ return self.registry.namespace("object") | self._types_namespace
@cached_property
def pydantic(self) -> PydanticMapper:
@@ -168,10 +175,10 @@ def field(
self,
resolver: Any,
*,
- filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
- distinct_on: type[EnumDTO] | None = None,
+ filter_input: type[BooleanFilterDTO] | bool | None = None,
+ order_by: IncludeFields | type[OrderByDTO] | None = None,
pagination: bool | DefaultOffsetPagination | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | None = None,
arguments: list[StrawberryArgument] | None = None,
id_field_name: str | None = None,
root_aggregations: bool = False,
@@ -196,10 +203,10 @@ def field(
def field(
self,
*,
- filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
- distinct_on: type[EnumDTO] | None = None,
+ filter_input: type[BooleanFilterDTO] | bool | None = None,
+ order_by: IncludeFields | type[OrderByDTO] | None = None,
pagination: bool | DefaultOffsetPagination | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | None = None,
arguments: list[StrawberryArgument] | None = None,
id_field_name: str | None = None,
root_aggregations: bool = False,
@@ -224,10 +231,10 @@ def field(
self,
resolver: Any | None = None,
*,
- filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
- distinct_on: type[EnumDTO] | None = None,
+ filter_input: type[BooleanFilterDTO] | bool | None = None,
+ order_by: IncludeFields | type[OrderByDTO] | None = None,
pagination: bool | DefaultOffsetPagination | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | None = None,
arguments: list[StrawberryArgument] | None = None,
id_field_name: str | None = None,
root_aggregations: bool = False,
@@ -285,21 +292,13 @@ def field(
"""
namespace = self._annotation_namespace()
type_annotation = StrawberryAnnotation.from_annotation(graphql_type, namespace) if graphql_type else None
- repository_type_ = repository_type if repository_type is not None else self.config.repository_type
- execution_options_ = execution_options if execution_options is not None else self.config.execution_options
- pagination = (
- DefaultOffsetPagination(limit=self.config.pagination_default_limit) if pagination is True else pagination
- )
- if pagination is None:
- pagination = self.config.pagination
- id_field_name = id_field_name or self.config.default_id_field_name
field = StrawchemyField(
config=self.config,
- repository_type=repository_type_,
+ repository_type=repository_type,
root_field=root_field,
filter_statement=filter_statement,
- execution_options=execution_options_,
+ execution_options=execution_options,
filter_type=filter_input,
order_by=order_by,
pagination=pagination,
@@ -321,6 +320,9 @@ def field(
registry_namespace=namespace,
description=description,
arguments=arguments,
+ order_by_factory=self._order_by_factory,
+ filter_factory=self._filter_factory,
+ distinct_on_factory=self._distinct_on_enum_factory,
)
return field(resolver) if resolver else field
diff --git a/src/strawchemy/schema/factories/aggregations.py b/src/strawchemy/schema/factories/aggregations.py
index a53103a0..ea818784 100644
--- a/src/strawchemy/schema/factories/aggregations.py
+++ b/src/strawchemy/schema/factories/aggregations.py
@@ -123,6 +123,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
function: FunctionInfo | None = None,
**kwargs: Any,
@@ -137,6 +138,7 @@ def factory(
raise_if_no_fields,
tags,
backend_kwargs,
+ no_cache,
function=function,
**kwargs,
)
diff --git a/src/strawchemy/schema/factories/base.py b/src/strawchemy/schema/factories/base.py
index 8fb7bd99..e89ff2d7 100644
--- a/src/strawchemy/schema/factories/base.py
+++ b/src/strawchemy/schema/factories/base.py
@@ -16,6 +16,7 @@
import dataclasses
from functools import cached_property
+from inspect import isclass
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, TypeVar, get_type_hints
from sqlalchemy.orm import DeclarativeBase, QueryableAttribute
@@ -29,17 +30,17 @@
from strawchemy.dto.strawberry import (
BooleanFilterDTO,
DTOKey,
+ EnumDTO,
GraphQLFieldDefinition,
MappedStrawberryGraphQLDTO,
OrderByDTO,
StrawchemyDTOAttributes,
UnmappedStrawberryGraphQLDTO,
)
-from strawchemy.dto.types import DTOAuto, DTOScope, Purpose
+from strawchemy.dto.types import DTOAuto, DTOScope, Purpose, cast_include_fields, is_fields_iterable
from strawchemy.dto.utils import config
from strawchemy.exceptions import StrawchemyError
from strawchemy.instance import MapperModelInstance
-from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.transpiler import hook
from strawchemy.typing import GraphQLDTOT, GraphQLPurpose, GraphQLType, MappedGraphQLDTO
from strawchemy.utils.annotation import get_annotations
@@ -50,7 +51,8 @@
from strawchemy import Strawchemy
from strawchemy.dto.inspectors import SQLAlchemyGraphQLInspector
- from strawchemy.dto.types import DTOConfig, ExcludeFields, IncludeFields
+ from strawchemy.dto.types import DTOConfig, FieldIterable, IncludeFields
+ from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.transpiler.hook import QueryHook
from strawchemy.utils.graph import Node
from strawchemy.validation.pydantic import MappedPydanticGraphQLDTO
@@ -97,9 +99,11 @@ def _type_info(
current_node: Node[Relation[Any, GraphQLDTOT], None] | None,
override: bool = False,
user_defined: bool = False,
- child_options: ChildOptions | None = None,
+ paginate: IncludeFields | None = None,
+ order: IncludeFields | type[OrderByDTO] | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | None = None,
+ default_pagination: DefaultOffsetPagination | None = None,
) -> RegistryTypeInfo:
- child_options = child_options or ChildOptions()
graphql_type = self.graphql_type(dto_config)
model: type[DeclarativeBase] | None = dto.__dto_model__ if issubclass(dto, MappedStrawberryGraphQLDTO) else None # type: ignore[reportGeneralTypeIssues]
default_name = self.root_dto_name(model, dto_config, current_node) if model else dto.__name__
@@ -109,8 +113,10 @@ def _type_info(
graphql_type=graphql_type,
override=override,
user_defined=user_defined,
- pagination=DefaultOffsetPagination() if child_options.pagination is True else child_options.pagination,
- order_by=child_options.order_by,
+ pagination=default_pagination,
+ order=cast_include_fields(order) if is_fields_iterable(order) else order,
+ distinct_on=cast_include_fields(distinct_on) if is_fields_iterable(distinct_on) else distinct_on,
+ paginate=cast_include_fields(paginate),
scope=dto_config.scope,
model=model,
exclude_from_scope=dto_config.exclude_from_scope,
@@ -130,14 +136,20 @@ def _register_type(
directives: Sequence[object] | None = (),
override: bool = False,
user_defined: bool = False,
- child_options: ChildOptions | None = None,
+ order: IncludeFields | type[OrderByDTO] | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | None = None,
+ paginate: IncludeFields | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
) -> type[StrawchemyDTOT]:
type_info = self._type_info(
dto,
dto_config,
override=override,
user_defined=user_defined,
- child_options=child_options,
+ order=order,
+ distinct_on=distinct_on,
+ paginate=paginate,
+ default_pagination=default_pagination,
current_node=current_node,
)
self._raise_if_type_conflicts(type_info)
@@ -181,7 +193,7 @@ def _config(
self,
purpose: Purpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -193,6 +205,8 @@ def _config(
purpose,
include=include,
exclude=exclude,
+ global_include=self._mapper.config.include,
+ global_exclude=self._mapper.config.exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
@@ -207,15 +221,15 @@ def _type_wrapper(
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
- child_pagination: bool | DefaultOffsetPagination = False,
- child_order_by: bool = False,
+ paginate: IncludeFields | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
+ order: IncludeFields | type[OrderByDTO] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
@@ -229,6 +243,8 @@ def wrapper(class_: type[Any]) -> type[GraphQLDTOT]:
purpose,
include=include,
exclude=exclude,
+ global_include=self._mapper.config.include,
+ global_exclude=self._mapper.config.exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
@@ -247,12 +263,16 @@ def wrapper(class_: type[Any]) -> type[GraphQLDTOT]:
override=override,
user_defined=True,
mode=mode,
- child_options=ChildOptions(pagination=child_pagination, order_by=child_order_by),
+ paginate=self._mapper.config.pagination if paginate is None else paginate,
+ order=self._mapper.config.order_by if order is None else order,
+ default_pagination=default_pagination,
)
dto.__strawchemy_query_hook__ = query_hook
if issubclass(dto, MappedStrawberryGraphQLDTO):
- dto.__strawchemy_filter__ = filter_input
- dto.__strawchemy_order_by__ = order_by
+ if isclass(order) and issubclass(order, OrderByDTO): # pyright: ignore[reportUnnecessaryIsInstance]
+ dto.__strawchemy_order_by__ = order
+ if isclass(filter_input) and issubclass(filter_input, BooleanFilterDTO): # pyright: ignore[reportUnnecessaryIsInstance]
+ dto.__strawchemy_filter__ = filter_input
dto.__strawchemy_purpose__ = mode
return dto
@@ -264,7 +284,7 @@ def _input_wrapper(
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -278,34 +298,79 @@ def _input_wrapper(
**kwargs: Any,
) -> Callable[[type[Any]], type[GraphQLDTOT]]:
def wrapper(class_: type[Any]) -> type[GraphQLDTOT]:
- dto_config = self._config(
- purpose,
+ return self.make_input(
+ model=model,
+ mode=mode,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
- alias_generator=alias_generator,
aliases=aliases,
- scope=scope,
- tags={mode},
- )
- dto = self.factory(
- model=model,
- dto_config=dto_config,
- base=class_,
+ alias_generator=alias_generator,
name=name,
description=description,
directives=directives,
override=override,
- user_defined=True,
- mode=mode,
+ purpose=purpose,
+ scope=scope,
+ base=class_,
**kwargs,
)
- dto.__strawchemy_purpose__ = mode
- return dto
return wrapper
+ def make_input(
+ self,
+ model: type[T],
+ *,
+ mode: GraphQLPurpose,
+ include: IncludeFields | None = None,
+ exclude: FieldIterable | None = None,
+ partial: bool | None = None,
+ type_map: Mapping[Any, Any] | None = None,
+ aliases: Mapping[str, str] | None = None,
+ alias_generator: Callable[[str], str] | None = None,
+ name: str | None = None,
+ description: str | None = None,
+ directives: Sequence[object] | None = (),
+ override: bool = False,
+ purpose: Purpose = Purpose.WRITE,
+ scope: DTOScope | None = None,
+ base: type[Any] | None = None,
+ user_defined: bool = False,
+ no_cache: bool = False,
+ **kwargs: Any,
+ ) -> type[GraphQLDTOT]:
+ dto_config = self._config(
+ purpose,
+ include=include,
+ exclude=exclude,
+ partial=partial,
+ type_map=type_map,
+ alias_generator=alias_generator,
+ aliases=aliases,
+ scope=scope,
+ tags={mode},
+ )
+ if no_cache:
+ dto_name = name or self.root_dto_name(model, dto_config)
+ name = self._mapper.registry.uniquify_name(self.graphql_type(dto_config), dto_name)
+ dto = self.factory(
+ model=model,
+ dto_config=dto_config,
+ base=base,
+ name=name,
+ description=description,
+ directives=directives,
+ override=override,
+ user_defined=user_defined,
+ mode=mode,
+ no_cache=no_cache,
+ **kwargs,
+ )
+ dto.__strawchemy_purpose__ = mode
+ return dto
+
@cached_property
def _namespace(self) -> dict[str, Any]:
return vars(strawchemy_typing) | vars(hook)
@@ -353,6 +418,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
description: str | None = None,
directives: Sequence[object] | None = (),
@@ -376,6 +442,7 @@ def factory(
tags,
backend_kwargs=backend_kwargs,
field_map=field_map,
+ no_cache=no_cache,
**kwargs,
)
if not dto.__strawchemy_field_map__:
@@ -431,15 +498,15 @@ def type(
model: type[T],
*,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
- child_pagination: bool | DefaultOffsetPagination = False,
- child_order_by: bool = False,
+ paginate: IncludeFields | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
+ order: IncludeFields | type[OrderByDTO] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
@@ -457,10 +524,10 @@ def type(
type_map=type_map,
aliases=aliases,
alias_generator=alias_generator,
- child_pagination=child_pagination,
- child_order_by=child_order_by,
+ paginate=paginate,
+ default_pagination=default_pagination,
filter_input=filter_input,
- order_by=order_by,
+ order=order,
name=name,
description=description,
directives=directives,
@@ -478,7 +545,7 @@ def input(
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -521,6 +588,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
mode: GraphQLPurpose | None = None,
**kwargs: Any,
@@ -549,7 +617,7 @@ def input(
model: type[T],
*,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -582,15 +650,15 @@ def type(
self,
model: type[T],
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
- child_pagination: bool | DefaultOffsetPagination = False,
- child_order_by: bool = False,
+ paginate: IncludeFields | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
filter_input: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
+ order_by: IncludeFields | type[OrderByDTO] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
@@ -607,10 +675,10 @@ def type(
type_map=type_map,
aliases=aliases,
alias_generator=alias_generator,
- child_pagination=child_pagination,
- child_order_by=child_order_by,
+ paginate=paginate,
+ default_pagination=default_pagination,
filter_input=filter_input,
- order_by=order_by,
+ order=order_by,
name=name,
description=description,
directives=directives,
diff --git a/src/strawchemy/schema/factories/enum.py b/src/strawchemy/schema/factories/enum.py
index 05d445ab..da30b546 100644
--- a/src/strawchemy/schema/factories/enum.py
+++ b/src/strawchemy/schema/factories/enum.py
@@ -8,9 +8,10 @@
from sqlalchemy.orm import DeclarativeBase, QueryableAttribute
from typing_extensions import override
+from strawchemy.dto import config
from strawchemy.dto.base import DTOBackend, DTOBase, DTOFactory, DTOFieldDefinition, Relation
from strawchemy.dto.strawberry import EnumDTO, GraphQLFieldDefinition
-from strawchemy.dto.types import DTOConfig, ExcludeFields, IncludeFields, Purpose
+from strawchemy.dto.types import DTOConfig, FieldIterable, IncludeFields, Purpose
from strawchemy.utils.text import snake_to_lower_camel_case
if TYPE_CHECKING:
@@ -141,7 +142,7 @@ def decorator(
model: type[DeclarativeBase],
purpose: Purpose = Purpose.READ,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -160,11 +161,42 @@ def decorator(
**kwargs,
)
+ def make_input(
+ self,
+ model: type[DeclarativeBase],
+ include: IncludeFields | None = None,
+ exclude: FieldIterable | None = None,
+ partial: bool | None = None,
+ type_map: Mapping[Any, Any] | None = None,
+ aliases: Mapping[str, str] | None = None,
+ alias_generator: Callable[[str], str] | None = None,
+ base: type[Any] | None = None,
+ name: str | None = None,
+ no_cache: bool = False,
+ **kwargs: Any,
+ ) -> type[EnumDTO]:
+ return self.factory(
+ model=model,
+ dto_config=config(
+ purpose=Purpose.WRITE,
+ include=include,
+ exclude=exclude,
+ partial=partial,
+ type_map=type_map,
+ aliases=aliases,
+ alias_generator=alias_generator,
+ ),
+ base=base,
+ name=name,
+ no_cache=no_cache,
+ **kwargs,
+ )
+
def input(
self,
model: type[DeclarativeBase],
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
diff --git a/src/strawchemy/schema/factories/inputs.py b/src/strawchemy/schema/factories/inputs.py
index 02869d39..351a8a3e 100644
--- a/src/strawchemy/schema/factories/inputs.py
+++ b/src/strawchemy/schema/factories/inputs.py
@@ -32,7 +32,7 @@
from strawchemy import Strawchemy
from strawchemy.dto.base import DTOBackend, DTOBase, DTOFieldDefinition, ModelFieldT, Relation
- from strawchemy.dto.types import ExcludeFields, IncludeFields
+ from strawchemy.dto.types import FieldIterable, IncludeFields
from strawchemy.repository.typing import DeclarativeT
from strawchemy.schema.filters import GraphQLFilter
from strawchemy.utils.graph import Node
@@ -52,7 +52,7 @@ def input(
model: type[DeclarativeT],
*,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -143,7 +143,9 @@ def iter_field_definitions(
if field.uselist and field.related_dto:
field.type_ = Union[field.related_dto, None]
if aggregate_filters:
- aggregation_field = self._aggregation_field(field, dto_config.copy_with(partial_default=UNSET))
+ aggregation_field = self._aggregation_field(
+ field, dto_config.copy_with(partial_default=UNSET, partial=True)
+ )
field_map[key + aggregation_field.name] = aggregation_field
yield aggregation_field
else:
@@ -172,6 +174,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
aggregate_filters: bool = True,
**kwargs: Any,
@@ -452,6 +455,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
aggregate_filters: bool = True,
**kwargs: Any,
@@ -466,6 +470,7 @@ def factory(
raise_if_no_fields,
tags,
backend_kwargs,
+ no_cache,
aggregate_filters=aggregate_filters,
**kwargs,
)
diff --git a/src/strawchemy/schema/factories/types.py b/src/strawchemy/schema/factories/types.py
index 861325bd..e1a04dd3 100644
--- a/src/strawchemy/schema/factories/types.py
+++ b/src/strawchemy/schema/factories/types.py
@@ -19,13 +19,13 @@
FunctionFieldDefinition,
GraphQLFieldDefinition,
MappedStrawberryGraphQLDTO,
+ OrderByDTO,
)
-from strawchemy.dto.types import DTOConfig, DTOMissing, Purpose
-from strawchemy.dto.utils import read_all_partial_config, read_partial, write_all_config
+from strawchemy.dto.types import DTOConfig, DTOMissing, IncludeFields, Purpose, is_fields_iterable
+from strawchemy.dto.utils import read_partial, write_all_config
from strawchemy.exceptions import EmptyDTOError
from strawchemy.schema.factories import (
AggregationInspector,
- ChildOptions,
EnumDTOFactory,
GraphQLDTOFactory,
MappedGraphQLDTOT,
@@ -104,27 +104,79 @@ def _aggregation_field(
related_dto=dto,
)
- def _update_fields(
+ def _add_fields_arguments(
self,
dto: type[GraphQLDTOT],
base: type[Any] | None,
- pagination: bool | DefaultOffsetPagination = False,
- order_by: bool = False,
+ order: IncludeFields | None = None,
+ paginate: IncludeFields | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
) -> type[GraphQLDTOT]:
+ """Add pagination and ordering arguments to a GraphQL DTO type.
+
+ Enhances a GraphQL Data Transfer Object (DTO) type with pagination and ordering
+ arguments for relation fields and path filtering for JSON fields. This is a
+ post-processing step that modifies the DTO type after initial generation to
+ add query capabilities.
+
+ For each relation field with `uselist=True` (one-to-many relationships):
+ - If included in the `order` specification: adds an order_by argument
+ - If included in the `paginate` specification: adds pagination configuration
+
+ For each JSON field:
+ - Adds a `json_path` argument for path-based filtering
+
+ Args:
+ dto: The GraphQL DTO type to enhance. Must be a generated strawberry type.
+ base: Optional base class whose annotations should be merged into the DTO.
+ If provided, annotations from the base class are added to the final DTO.
+ order: Field inclusion specification for ordering arguments. Can be:
+ - None: No ordering arguments added (default)
+ - "all": Add order_by arguments to all relation fields
+ - list/set of field names: Add order_by arguments only to named relations
+ paginate: Field inclusion specification for pagination arguments. Can be:
+ - None: No pagination arguments added (default)
+ - "all": Add pagination to all relation fields
+ - list/set of field names: Add pagination only to named relations
+ default_pagination: Default pagination configuration to apply when
+ paginate is enabled. If None, uses default pagination (True).
+
+ Returns:
+ The modified DTO type with updated __annotations__ and attributes
+ containing the new pagination and ordering arguments.
+ """
attributes: dict[str, Any] = {}
annotations: dict[str, Any] = {}
+ order_config = DTOConfig.from_include(order)
+ paginate_config = DTOConfig.from_include(paginate)
for field in dto.__strawchemy_field_map__.values():
+ # Add pagination and ordering arguments for relations
if field.is_relation and field.uselist:
related = Self if field.related_dto is dto else field.related_dto
type_annotation = list[related] if related is not None else field.type_
assert field.related_model
- order_by_input = None
- if order_by:
- order_by_input = self._order_by_factory.factory(field.related_model, read_all_partial_config)
+ order_by_input, pagination = None, False
+ if order_config.is_field_included(field.model_field_name) or self._mapper.config.order_by:
+ try:
+ order_by_input = self._order_by_factory.factory(
+ field.related_model,
+ DTOConfig(
+ Purpose.READ,
+ partial=True,
+ include=self._mapper.config.order_by or "all",
+ global_include=self._mapper.config.order_by or "all",
+ ),
+ raise_if_no_fields=True,
+ )
+ except EmptyDTOError:
+ order_by_input = None
+ if paginate_config.is_field_included(field.model_field_name):
+ pagination = default_pagination or True
strawberry_field = self._mapper.field(pagination=pagination, order_by=order_by_input, root_field=False)
attributes[field.name] = strawberry_field
annotations[field.name] = type_annotation
+ # Add path filtering argument for JSON fields
elif (
not field.is_relation
and field.has_model_field
@@ -152,18 +204,6 @@ def _update_fields(
setattr(dto, name, value)
return dto
- @override
- def _cache_key(
- self,
- model: type[Any],
- dto_config: DTOConfig,
- node: Node[Relation[Any, MappedGraphQLDTOT], None],
- *,
- child_options: ChildOptions,
- **factory_kwargs: Any,
- ) -> Hashable:
- return (super()._cache_key(model, dto_config, node, **factory_kwargs), child_options)
-
@override
def dto_name(
self, base_name: str, dto_config: DTOConfig, node: Node[Relation[Any, MappedGraphQLDTOT], None] | None = None
@@ -207,8 +247,11 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
- child_options: ChildOptions | None = None,
+ default_pagination: None | DefaultOffsetPagination = None,
+ order: IncludeFields | type[OrderByDTO] | None = None,
+ paginate: IncludeFields | None = None,
aggregations: bool = True,
description: str | None = None,
directives: Sequence[object] | None = (),
@@ -230,12 +273,19 @@ def factory(
aggregations=aggregations if dto_config.purpose is Purpose.READ else False,
register_type=False,
override=override,
- child_options=child_options,
+ paginate=paginate if paginate == "all" else self._mapper.config.pagination,
+ order=order if order == "all" else self._mapper.config.order_by,
+ default_pagination=default_pagination,
**kwargs,
)
- child_options = child_options or ChildOptions()
if self.graphql_type(dto_config) == "object":
- dto = self._update_fields(dto, base, pagination=child_options.pagination, order_by=child_options.order_by)
+ dto = self._add_fields_arguments(
+ dto,
+ base,
+ default_pagination=default_pagination,
+ order=order if is_fields_iterable(order) else None,
+ paginate=paginate,
+ )
if register_type:
return self._register_type(
dto,
@@ -244,7 +294,9 @@ def factory(
directives=directives,
override=override,
user_defined=user_defined,
- child_options=child_options,
+ default_pagination=default_pagination,
+ order=order,
+ paginate=paginate,
current_node=current_node,
)
return dto
@@ -322,6 +374,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
aggregations: bool = True,
**kwargs: Any,
@@ -558,12 +611,11 @@ def _cache_key(
dto_config: DTOConfig,
node: Node[Relation[Any, MappedGraphQLDTOT], None],
*,
- child_options: ChildOptions,
mode: GraphQLPurpose,
**factory_kwargs: Any,
) -> Hashable:
return (
- super()._cache_key(model, dto_config, node, child_options=child_options, **factory_kwargs),
+ super()._cache_key(model, dto_config, node, **factory_kwargs),
node.root.value.model,
mode,
)
@@ -666,6 +718,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
description: str | None = None,
mode: GraphQLPurpose,
@@ -683,5 +736,6 @@ def factory(
backend_kwargs=backend_kwargs,
description=description or self._description(mode),
mode=mode,
+ no_cache=no_cache,
**kwargs,
)
diff --git a/src/strawchemy/schema/field.py b/src/strawchemy/schema/field.py
index 32a21f04..bd5b9e5a 100644
--- a/src/strawchemy/schema/field.py
+++ b/src/strawchemy/schema/field.py
@@ -14,8 +14,14 @@
from strawchemy.constants import DISTINCT_ON_KEY, FILTER_KEY, LIMIT_KEY, NODES_KEY, OFFSET_KEY, ORDER_BY_KEY
from strawchemy.dto.base import MappedDTO
-from strawchemy.dto.strawberry import MappedStrawberryGraphQLDTO, StrawchemyDTOAttributes
-from strawchemy.dto.types import DTOConfig, Purpose
+from strawchemy.dto.strawberry import (
+ BooleanFilterDTO,
+ EnumDTO,
+ MappedStrawberryGraphQLDTO,
+ OrderByDTO,
+ StrawchemyDTOAttributes,
+)
+from strawchemy.dto.types import DTOConfig, IncludeFields, Purpose
from strawchemy.exceptions import StrawchemyFieldError
from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.utils.annotation import is_type_hint_optional
@@ -37,10 +43,11 @@
from strawberry.types.fields.resolver import StrawberryResolver
from strawchemy import StrawchemyConfig
- from strawchemy.dto.strawberry import BooleanFilterDTO, EnumDTO, OrderByDTO
from strawchemy.repository.strawberry import StrawchemyAsyncRepository, StrawchemySyncRepository
from strawchemy.repository.strawberry.base import GraphQLResult
from strawchemy.repository.typing import QueryHookCallable
+ from strawchemy.schema.factories import DistinctOnFieldsDTOFactory
+ from strawchemy.schema.factories.inputs import BooleanFilterDTOFactory, OrderByDTOFactory
from strawchemy.typing import (
AnyRepository,
AnyRepositoryType,
@@ -74,17 +81,20 @@ class StrawchemyField(StrawberryField):
def __init__(
self,
config: StrawchemyConfig,
- repository_type: AnyRepositoryType,
- filter_type: type[BooleanFilterDTO] | None = None,
- order_by: type[OrderByDTO] | None = None,
- distinct_on: type[EnumDTO] | None = None,
- pagination: bool | DefaultOffsetPagination = False,
+ order_by_factory: OrderByDTOFactory,
+ filter_factory: BooleanFilterDTOFactory,
+ distinct_on_factory: DistinctOnFieldsDTOFactory,
+ filter_type: type[BooleanFilterDTO] | bool | None = None,
+ order_by: IncludeFields | type[OrderByDTO] | Literal[False] | None = None,
+ distinct_on: IncludeFields | type[EnumDTO] | Literal[False] | None = None,
+ pagination: DefaultOffsetPagination | bool | None = False,
+ repository_type: AnyRepositoryType | None = None,
root_aggregations: bool = False,
registry_namespace: dict[str, Any] | None = None,
filter_statement: FilterStatementCallable | None = None,
query_hook: QueryHookCallable[Any] | Sequence[QueryHookCallable[Any]] | None = None,
execution_options: dict[str, Any] | None = None,
- id_field_name: str = "id",
+ id_field_name: str | None = None,
arguments: list[StrawberryArgument] | None = None,
# Original StrawberryField args
python_name: str | None = None,
@@ -107,20 +117,22 @@ def __init__(
self.registry_namespace = registry_namespace
self.is_root_field = root_field
self.root_aggregations = root_aggregations
- self.distinct_on = distinct_on
self.query_hook = query_hook
- self.pagination: DefaultOffsetPagination | Literal[False] = (
- DefaultOffsetPagination() if pagination is True else pagination
- )
- self.id_field_name = id_field_name
+ self.id_field_name = config.default_id_field_name if id_field_name is None else id_field_name
+
+ self._pagination = pagination
+ self._distinct_on = distinct_on
self._filter = filter_type
self._order_by = order_by
self._description = description
self._filter_statement = filter_statement
- self._execution_options = execution_options
+ self._execution_options = config.execution_options if execution_options is None else execution_options
self._config = config
- self._repository_type = repository_type
+ self._repository_type = config.repository_type if repository_type is None else repository_type
+ self._order_by_factory = order_by_factory
+ self._filter_factory = filter_factory
+ self._distinct_on_factory = distinct_on_factory
super().__init__(
python_name,
@@ -224,18 +236,97 @@ def _is_strawchemy_type(
)
@cached_property
- def filter(self) -> type[BooleanFilterDTO] | None:
+ def pagination(self) -> DefaultOffsetPagination | None:
+ if self._pagination is True or (self._pagination is None and self._config.pagination == "all"):
+ return DefaultOffsetPagination(
+ limit=self._config.pagination_default_limit, offset=self._config.pagination_default_offset
+ )
+
+ return None if not self._pagination else self._pagination
+
+ @cached_property
+ def distinct_on(self) -> type[EnumDTO] | None:
+ if isclass(self._distinct_on) and issubclass(self._distinct_on, EnumDTO): # pyright: ignore[reportUnnecessaryIsInstance]
+ return self._distinct_on
+
inner_type = strawberry_contained_user_type(self.type)
- if self._filter is None and self._is_strawchemy_type(inner_type):
- return inner_type.__strawchemy_filter__
- return self._filter
+ distinct_on = self._config.distinct_on if self._distinct_on is None else self._distinct_on
+
+ if self._is_strawchemy_type(inner_type) and distinct_on:
+ config = inner_type.__dto_config__.copy_with(
+ include=inner_type.__dto_config__.include if distinct_on == "all" else distinct_on
+ )
+
+ return self._distinct_on_factory.make_input(
+ inner_type.__dto_model__, # type: ignore[reportGeneralTypeIssues]
+ include=config.include,
+ exclude=config.exclude,
+ aliases=config.aliases,
+ alias_generator=config.alias_generator,
+ type_map=config.type_overrides,
+ )
+
+ return None
@cached_property
def order_by(self) -> type[OrderByDTO] | None:
+ if isclass(self._order_by) and issubclass(self._order_by, OrderByDTO): # pyright: ignore[reportUnnecessaryIsInstance]
+ return self._order_by
+
inner_type = strawberry_contained_user_type(self.type)
- if self._order_by is None and self._is_strawchemy_type(inner_type):
+ order_by = self._config.order_by if self._order_by is None else self._order_by
+
+ if self._is_strawchemy_type(inner_type) and order_by:
+ config = inner_type.__dto_config__.copy_with(
+ include=inner_type.__dto_config__.include if order_by == "all" else order_by
+ )
+
+ return self._order_by_factory.make_input(
+ inner_type.__dto_model__, # type: ignore[reportGeneralTypeIssues]
+ mode="order_by",
+ include=config.include if order_by == "all" else order_by,
+ exclude=config.exclude,
+ aliases=config.aliases,
+ alias_generator=config.alias_generator,
+ type_map=config.type_overrides,
+ no_cache=True,
+ )
+ if (
+ self._order_by is None
+ and self._is_strawchemy_type(inner_type)
+ and inner_type.__strawchemy_order_by__ is not None
+ ):
return inner_type.__strawchemy_order_by__
- return self._order_by
+
+ return None
+
+ @cached_property
+ def filter(self) -> type[BooleanFilterDTO] | None:
+ if isclass(self._filter) and issubclass(self._filter, BooleanFilterDTO): # pyright: ignore[reportUnnecessaryIsInstance]
+ return self._filter
+
+ inner_type = strawberry_contained_user_type(self.type)
+
+ if self._is_strawchemy_type(inner_type) and self._filter is True:
+ config = inner_type.__dto_config__
+ return self._filter_factory.make_input(
+ inner_type.__dto_model__, # type: ignore[reportGeneralTypeIssues]
+ mode="filter",
+ include=config.include,
+ exclude=config.exclude,
+ aliases=config.aliases,
+ alias_generator=config.alias_generator,
+ type_map=config.type_overrides,
+ no_cache=True,
+ )
+ if (
+ self._filter is None
+ and self._is_strawchemy_type(inner_type)
+ and inner_type.__strawchemy_filter__ is not None
+ ):
+ return inner_type.__strawchemy_filter__
+
+ return None
def auto_arguments(self) -> list[StrawberryArgument]:
arguments: list[StrawberryArgument] = []
@@ -349,11 +440,14 @@ def __copy__(self) -> Self:
root_aggregations=self.root_aggregations,
filter_type=self._filter,
order_by=self._order_by,
- distinct_on=self.distinct_on,
+ distinct_on=self._distinct_on,
pagination=self.pagination,
registry_namespace=self.registry_namespace,
execution_options=self._execution_options,
config=self._config,
+ order_by_factory=self._order_by_factory,
+ filter_factory=self._filter_factory,
+ distinct_on_factory=self._distinct_on_factory,
)
new_field._arguments = self._arguments[:] if self._arguments is not None else None # noqa: SLF001
return new_field
diff --git a/src/strawchemy/schema/mutation/field_builder.py b/src/strawchemy/schema/mutation/field_builder.py
index 72903bb2..f44192f9 100644
--- a/src/strawchemy/schema/mutation/field_builder.py
+++ b/src/strawchemy/schema/mutation/field_builder.py
@@ -15,6 +15,8 @@
from strawberry.extensions.field_extension import FieldExtension
from strawchemy.config.base import StrawchemyConfig
+ from strawchemy.schema.factories import DistinctOnFieldsDTOFactory
+ from strawchemy.schema.factories.inputs import BooleanFilterDTOFactory, OrderByDTOFactory
from strawchemy.schema.mutation.fields import (
StrawchemyCreateMutationField,
StrawchemyDeleteMutationField,
@@ -35,6 +37,9 @@ class MutationFieldBuilder:
config: StrawchemyConfig
registry_namespace_getter: Callable[[], dict[str, Any]]
+ order_by_factory: OrderByDTOFactory
+ filter_factory: BooleanFilterDTOFactory
+ distinct_on_factory: DistinctOnFieldsDTOFactory
def build(
self,
@@ -86,11 +91,10 @@ def build(
"""
namespace = self.registry_namespace_getter()
type_annotation = StrawberryAnnotation.from_annotation(graphql_type, namespace) if graphql_type else None
- repository_type_ = repository_type if repository_type is not None else self.config.repository_type
field = field_class(
config=self.config,
- repository_type=repository_type_,
+ repository_type=repository_type,
python_name=None,
graphql_name=name,
type_annotation=type_annotation,
@@ -104,6 +108,9 @@ def build(
extensions=extensions or [],
registry_namespace=namespace,
description=description,
+ order_by_factory=self.order_by_factory,
+ filter_factory=self.filter_factory,
+ distinct_on_factory=self.distinct_on_factory,
**field_specific_kwargs,
)
return field(resolver) if resolver else field
diff --git a/src/strawchemy/utils/registry.py b/src/strawchemy/utils/registry.py
index ad060ac8..eb68315e 100644
--- a/src/strawchemy/utils/registry.py
+++ b/src/strawchemy/utils/registry.py
@@ -30,6 +30,7 @@
from strawberry.types.arguments import StrawberryArgument
from strawberry.types.base import WithStrawberryObjectDefinition
+ from strawchemy.dto.strawberry import EnumDTO, OrderByDTO
from strawchemy.dto.types import DTOScope
from strawchemy.schema.pagination import DefaultOffsetPagination
from strawchemy.typing import GraphQLType, StrawchemyTypeWithStrawberryObjectDefinition
@@ -101,8 +102,10 @@ class RegistryTypeInfo:
default_name: str | None = None
user_defined: bool = False
override: bool = False
- pagination: DefaultOffsetPagination | Literal[False] = False
- order_by: bool = False
+ pagination: DefaultOffsetPagination | None = None
+ order: frozenset[str] | Literal["all"] | type[OrderByDTO] = dataclasses.field(default_factory=frozenset)
+ distinct_on: frozenset[str] | Literal["all"] | type[EnumDTO] = dataclasses.field(default_factory=frozenset)
+ paginate: frozenset[str] | Literal["all"] = dataclasses.field(default_factory=frozenset)
scope: DTOScope | None = None
model: type[DeclarativeBase] | None = None
tags: frozenset[str] = dataclasses.field(default_factory=frozenset)
@@ -110,7 +113,7 @@ class RegistryTypeInfo:
@property
def scoped_id(self) -> Hashable:
- return (self.model, self.graphql_type, self.tags)
+ return self.model, self.graphql_type, self.tags
class StrawberryRegistry:
@@ -127,6 +130,7 @@ def __init__(self, strawberry_config: StrawberryConfig) -> None:
self._type_map: dict[RegistryTypeInfo, type[Any]] = {}
self._names_map: defaultdict[GraphQLType, dict[str, RegistryTypeInfo]] = defaultdict(dict)
self._tracked_type_names: defaultdict[GraphQLType, set[str]] = defaultdict(set)
+ self._unique_names: defaultdict[str, int] = defaultdict(int)
def _get_field_type_name(
self,
@@ -303,6 +307,13 @@ def name_clash(self, type_info: RegistryTypeInfo) -> bool:
and not type_info.override
)
+ def uniquify_name(self, graphql_type: GraphQLType, name: str) -> str:
+ """Return a type name guaranteed to be unique within the registry."""
+ while self.get(graphql_type, name, None):
+ self._unique_names[name] += 1
+ name = f"{name}{self._unique_names[name]}"
+ return name
+
@overload
def get(self, graphql_type: GraphQLType, name: str, default: _RegistryMissing) -> RegistryTypeInfo: ...
diff --git a/src/strawchemy/validation/pydantic.py b/src/strawchemy/validation/pydantic.py
index 93426ebd..e6261307 100644
--- a/src/strawchemy/validation/pydantic.py
+++ b/src/strawchemy/validation/pydantic.py
@@ -24,7 +24,7 @@
from strawchemy import Strawchemy
from strawchemy.dto.base import DTOFieldDefinition, MappedDTO, Relation
- from strawchemy.dto.types import DTOConfig, ExcludeFields, IncludeFields, Purpose
+ from strawchemy.dto.types import DTOConfig, FieldIterable, IncludeFields, Purpose
from strawchemy.repository.typing import DeclarativeT
from strawchemy.typing import GraphQLPurpose
from strawchemy.utils.graph import Node
@@ -84,7 +84,7 @@ def input(
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
- exclude: ExcludeFields | None = None,
+ exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
@@ -109,6 +109,7 @@ def factory(
raise_if_no_fields: bool = False,
tags: set[str] | None = None,
backend_kwargs: dict[str, Any] | None = None,
+ no_cache: bool = False,
*,
description: str | None = None,
mode: GraphQLPurpose,
@@ -123,6 +124,7 @@ def factory(
current_node,
raise_if_no_fields,
tags,
+ no_cache=no_cache,
backend_kwargs=backend_kwargs,
description=description or f"{mode.capitalize()} validation type",
mode=mode,
diff --git a/tests/integration/types/mysql.py b/tests/integration/types/mysql.py
index 910e6b98..76e1ab32 100644
--- a/tests/integration/types/mysql.py
+++ b/tests/integration/types/mysql.py
@@ -151,7 +151,7 @@ class OrderedFruitType: ...
class FruitAggregationType: ...
-@strawchemy.type(Fruit, include="all", child_pagination=True, child_order_by=True)
+@strawchemy.type(Fruit, include="all", paginate="all", order="all")
class FruitTypeWithPaginationAndOrderBy: ...
@@ -182,7 +182,7 @@ class FruitUpsertConflictFields: ...
# Color
-@strawchemy.type(Color, include="all", override=True, child_order_by=True)
+@strawchemy.type(Color, include="all", override=True, order="all")
class ColorType: ...
@@ -194,7 +194,7 @@ class ColorOrder: ...
class ColorDistinctOn: ...
-@strawchemy.type(Color, include="all", child_pagination=True)
+@strawchemy.type(Color, include="all", paginate="all")
class ColorTypeWithPagination: ...
diff --git a/tests/integration/types/postgres.py b/tests/integration/types/postgres.py
index 12c688a7..774e04df 100644
--- a/tests/integration/types/postgres.py
+++ b/tests/integration/types/postgres.py
@@ -159,7 +159,7 @@ class OrderedFruitType: ...
class FruitAggregationType: ...
-@strawchemy.type(Fruit, include="all", child_pagination=True, child_order_by=True)
+@strawchemy.type(Fruit, include="all", paginate="all", order="all")
class FruitTypeWithPaginationAndOrderBy: ...
@@ -190,7 +190,7 @@ class FruitUpsertConflictFields: ...
# Color
-@strawchemy.type(Color, include="all", override=True, child_order_by=True)
+@strawchemy.type(Color, include="all", override=True, order="all")
class ColorType: ...
@@ -202,7 +202,7 @@ class ColorOrder: ...
class ColorDistinctOn: ...
-@strawchemy.type(Color, include="all", child_pagination=True)
+@strawchemy.type(Color, include="all", paginate="all")
class ColorTypeWithPagination: ...
diff --git a/tests/integration/types/sqlite.py b/tests/integration/types/sqlite.py
index d090a8e5..75e61eb9 100644
--- a/tests/integration/types/sqlite.py
+++ b/tests/integration/types/sqlite.py
@@ -150,7 +150,7 @@ class OrderedFruitType: ...
class FruitAggregationType: ...
-@strawchemy.type(Fruit, include="all", child_pagination=True, child_order_by=True)
+@strawchemy.type(Fruit, include="all", paginate="all", order="all")
class FruitTypeWithPaginationAndOrderBy: ...
@@ -181,7 +181,7 @@ class FruitUpsertConflictFields: ...
# Color
-@strawchemy.type(Color, include="all", override=True, child_order_by=True)
+@strawchemy.type(Color, include="all", override=True, order="all")
class ColorType: ...
@@ -193,7 +193,7 @@ class ColorOrder: ...
class ColorDistinctOn: ...
-@strawchemy.type(Color, include="all", child_pagination=True)
+@strawchemy.type(Color, include="all", paginate="all")
class ColorTypeWithPagination: ...
diff --git a/tests/unit/__snapshots__/test_example_app/test_graphql_schema.gql b/tests/unit/__snapshots__/test_example_app/test_graphql_schema.gql
index f6714914..60407eeb 100644
--- a/tests/unit/__snapshots__/test_example_app/test_graphql_schema.gql
+++ b/tests/unit/__snapshots__/test_example_app/test_graphql_schema.gql
@@ -62,16 +62,16 @@ input CustomerAggregateBoolExpSum {
}
input CustomerAggregateMinMaxDatetimeFieldsOrderBy {
- createdAt: OrderByEnum!
- updatedAt: OrderByEnum!
+ createdAt: OrderByEnum
+ updatedAt: OrderByEnum
}
input CustomerAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input CustomerAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input CustomerAggregateOrderBy {
@@ -328,16 +328,16 @@ input MilestoneAggregateBoolExpSum {
}
input MilestoneAggregateMinMaxDatetimeFieldsOrderBy {
- createdAt: OrderByEnum!
- updatedAt: OrderByEnum!
+ createdAt: OrderByEnum
+ updatedAt: OrderByEnum
}
input MilestoneAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input MilestoneAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input MilestoneAggregateOrderBy {
@@ -563,16 +563,16 @@ input ProjectAggregateBoolExpSum {
}
input ProjectAggregateMinMaxDatetimeFieldsOrderBy {
- createdAt: OrderByEnum!
- updatedAt: OrderByEnum!
+ createdAt: OrderByEnum
+ updatedAt: OrderByEnum
}
input ProjectAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input ProjectAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input ProjectAggregateOrderBy {
@@ -798,16 +798,16 @@ input TagAggregateBoolExpSum {
}
input TagAggregateMinMaxDatetimeFieldsOrderBy {
- createdAt: OrderByEnum!
- updatedAt: OrderByEnum!
+ createdAt: OrderByEnum
+ updatedAt: OrderByEnum
}
input TagAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input TagAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input TagAggregateOrderBy {
@@ -962,16 +962,16 @@ input TicketAggregateBoolExpSum {
}
input TicketAggregateMinMaxDatetimeFieldsOrderBy {
- createdAt: OrderByEnum!
- updatedAt: OrderByEnum!
+ createdAt: OrderByEnum
+ updatedAt: OrderByEnum
}
input TicketAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input TicketAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input TicketAggregateOrderBy {
diff --git a/tests/unit/dto/test_dto.py b/tests/unit/dto/test_dto.py
index 455de40a..1f35819f 100644
--- a/tests/unit/dto/test_dto.py
+++ b/tests/unit/dto/test_dto.py
@@ -266,3 +266,72 @@ def test_forward_refs_resolved(name: str, sqlalchemy_pydantic_factory: MappedPyd
],
}
)
+
+
+# Tests for DTOConfig.from_include() and is_field_included()
+
+
+def test_from_include_with_none() -> None:
+ """Test that from_include(None) creates a config with empty include set."""
+ config = DTOConfig.from_include(None)
+ assert config.include == set()
+ assert config.purpose == Purpose.READ
+
+
+def test_from_include_with_all() -> None:
+ """Test that from_include('all') creates a config with include='all'."""
+ config = DTOConfig.from_include("all")
+ assert config.include == "all"
+ assert config.purpose == Purpose.READ
+
+
+def test_from_include_with_list() -> None:
+ """Test that from_include() accepts a list and converts it to the include parameter."""
+ config = DTOConfig.from_include(["field1", "field2"])
+ assert config.include == ["field1", "field2"]
+ assert config.purpose == Purpose.READ
+
+
+def test_from_include_with_set() -> None:
+ """Test that from_include() accepts a set for the include parameter."""
+ config = DTOConfig.from_include({"field1", "field2"})
+ assert config.include == {"field1", "field2"}
+ assert config.purpose == Purpose.READ
+
+
+def test_from_include_with_custom_purpose() -> None:
+ """Test that from_include() accepts a custom purpose."""
+ config = DTOConfig.from_include(["field1"], purpose=Purpose.WRITE)
+ assert config.include == ["field1"]
+ assert config.purpose == Purpose.WRITE
+
+
+def test_is_field_included_with_all() -> None:
+ """Test that is_field_included() returns True for any field when include='all'."""
+ config = DTOConfig.from_include("all")
+ assert config.is_field_included("any_field") is True
+ assert config.is_field_included("another_field") is True
+
+
+def test_is_field_included_with_specific_list() -> None:
+ """Test that is_field_included() returns True only for listed fields."""
+ config = DTOConfig.from_include(["field1", "field2"])
+ assert config.is_field_included("field1") is True
+ assert config.is_field_included("field2") is True
+ assert config.is_field_included("field3") is False
+
+
+def test_is_field_included_with_empty_include() -> None:
+ """Test that is_field_included() returns False for all fields when include is empty."""
+ config = DTOConfig.from_include(None)
+ assert config.is_field_included("field1") is False
+ assert config.is_field_included("any_field") is False
+
+
+def test_is_field_included_with_exclude() -> None:
+ """Test that excluded fields are properly excluded even when include='all'."""
+ config = DTOConfig(Purpose.READ, include="all", exclude={"field2", "field3"})
+ assert config.is_field_included("field1") is True
+ assert config.is_field_included("field2") is False
+ assert config.is_field_included("field3") is False
+ assert config.is_field_included("field4") is True
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[all_fields_order_by].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[all_fields_order_by].gql
index efccf7e8..ff732a2b 100644
--- a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[all_fields_order_by].gql
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[all_fields_order_by].gql
@@ -1,10 +1,10 @@
'''
input ColorAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input ColorAggregateNumericFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input ColorAggregateOrderBy {
@@ -48,11 +48,11 @@ type FruitAggregate {
}
input FruitAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum!
+ name: OrderByEnum
}
input FruitAggregateNumericFieldsOrderBy {
- sweetness: OrderByEnum!
+ sweetness: OrderByEnum
}
input FruitAggregateOrderBy {
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[auto_order_by].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[auto_order_by].gql
deleted file mode 100644
index 32d165b0..00000000
--- a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[auto_order_by].gql
+++ /dev/null
@@ -1,339 +0,0 @@
-'''
-input ColorAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input ColorAggregateNumericFieldsOrderBy {
- name: OrderByEnum
-}
-
-input ColorAggregateOrderBy {
- count: OrderByEnum
- maxString: ColorAggregateMinMaxStringFieldsOrderBy
- minString: ColorAggregateMinMaxStringFieldsOrderBy
- sum: ColorAggregateNumericFieldsOrderBy
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input ColorOrderBy {
- fruitsAggregate: FruitAggregateOrderBy
- fruits: FruitOrderBy
- name: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type ColorType {
- fruitsAggregate: FruitAggregate!
-
- """Fetch objects from the FruitType collection"""
- fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
- name: String!
- id: UUID!
-}
-
-"""Aggregation fields"""
-type DepartmentAggregate {
- count: Int
- max: DepartmentMinMaxFields!
- min: DepartmentMinMaxFields!
- sum: DepartmentSumFields!
-}
-
-input DepartmentAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input DepartmentAggregateNumericFieldsOrderBy {
- name: OrderByEnum
-}
-
-input DepartmentAggregateOrderBy {
- count: OrderByEnum
- maxString: DepartmentAggregateMinMaxStringFieldsOrderBy
- minString: DepartmentAggregateMinMaxStringFieldsOrderBy
- sum: DepartmentAggregateNumericFieldsOrderBy
-}
-
-"""GraphQL type"""
-type DepartmentMinMaxFields {
- name: String
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input DepartmentOrderBy {
- usersAggregate: UserAggregateOrderBy
- users: UserOrderBy
- name: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type DepartmentSumFields {
- name: String
-}
-
-"""GraphQL type"""
-type DepartmentType {
- usersAggregate: UserAggregate!
-
- """Fetch objects from the UserType collection"""
- users(orderBy: [UserOrderBy!] = null): [UserType!]!
- name: String
- id: UUID!
-}
-
-"""Aggregation fields"""
-type FruitAggregate {
- avg: FruitNumericFields!
- count: Int
- max: FruitMinMaxFields!
- min: FruitMinMaxFields!
- stddevPop: FruitNumericFields!
- stddevSamp: FruitNumericFields!
- sum: FruitSumFields!
- varPop: FruitNumericFields!
- varSamp: FruitNumericFields!
-}
-
-input FruitAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input FruitAggregateNumericFieldsOrderBy {
- sweetness: OrderByEnum
-}
-
-input FruitAggregateOrderBy {
- avg: FruitAggregateNumericFieldsOrderBy
- count: OrderByEnum
- max: FruitAggregateNumericFieldsOrderBy
- maxString: FruitAggregateMinMaxStringFieldsOrderBy
- min: FruitAggregateNumericFieldsOrderBy
- minString: FruitAggregateMinMaxStringFieldsOrderBy
- stddevPop: FruitAggregateNumericFieldsOrderBy
- stddevSamp: FruitAggregateNumericFieldsOrderBy
- sum: FruitAggregateNumericFieldsOrderBy
- varPop: FruitAggregateNumericFieldsOrderBy
- varSamp: FruitAggregateNumericFieldsOrderBy
-}
-
-"""GraphQL type"""
-type FruitMinMaxFields {
- name: String
- sweetness: Int
-}
-
-"""GraphQL type"""
-type FruitNumericFields {
- sweetness: Float
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input FruitOrderBy {
- colorAggregate: ColorAggregateOrderBy
- color: ColorOrderBy
- name: OrderByEnum
- colorId: OrderByEnum
- sweetness: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type FruitSumFields {
- name: String
- sweetness: Int
-}
-
-"""GraphQL type"""
-type FruitType {
- color: ColorType!
- name: String!
- colorId: UUID
- sweetness: Int!
- id: UUID!
-}
-
-"""Aggregation fields"""
-type GroupAggregate {
- count: Int
- max: GroupMinMaxFields!
- min: GroupMinMaxFields!
- sum: GroupSumFields!
-}
-
-input GroupAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input GroupAggregateNumericFieldsOrderBy {
- name: OrderByEnum
-}
-
-input GroupAggregateOrderBy {
- count: OrderByEnum
- maxString: GroupAggregateMinMaxStringFieldsOrderBy
- minString: GroupAggregateMinMaxStringFieldsOrderBy
- sum: GroupAggregateNumericFieldsOrderBy
-}
-
-"""GraphQL type"""
-type GroupMinMaxFields {
- name: String
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input GroupOrderBy {
- tagAggregate: TagAggregateOrderBy
- tag: TagOrderBy
- usersAggregate: UserAggregateOrderBy
- users: UserOrderBy
- colorAggregate: ColorAggregateOrderBy
- color: ColorOrderBy
- name: OrderByEnum
- tagId: OrderByEnum
- colorId: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type GroupSumFields {
- name: String
-}
-
-"""GraphQL type"""
-type GroupType {
- tag: TagType!
- usersAggregate: UserAggregate!
-
- """Fetch objects from the UserType collection"""
- users(orderBy: [UserOrderBy!] = null): [UserType!]!
- color: ColorType!
- name: String!
- tagId: UUID!
- colorId: UUID!
- id: UUID!
-}
-
-enum OrderByEnum {
- ASC
- ASC_NULLS_FIRST
- ASC_NULLS_LAST
- DESC
- DESC_NULLS_FIRST
- DESC_NULLS_LAST
-}
-
-type Query {
- """Fetch objects from the GroupType collection"""
- group: [GroupType!]!
-}
-
-input TagAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input TagAggregateNumericFieldsOrderBy {
- name: OrderByEnum
-}
-
-input TagAggregateOrderBy {
- count: OrderByEnum
- maxString: TagAggregateMinMaxStringFieldsOrderBy
- minString: TagAggregateMinMaxStringFieldsOrderBy
- sum: TagAggregateNumericFieldsOrderBy
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input TagOrderBy {
- groupsAggregate: GroupAggregateOrderBy
- groups: GroupOrderBy
- name: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type TagType {
- groupsAggregate: GroupAggregate!
-
- """Fetch objects from the GroupType collection"""
- groups(orderBy: [GroupOrderBy!] = null): [GroupType!]!
- name: String!
- id: UUID!
-}
-
-scalar UUID
-
-"""Aggregation fields"""
-type UserAggregate {
- count: Int
- max: UserMinMaxFields!
- min: UserMinMaxFields!
- sum: UserSumFields!
-}
-
-input UserAggregateMinMaxStringFieldsOrderBy {
- name: OrderByEnum
-}
-
-input UserAggregateNumericFieldsOrderBy {
- name: OrderByEnum
-}
-
-input UserAggregateOrderBy {
- count: OrderByEnum
- maxString: UserAggregateMinMaxStringFieldsOrderBy
- minString: UserAggregateMinMaxStringFieldsOrderBy
- sum: UserAggregateNumericFieldsOrderBy
-}
-
-"""GraphQL type"""
-type UserMinMaxFields {
- name: String
-}
-
-"""
-Boolean expression to compare fields. All fields are combined with logical 'AND'.
-"""
-input UserOrderBy {
- groupAggregate: GroupAggregateOrderBy
- group: GroupOrderBy
- tagAggregate: TagAggregateOrderBy
- tag: TagOrderBy
- departmentsAggregate: DepartmentAggregateOrderBy
- departments: DepartmentOrderBy
- name: OrderByEnum
- groupId: OrderByEnum
- tagId: OrderByEnum
- id: OrderByEnum
-}
-
-"""GraphQL type"""
-type UserSumFields {
- name: String
-}
-
-"""GraphQL type"""
-type UserType {
- group: GroupType!
- tag: TagType!
- departmentsAggregate: DepartmentAggregate!
-
- """Fetch objects from the DepartmentType collection"""
- departments(orderBy: [DepartmentOrderBy!] = null): [DepartmentType!]!
- name: String!
- groupId: UUID
- tagId: UUID
- id: UUID!
-}
-'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_all].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_all].gql
new file mode 100644
index 00000000..e858f495
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_all].gql
@@ -0,0 +1,69 @@
+'''
+enum ColorDistinctOnFields {
+ name
+ id
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(distinctOn: [FruitDistinctOnFields!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+enum FruitDistinctOnFields {
+ name
+ colorId
+ sweetness
+ id
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors(distinctOn: [ColorDistinctOnFields!] = null): [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_empty].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_empty].gql
new file mode 100644
index 00000000..4121398a
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_empty].gql
@@ -0,0 +1,57 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_specific_fields].gql
new file mode 100644
index 00000000..b0ee37ba
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_specific_fields].gql
@@ -0,0 +1,65 @@
+'''
+enum ColorDistinctOnFields {
+ name
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(distinctOn: [FruitDistinctOnFields!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+enum FruitDistinctOnFields {
+ name
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors(distinctOn: [ColorDistinctOnFields!] = null): [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_with_field_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_with_field_override].gql
new file mode 100644
index 00000000..ca5c2cbb
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_with_field_override].gql
@@ -0,0 +1,68 @@
+'''
+enum ColorDistinctOnFields {
+ name
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(distinctOn: [FruitDistinctOnFields!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+enum FruitDistinctOnFields {
+ name
+ colorId
+ sweetness
+ id
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors(distinctOn: [ColorDistinctOnFields!] = null): [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_all].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_all].gql
new file mode 100644
index 00000000..2a8310bc
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_all].gql
@@ -0,0 +1,62 @@
+'''
+enum ColorDistinctOnFields {
+ name
+ id
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors(distinctOn: [ColorDistinctOnFields!] = null): [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_specific_fields].gql
new file mode 100644
index 00000000..ef46c7b2
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_specific_fields].gql
@@ -0,0 +1,61 @@
+'''
+enum ColorDistinctOnFields {
+ name
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ColorType collection"""
+ colors(distinctOn: [ColorDistinctOnFields!] = null): [ColorType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_filter_auto_generate].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_filter_auto_generate].gql
new file mode 100644
index 00000000..68f44ab4
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_filter_auto_generate].gql
@@ -0,0 +1,324 @@
+'''
+"""
+Boolean expression to compare aggregated fields. All fields are combined with logical 'AND'.
+"""
+input ColorAggregateBoolExp {
+ count: ColorAggregateBoolExpCount
+ maxString: ColorAggregateBoolExpMaxstring
+ minString: ColorAggregateBoolExpMinstring
+ sum: ColorAggregateBoolExpSum
+}
+
+"""Boolean expression to compare count aggregation."""
+input ColorAggregateBoolExpCount {
+ arguments: [ColorCountFields!] = []
+ predicate: IntOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare max aggregation."""
+input ColorAggregateBoolExpMaxstring {
+ arguments: [ColorMinMaxStringFieldsEnum!]!
+ predicate: TextComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare min aggregation."""
+input ColorAggregateBoolExpMinstring {
+ arguments: [ColorMinMaxStringFieldsEnum!]!
+ predicate: TextComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare sum aggregation."""
+input ColorAggregateBoolExpSum {
+ arguments: [ColorSumFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorBoolExp {
+ _and: [ColorBoolExp!]! = []
+ _or: [ColorBoolExp!]! = []
+ _not: ColorBoolExp
+ fruitsAggregate: FruitAggregateBoolExp
+ fruits: FruitBoolExp
+ name: TextComparison
+ id: UUIDGenericComparison
+}
+
+enum ColorCountFields {
+ name
+ id
+}
+
+enum ColorMinMaxStringFieldsEnum {
+ name
+}
+
+enum ColorSumFieldsEnum {
+ name
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""
+Boolean expression to compare fields supporting order comparisons. All fields are combined with logical 'AND'
+"""
+input FloatOrderComparison {
+ eq: Float
+ neq: Float
+ isNull: Boolean
+ in: [Float!]
+ nin: [Float!]
+ gt: Float
+ gte: Float
+ lt: Float
+ lte: Float
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""
+Boolean expression to compare aggregated fields. All fields are combined with logical 'AND'.
+"""
+input FruitAggregateBoolExp {
+ avg: FruitAggregateBoolExpAvg
+ count: FruitAggregateBoolExpCount
+ max: FruitAggregateBoolExpMax
+ maxString: FruitAggregateBoolExpMaxstring
+ min: FruitAggregateBoolExpMin
+ minString: FruitAggregateBoolExpMinstring
+ stddevPop: FruitAggregateBoolExpStddevpop
+ stddevSamp: FruitAggregateBoolExpStddevsamp
+ sum: FruitAggregateBoolExpSum
+ varPop: FruitAggregateBoolExpVarpop
+ varSamp: FruitAggregateBoolExpVarsamp
+}
+
+"""Boolean expression to compare avg aggregation."""
+input FruitAggregateBoolExpAvg {
+ arguments: [FruitNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare count aggregation."""
+input FruitAggregateBoolExpCount {
+ arguments: [FruitCountFields!] = []
+ predicate: IntOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare max aggregation."""
+input FruitAggregateBoolExpMax {
+ arguments: [FruitMinMaxNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare max aggregation."""
+input FruitAggregateBoolExpMaxstring {
+ arguments: [FruitMinMaxStringFieldsEnum!]!
+ predicate: TextComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare min aggregation."""
+input FruitAggregateBoolExpMin {
+ arguments: [FruitMinMaxNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare min aggregation."""
+input FruitAggregateBoolExpMinstring {
+ arguments: [FruitMinMaxStringFieldsEnum!]!
+ predicate: TextComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare stddev_pop aggregation."""
+input FruitAggregateBoolExpStddevpop {
+ arguments: [FruitNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare stddev_samp aggregation."""
+input FruitAggregateBoolExpStddevsamp {
+ arguments: [FruitNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare sum aggregation."""
+input FruitAggregateBoolExpSum {
+ arguments: [FruitSumFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare var_pop aggregation."""
+input FruitAggregateBoolExpVarpop {
+ arguments: [FruitNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""Boolean expression to compare var_samp aggregation."""
+input FruitAggregateBoolExpVarsamp {
+ arguments: [FruitNumericFieldsEnum!]!
+ predicate: FloatOrderComparison!
+ distinct: Boolean = false
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitBoolExp {
+ _and: [FruitBoolExp!]! = []
+ _or: [FruitBoolExp!]! = []
+ _not: FruitBoolExp
+ colorAggregate: ColorAggregateBoolExp
+ color: ColorBoolExp
+ name: TextComparison
+ colorId: UUIDGenericComparison
+ sweetness: IntOrderComparison
+ id: UUIDGenericComparison
+}
+
+enum FruitCountFields {
+ name
+ colorId
+ sweetness
+ id
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+enum FruitMinMaxNumericFieldsEnum {
+ sweetness
+}
+
+enum FruitMinMaxStringFieldsEnum {
+ name
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+enum FruitNumericFieldsEnum {
+ sweetness
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+enum FruitSumFieldsEnum {
+ name
+ sweetness
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+"""
+Boolean expression to compare fields supporting order comparisons. All fields are combined with logical 'AND'
+"""
+input IntOrderComparison {
+ eq: Int
+ neq: Int
+ isNull: Boolean
+ in: [Int!]
+ nin: [Int!]
+ gt: Int
+ gte: Int
+ lt: Int
+ lte: Int
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(filter: FruitBoolExp = null): [FruitType!]!
+}
+
+"""
+Boolean expression to compare String fields. All fields are combined with logical 'AND'
+"""
+input TextComparison {
+ eq: String
+ neq: String
+ isNull: Boolean
+ in: [String!]
+ nin: [String!]
+ gt: String
+ gte: String
+ lt: String
+ lte: String
+ like: String
+ nlike: String
+ ilike: String
+ nilike: String
+ regexp: String
+ iregexp: String
+ nregexp: String
+ inregexp: String
+ startswith: String
+ endswith: String
+ contains: String
+ istartswith: String
+ iendswith: String
+ icontains: String
+}
+
+scalar UUID
+
+"""
+Boolean expression to compare fields supporting equality comparisons. All fields are combined with logical 'AND'
+"""
+input UUIDGenericComparison {
+ eq: UUID
+ neq: UUID
+ isNull: Boolean
+ in: [UUID!]
+ nin: [UUID!]
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_all].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_all].gql
new file mode 100644
index 00000000..7eb68e81
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_all].gql
@@ -0,0 +1,125 @@
+'''
+input ColorAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateNumericFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+ maxString: ColorAggregateMinMaxStringFieldsOrderBy
+ minString: ColorAggregateMinMaxStringFieldsOrderBy
+ sum: ColorAggregateNumericFieldsOrderBy
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input FruitAggregateNumericFieldsOrderBy {
+ sweetness: OrderByEnum
+}
+
+input FruitAggregateOrderBy {
+ avg: FruitAggregateNumericFieldsOrderBy
+ count: OrderByEnum
+ max: FruitAggregateNumericFieldsOrderBy
+ maxString: FruitAggregateMinMaxStringFieldsOrderBy
+ min: FruitAggregateNumericFieldsOrderBy
+ minString: FruitAggregateMinMaxStringFieldsOrderBy
+ stddevPop: FruitAggregateNumericFieldsOrderBy
+ stddevSamp: FruitAggregateNumericFieldsOrderBy
+ sum: FruitAggregateNumericFieldsOrderBy
+ varPop: FruitAggregateNumericFieldsOrderBy
+ varSamp: FruitAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_specific_fields].gql
new file mode 100644
index 00000000..80a0def4
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_specific_fields].gql
@@ -0,0 +1,192 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ContainerOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ vegetablesAggregate: VegetableAggregateOrderBy
+ vegetables: VegetableOrderBy
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateOrderBy {
+ count: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ containers(orderBy: [ContainerOrderBy!] = null): [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+input VegetableAggregateOrderBy {
+ count: OrderByEnum
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input VegetableOrderBy {
+ family: OrderByEnum
+ id: OrderByEnum
+ name: OrderByEnum
+ description: OrderByEnum
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all].gql
new file mode 100644
index 00000000..fd4005ac
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all].gql
@@ -0,0 +1,137 @@
+'''
+input ColorAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateNumericFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+ maxString: ColorAggregateMinMaxStringFieldsOrderBy
+ minString: ColorAggregateMinMaxStringFieldsOrderBy
+ sum: ColorAggregateNumericFieldsOrderBy
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input FruitAggregateNumericFieldsOrderBy {
+ sweetness: OrderByEnum
+}
+
+input FruitAggregateOrderBy {
+ avg: FruitAggregateNumericFieldsOrderBy
+ count: OrderByEnum
+ max: FruitAggregateNumericFieldsOrderBy
+ maxString: FruitAggregateMinMaxStringFieldsOrderBy
+ min: FruitAggregateNumericFieldsOrderBy
+ minString: FruitAggregateMinMaxStringFieldsOrderBy
+ stddevPop: FruitAggregateNumericFieldsOrderBy
+ stddevSamp: FruitAggregateNumericFieldsOrderBy
+ sum: FruitAggregateNumericFieldsOrderBy
+ varPop: FruitAggregateNumericFieldsOrderBy
+ varSamp: FruitAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy1 {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy1!] = null): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all_with_field_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all_with_field_override].gql
new file mode 100644
index 00000000..3849a677
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all_with_field_override].gql
@@ -0,0 +1,132 @@
+'''
+input ColorAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateNumericFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+ maxString: ColorAggregateMinMaxStringFieldsOrderBy
+ minString: ColorAggregateMinMaxStringFieldsOrderBy
+ sum: ColorAggregateNumericFieldsOrderBy
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input FruitAggregateNumericFieldsOrderBy {
+ sweetness: OrderByEnum
+}
+
+input FruitAggregateOrderBy {
+ avg: FruitAggregateNumericFieldsOrderBy
+ count: OrderByEnum
+ max: FruitAggregateNumericFieldsOrderBy
+ maxString: FruitAggregateMinMaxStringFieldsOrderBy
+ min: FruitAggregateNumericFieldsOrderBy
+ minString: FruitAggregateMinMaxStringFieldsOrderBy
+ stddevPop: FruitAggregateNumericFieldsOrderBy
+ stddevSamp: FruitAggregateNumericFieldsOrderBy
+ sum: FruitAggregateNumericFieldsOrderBy
+ varPop: FruitAggregateNumericFieldsOrderBy
+ varSamp: FruitAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy1 {
+ name: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy1!] = null): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty].gql
new file mode 100644
index 00000000..918eed85
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty].gql
@@ -0,0 +1,57 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty_with_empty_type_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty_with_empty_type_override].gql
new file mode 100644
index 00000000..03fdedc2
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty_with_empty_type_override].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields].gql
new file mode 100644
index 00000000..bc92e35d
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields].gql
@@ -0,0 +1,82 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ name: OrderByEnum
+ sweetness: OrderByEnum
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy1 {
+ name: OrderByEnum
+ sweetness: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy1!] = null): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields_with_type_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields_with_type_override].gql
new file mode 100644
index 00000000..b0b04334
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields_with_type_override].gql
@@ -0,0 +1,207 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+input ColorAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateNumericFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+ maxString: ColorAggregateMinMaxStringFieldsOrderBy
+ minString: ColorAggregateMinMaxStringFieldsOrderBy
+ sum: ColorAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(orderBy: [VegetableOrderBy!] = null): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input FruitAggregateNumericFieldsOrderBy {
+ sweetness: OrderByEnum
+}
+
+input FruitAggregateOrderBy {
+ avg: FruitAggregateNumericFieldsOrderBy
+ count: OrderByEnum
+ max: FruitAggregateNumericFieldsOrderBy
+ maxString: FruitAggregateMinMaxStringFieldsOrderBy
+ min: FruitAggregateNumericFieldsOrderBy
+ minString: FruitAggregateMinMaxStringFieldsOrderBy
+ stddevPop: FruitAggregateNumericFieldsOrderBy
+ stddevSamp: FruitAggregateNumericFieldsOrderBy
+ sum: FruitAggregateNumericFieldsOrderBy
+ varPop: FruitAggregateNumericFieldsOrderBy
+ varSamp: FruitAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input VegetableOrderBy {
+ family: OrderByEnum
+ id: OrderByEnum
+ name: OrderByEnum
+ description: OrderByEnum
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_with_field_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_with_field_override].gql
new file mode 100644
index 00000000..e5fb8804
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_with_field_override].gql
@@ -0,0 +1,80 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ name: OrderByEnum
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy1 {
+ sweetness: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(orderBy: [FruitOrderBy1!] = null): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_all_with_default].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_all_with_default].gql
new file mode 100644
index 00000000..1540a4c6
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_all_with_default].gql
@@ -0,0 +1,57 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 20, offset: Int! = 5): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruitWithCustomDefault: [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_and_order_combined].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_and_order_combined].gql
new file mode 100644
index 00000000..24d5676a
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_and_order_combined].gql
@@ -0,0 +1,249 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+input ColorAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateNumericFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input ColorAggregateOrderBy {
+ count: OrderByEnum
+ maxString: ColorAggregateMinMaxStringFieldsOrderBy
+ minString: ColorAggregateMinMaxStringFieldsOrderBy
+ sum: ColorAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input ColorOrderBy {
+ fruitsAggregate: FruitAggregateOrderBy
+ fruits: FruitOrderBy
+ name: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType1 {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(orderBy: [VegetableOrderBy!] = null): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType2 {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0, orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(limit: Int = 100, offset: Int! = 0): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType3 {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0, orderBy: [FruitOrderBy!] = null): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(limit: Int = 100, offset: Int! = 0): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors(limit: Int = 100, offset: Int! = 0): [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+input FruitAggregateMinMaxStringFieldsOrderBy {
+ name: OrderByEnum
+}
+
+input FruitAggregateNumericFieldsOrderBy {
+ sweetness: OrderByEnum
+}
+
+input FruitAggregateOrderBy {
+ avg: FruitAggregateNumericFieldsOrderBy
+ count: OrderByEnum
+ max: FruitAggregateNumericFieldsOrderBy
+ maxString: FruitAggregateMinMaxStringFieldsOrderBy
+ min: FruitAggregateNumericFieldsOrderBy
+ minString: FruitAggregateMinMaxStringFieldsOrderBy
+ stddevPop: FruitAggregateNumericFieldsOrderBy
+ stddevSamp: FruitAggregateNumericFieldsOrderBy
+ sum: FruitAggregateNumericFieldsOrderBy
+ varPop: FruitAggregateNumericFieldsOrderBy
+ varSamp: FruitAggregateNumericFieldsOrderBy
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input FruitOrderBy {
+ colorAggregate: ColorAggregateOrderBy
+ color: ColorOrderBy
+ name: OrderByEnum
+ colorId: OrderByEnum
+ sweetness: OrderByEnum
+ id: OrderByEnum
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+enum OrderByEnum {
+ ASC
+ ASC_NULLS_FIRST
+ ASC_NULLS_LAST
+ DESC
+ DESC_NULLS_FIRST
+ DESC_NULLS_LAST
+}
+
+type Query {
+ """Fetch objects from the ContainerType1 collection"""
+ container1: [ContainerType1!]!
+
+ """Fetch objects from the ContainerType2 collection"""
+ container2: [ContainerType2!]!
+
+ """Fetch objects from the ContainerType3 collection"""
+ container3: [ContainerType3!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""
+Boolean expression to compare fields. All fields are combined with logical 'AND'.
+"""
+input VegetableOrderBy {
+ family: OrderByEnum
+ id: OrderByEnum
+ name: OrderByEnum
+ description: OrderByEnum
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_empty].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_empty].gql
new file mode 100644
index 00000000..03fdedc2
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_empty].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_specific_fields].gql
new file mode 100644
index 00000000..7c9a3730
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_specific_fields].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_default].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_default].gql
index 9bf20f23..0b4346cf 100644
--- a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_default].gql
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_default].gql
@@ -4,7 +4,7 @@ type ColorType {
fruitsAggregate: FruitAggregate!
"""Fetch objects from the FruitType collection"""
- fruits: [FruitType!]!
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
name: String!
id: UUID!
}
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty].gql
new file mode 100644
index 00000000..03fdedc2
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty_with_type_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty_with_type_override].gql
new file mode 100644
index 00000000..7c9a3730
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty_with_type_override].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables: [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_specific_fields].gql
new file mode 100644
index 00000000..e9c2d7ed
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_specific_fields].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(limit: Int = 100, offset: Int! = 0): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_with_type_override].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_with_type_override].gql
new file mode 100644
index 00000000..6bcea719
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_with_type_override].gql
@@ -0,0 +1,129 @@
+'''
+"""Aggregation fields"""
+type ColorAggregate {
+ count: Int
+ max: ColorMinMaxFields!
+ min: ColorMinMaxFields!
+ sum: ColorSumFields!
+}
+
+"""GraphQL type"""
+type ColorMinMaxFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorSumFields {
+ name: String
+}
+
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 0): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""GraphQL type"""
+type ContainerType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits: [FruitType!]!
+ vegetablesAggregate: VegetableAggregate!
+
+ """Fetch objects from the VegetableType collection"""
+ vegetables(limit: Int = 100, offset: Int! = 0): [VegetableType!]!
+ colorsAggregate: ColorAggregate!
+
+ """Fetch objects from the ColorType collection"""
+ colors: [ColorType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the ContainerType collection"""
+ container: [ContainerType!]!
+}
+
+scalar UUID
+
+"""Aggregation fields"""
+type VegetableAggregate {
+ count: Int
+ max: VegetableMinMaxFields!
+ min: VegetableMinMaxFields!
+ sum: VegetableSumFields!
+}
+
+enum VegetableFamily {
+ MUSHROOM
+ GOURD
+ CABBAGE
+ ONION
+ SEEDS
+}
+
+"""GraphQL type"""
+type VegetableMinMaxFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableSumFields {
+ name: String
+ description: String
+}
+
+"""GraphQL type"""
+type VegetableType {
+ family: VegetableFamily!
+ id: UUID!
+ name: String!
+ description: String!
+}
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_default_offset].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_default_offset].gql
new file mode 100644
index 00000000..01352f38
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_default_offset].gql
@@ -0,0 +1,57 @@
+'''
+"""GraphQL type"""
+type ColorType {
+ fruitsAggregate: FruitAggregate!
+
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 5): [FruitType!]!
+ name: String!
+ id: UUID!
+}
+
+"""Aggregation fields"""
+type FruitAggregate {
+ avg: FruitNumericFields!
+ count: Int
+ max: FruitMinMaxFields!
+ min: FruitMinMaxFields!
+ stddevPop: FruitNumericFields!
+ stddevSamp: FruitNumericFields!
+ sum: FruitSumFields!
+ varPop: FruitNumericFields!
+ varSamp: FruitNumericFields!
+}
+
+"""GraphQL type"""
+type FruitMinMaxFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitNumericFields {
+ sweetness: Float
+}
+
+"""GraphQL type"""
+type FruitSumFields {
+ name: String
+ sweetness: Int
+}
+
+"""GraphQL type"""
+type FruitType {
+ color: ColorType!
+ name: String!
+ colorId: UUID
+ sweetness: Int!
+ id: UUID!
+}
+
+type Query {
+ """Fetch objects from the FruitType collection"""
+ fruits(limit: Int = 100, offset: Int! = 5): [FruitType!]!
+}
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_distinct_manual_enum].gql
similarity index 100%
rename from tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct].gql
rename to tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_distinct_manual_enum].gql
diff --git a/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_order_by_specific_fields].gql b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_order_by_specific_fields].gql
new file mode 100644
index 00000000..a82cf7c9
--- /dev/null
+++ b/tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_order_by_specific_fields].gql
@@ -0,0 +1,47 @@
+'''
+"""Date (isoformat)"""
+scalar Date
+
+"""Date with time (isoformat)"""
+scalar DateTime
+
+"""Decimal (fixed-point)"""
+scalar Decimal
+
+"""
+The `Interval` scalar type represents a duration of time as specified by [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations).
+"""
+scalar Interval @specifiedBy(url: "https://en.wikipedia.org/wiki/ISO_8601#Durations")
+
+"""
+The `JSON` scalar type represents JSON values as specified by [ECMA-404](https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf).
+"""
+scalar JSON @specifiedBy(url: "https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf")
+
+type Query {
+ """Fetch objects from the SQLDataTypesType collection"""
+ sqlDataTypes: [SQLDataTypesType!]!
+}
+
+"""GraphQL type"""
+type SQLDataTypesType {
+ dateCol: Date!
+ timeCol: Time!
+ timeDeltaCol: Interval!
+ datetimeCol: DateTime!
+ strCol: String!
+ intCol: Int!
+ floatCol: Float!
+ decimalCol: Decimal!
+ boolCol: Boolean!
+ uuidCol: UUID!
+ dictCol(path: String): JSON
+ arrayStrCol: [String!]!
+ id: UUID!
+}
+
+"""Time (isoformat)"""
+scalar Time
+
+scalar UUID
+'''
\ No newline at end of file
diff --git a/tests/unit/mapping/test_schemas.py b/tests/unit/mapping/test_schemas.py
index ae1d08f6..74df37e7 100644
--- a/tests/unit/mapping/test_schemas.py
+++ b/tests/unit/mapping/test_schemas.py
@@ -184,18 +184,49 @@ def test_update_mutation_by_filter_type_not_list_fail() -> None:
pytest.param("pagination.pagination_defaults.Query", id="pagination_defaults"),
pytest.param("pagination.children_pagination.Query", id="children_pagination"),
pytest.param("pagination.children_pagination_defaults.Query", id="children_pagination_defaults"),
- pytest.param("pagination.pagination_default_limit.Query", id="pagination_default_limit"),
pytest.param("pagination.pagination_config_default.Query", id="pagination_config_default"),
+ pytest.param("pagination.pagination_default_limit.Query", id="pagination_default_limit"),
+ pytest.param("pagination.pagination_default_offset.Query", id="pagination_default_offset"),
+ pytest.param("pagination.paginate_specific_fields.Query", id="paginate_specific_fields"),
+ pytest.param("pagination.paginate_empty.Query", id="paginate_empty"),
+ pytest.param("pagination.pagination_config_empty.Query", id="pagination_config_empty"),
+ pytest.param("pagination.pagination_config_specific_fields.Query", id="pagination_config_specific_fields"),
+ pytest.param(
+ "pagination.pagination_config_with_type_override.Query", id="pagination_config_with_type_override"
+ ),
+ pytest.param("pagination.paginate_and_order_combined.Query", id="paginate_and_order_combined"),
+ pytest.param("pagination.paginate_all_with_default.Query", id="paginate_all_with_default"),
pytest.param("custom_id_field_name.Query", id="custom_id_field_name"),
pytest.param("enums.Query", id="enums"),
pytest.param("filters.filters.Query", id="filters"),
pytest.param("filters.filters_aggregation.Query", id="aggregation_filters"),
pytest.param("filters.type_filter.Query", id="type_filter"),
+ pytest.param("filters.field_filter_auto_generate.Query", id="field_filter_auto_generate"),
pytest.param("order.type_order_by.Query", id="type_order_by"),
pytest.param("order.field_order_by.Query", id="field_order_by"),
- pytest.param("order.auto_order_by.Query", id="auto_order_by"),
+ pytest.param("order.field_order_by_all.Query", id="field_order_by_all"),
+ pytest.param("order.field_order_by_specific_fields.Query", id="field_order_by_specific_fields"),
+ pytest.param("order.order_config_all.Query", id="order_config_all"),
+ pytest.param(
+ "order.order_config_specific_fields_with_type_override.Query",
+ id="order_config_specific_fields_with_type_override",
+ ),
+ pytest.param(
+ "order.order_config_empty_with_empty_type_override.Query", id="order_config_empty_with_empty_type_override"
+ ),
+ pytest.param("order.order_config_all_with_field_override.Query", id="order_config_all_with_field_override"),
+ pytest.param("order.order_config_specific_fields.Query", id="order_config_specific_fields"),
+ pytest.param("order.order_config_empty.Query", id="order_config_empty"),
+ pytest.param("order.order_config_with_field_override.Query", id="order_config_with_field_override"),
+ pytest.param("order.type_order_by_specific_fields.Query", id="type_order_by_specific_fields"),
pytest.param("aggregations.root_aggregations.Query", id="root_aggregations"),
- pytest.param("distinct.Query", id="distinct"),
+ pytest.param("distinct.type_distinct_manual_enum.Query", id="type_distinct_manual_enum"),
+ pytest.param("distinct.distinct_config_all.Query", id="distinct_config_all"),
+ pytest.param("distinct.field_distinct_all.Query", id="field_distinct_all"),
+ pytest.param("distinct.field_distinct_specific_fields.Query", id="field_distinct_specific_fields"),
+ pytest.param("distinct.distinct_config_with_field_override.Query", id="distinct_config_with_field_override"),
+ pytest.param("distinct.distinct_config_specific_fields.Query", id="distinct_config_specific_fields"),
+ pytest.param("distinct.distinct_config_empty.Query", id="distinct_config_empty"),
pytest.param("scope.schema_before.Query", id="scope_schema_before"),
pytest.param("scope.schema_after.Query", id="scope_schema_after"),
pytest.param("scope.schema_in_the_middle.Query", id="scope_schema_in_the_middle"),
diff --git a/tests/unit/models.py b/tests/unit/models.py
index c2832440..a9580c9d 100644
--- a/tests/unit/models.py
+++ b/tests/unit/models.py
@@ -197,6 +197,21 @@ class SQLDataTypes(UUIDBase):
array_str_col: Mapped[list[str]] = mapped_column(postgresql.ARRAY(Text), default=list)
+class Container(UUIDBase):
+ """Test model with multiple list relationships for testing paginate/order configuration."""
+
+ __tablename__ = "container"
+
+ name: Mapped[str]
+ fruits: Mapped[list[Fruit]] = relationship(
+ "Fruit", primaryjoin="Container.id == foreign(Fruit.color_id)", viewonly=True
+ )
+ vegetables: Mapped[list[Vegetable]] = relationship(
+ "Vegetable", primaryjoin="Container.id == foreign(Vegetable.id)", viewonly=True
+ )
+ colors: Mapped[list[Color]] = relationship("Color", primaryjoin="Container.id == foreign(Color.id)", viewonly=True)
+
+
# Geo
if GEO_INSTALLED:
diff --git a/tests/unit/schemas/distinct/__init__.py b/tests/unit/schemas/distinct/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/schemas/distinct/distinct_config_all.py b/tests/unit/schemas/distinct/distinct_config_all.py
new file mode 100644
index 00000000..0da45351
--- /dev/null
+++ b/tests/unit/schemas/distinct/distinct_config_all.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Color
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", distinct_on="all"))
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field()
diff --git a/tests/unit/schemas/distinct/distinct_config_empty.py b/tests/unit/schemas/distinct/distinct_config_empty.py
new file mode 100644
index 00000000..67d8fea3
--- /dev/null
+++ b/tests/unit/schemas/distinct/distinct_config_empty.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Color
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", distinct_on=[]))
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field()
diff --git a/tests/unit/schemas/distinct/distinct_config_specific_fields.py b/tests/unit/schemas/distinct/distinct_config_specific_fields.py
new file mode 100644
index 00000000..3b7431c4
--- /dev/null
+++ b/tests/unit/schemas/distinct/distinct_config_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Color
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", distinct_on=["name"]))
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field()
diff --git a/tests/unit/schemas/distinct/distinct_config_with_field_override.py b/tests/unit/schemas/distinct/distinct_config_with_field_override.py
new file mode 100644
index 00000000..7ea7c088
--- /dev/null
+++ b/tests/unit/schemas/distinct/distinct_config_with_field_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Color
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", distinct_on="all"))
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field(distinct_on=["name"])
diff --git a/tests/unit/schemas/distinct/field_distinct_all.py b/tests/unit/schemas/distinct/field_distinct_all.py
new file mode 100644
index 00000000..7a4a3acb
--- /dev/null
+++ b/tests/unit/schemas/distinct/field_distinct_all.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Color
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field(distinct_on="all")
diff --git a/tests/unit/schemas/distinct/field_distinct_specific_fields.py b/tests/unit/schemas/distinct/field_distinct_specific_fields.py
new file mode 100644
index 00000000..b6e4b242
--- /dev/null
+++ b/tests/unit/schemas/distinct/field_distinct_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Color
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Color, include="all")
+class ColorType:
+ pass
+
+
+@strawberry.type
+class Query:
+ colors: list[ColorType] = strawchemy.field(distinct_on=["name", "hex"])
diff --git a/tests/unit/schemas/distinct.py b/tests/unit/schemas/distinct/type_distinct_manual_enum.py
similarity index 100%
rename from tests/unit/schemas/distinct.py
rename to tests/unit/schemas/distinct/type_distinct_manual_enum.py
diff --git a/tests/unit/schemas/filters/field_filter_auto_generate.py b/tests/unit/schemas/filters/field_filter_auto_generate.py
new file mode 100644
index 00000000..6f72b2cf
--- /dev/null
+++ b/tests/unit/schemas/filters/field_filter_auto_generate.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field(filter_input=True)
diff --git a/tests/unit/schemas/order/auto_order_by.py b/tests/unit/schemas/order/auto_order_by.py
deleted file mode 100644
index a8a96e21..00000000
--- a/tests/unit/schemas/order/auto_order_by.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import annotations
-
-import strawberry
-
-from strawchemy import Strawchemy
-from tests.unit.models import Group
-
-strawchemy = Strawchemy("postgresql")
-
-
-@strawchemy.type(Group, include="all", child_order_by=True)
-class GroupType: ...
-
-
-@strawberry.type
-class Query:
- group: list[GroupType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/field_order_by_all.py b/tests/unit/schemas/order/field_order_by_all.py
new file mode 100644
index 00000000..9aec1336
--- /dev/null
+++ b/tests/unit/schemas/order/field_order_by_all.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field(order_by="all")
diff --git a/tests/unit/schemas/order/field_order_by_specific_fields.py b/tests/unit/schemas/order/field_order_by_specific_fields.py
new file mode 100644
index 00000000..91c9a19a
--- /dev/null
+++ b/tests/unit/schemas/order/field_order_by_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Container, include="all")
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ containers: list[ContainerType] = strawchemy.field(order_by=["fruits", "vegetables"])
diff --git a/tests/unit/schemas/order/order_config_all.py b/tests/unit/schemas/order/order_config_all.py
new file mode 100644
index 00000000..abaed7d4
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_all.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by="all"))
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/order_config_all_with_field_override.py b/tests/unit/schemas/order/order_config_all_with_field_override.py
new file mode 100644
index 00000000..d71294b0
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_all_with_field_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by="all"))
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field(order_by=["name"])
diff --git a/tests/unit/schemas/order/order_config_empty.py b/tests/unit/schemas/order/order_config_empty.py
new file mode 100644
index 00000000..aac1b568
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_empty.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by=[]))
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/order_config_empty_with_empty_type_override.py b/tests/unit/schemas/order/order_config_empty_with_empty_type_override.py
new file mode 100644
index 00000000..11f3cbf3
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_empty_with_empty_type_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Container, include="all", order=[])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/order_config_specific_fields.py b/tests/unit/schemas/order/order_config_specific_fields.py
new file mode 100644
index 00000000..b48ab41e
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by=["name", "sweetness"]))
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/order_config_specific_fields_with_type_override.py b/tests/unit/schemas/order/order_config_specific_fields_with_type_override.py
new file mode 100644
index 00000000..bb9c5573
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_specific_fields_with_type_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Container, include="all", order=["fruits", "vegetables"])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/order/order_config_with_field_override.py b/tests/unit/schemas/order/order_config_with_field_override.py
new file mode 100644
index 00000000..448c573f
--- /dev/null
+++ b/tests/unit/schemas/order/order_config_with_field_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by=["name"]))
+
+
+@strawchemy.type(Fruit, include="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field(order_by=["sweetness"])
diff --git a/tests/unit/schemas/order/type_order_by.py b/tests/unit/schemas/order/type_order_by.py
index 022c8828..1fd982da 100644
--- a/tests/unit/schemas/order/type_order_by.py
+++ b/tests/unit/schemas/order/type_order_by.py
@@ -12,7 +12,7 @@
class SQLDataTypesOrderBy: ...
-@strawchemy.type(SQLDataTypes, include="all", order_by=SQLDataTypesOrderBy)
+@strawchemy.type(SQLDataTypes, include="all", order=SQLDataTypesOrderBy)
class SQLDataTypesType: ...
diff --git a/tests/unit/schemas/order/type_order_by_specific_fields.py b/tests/unit/schemas/order/type_order_by_specific_fields.py
new file mode 100644
index 00000000..075e6806
--- /dev/null
+++ b/tests/unit/schemas/order/type_order_by_specific_fields.py
@@ -0,0 +1,17 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import SQLDataTypes
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(SQLDataTypes, include="all", order=["str_col", "int_col"])
+class SQLDataTypesType: ...
+
+
+@strawberry.type
+class Query:
+ sql_data_types: list[SQLDataTypesType] = strawchemy.field()
diff --git a/tests/unit/schemas/override/override_argument.py b/tests/unit/schemas/override/override_argument.py
index a41b6520..fc76c887 100644
--- a/tests/unit/schemas/override/override_argument.py
+++ b/tests/unit/schemas/override/override_argument.py
@@ -8,7 +8,7 @@
strawchemy = Strawchemy("postgresql")
-@strawchemy.type(Fruit, include="all", child_pagination=True, child_order_by=True)
+@strawchemy.type(Fruit, include="all", paginate="all", order="all")
class FruitType:
name: int
diff --git a/tests/unit/schemas/pagination/children_pagination.py b/tests/unit/schemas/pagination/children_pagination.py
index 2bcd129d..727e6bd1 100644
--- a/tests/unit/schemas/pagination/children_pagination.py
+++ b/tests/unit/schemas/pagination/children_pagination.py
@@ -8,7 +8,7 @@
strawchemy = Strawchemy("postgresql")
-@strawchemy.type(Fruit, include="all", child_pagination=True)
+@strawchemy.type(Fruit, include="all", paginate="all")
class FruitType:
pass
diff --git a/tests/unit/schemas/pagination/children_pagination_defaults.py b/tests/unit/schemas/pagination/children_pagination_defaults.py
index 9c81a1c5..3326df77 100644
--- a/tests/unit/schemas/pagination/children_pagination_defaults.py
+++ b/tests/unit/schemas/pagination/children_pagination_defaults.py
@@ -9,7 +9,7 @@
strawchemy = Strawchemy("postgresql")
-@strawchemy.type(Fruit, include="all", child_pagination=DefaultOffsetPagination(limit=10, offset=10))
+@strawchemy.type(Fruit, include="all", paginate="all", default_pagination=DefaultOffsetPagination(limit=10, offset=10))
class FruitType:
pass
diff --git a/tests/unit/schemas/pagination/paginate_all_with_default.py b/tests/unit/schemas/pagination/paginate_all_with_default.py
new file mode 100644
index 00000000..85d70fd1
--- /dev/null
+++ b/tests/unit/schemas/pagination/paginate_all_with_default.py
@@ -0,0 +1,19 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from strawchemy.schema.pagination import DefaultOffsetPagination
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Fruit, include="all", paginate="all", default_pagination=DefaultOffsetPagination(limit=20, offset=5))
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruit_with_custom_default: list[FruitType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/paginate_and_order_combined.py b/tests/unit/schemas/pagination/paginate_and_order_combined.py
new file mode 100644
index 00000000..42affe2b
--- /dev/null
+++ b/tests/unit/schemas/pagination/paginate_and_order_combined.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+# Different fields for paginate vs order
+@strawchemy.type(Container, include="all", paginate=["fruits"], order=["vegetables"])
+class ContainerType1:
+ pass
+
+
+# Overlapping fields
+@strawchemy.type(Container, include="all", paginate=["fruits", "vegetables"], order=["fruits"])
+class ContainerType2:
+ pass
+
+
+# All + specific
+@strawchemy.type(Container, include="all", paginate="all", order=["fruits"])
+class ContainerType3:
+ pass
+
+
+@strawberry.type
+class Query:
+ container1: list[ContainerType1] = strawchemy.field()
+ container2: list[ContainerType2] = strawchemy.field()
+ container3: list[ContainerType3] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/paginate_empty.py b/tests/unit/schemas/pagination/paginate_empty.py
new file mode 100644
index 00000000..c9d25536
--- /dev/null
+++ b/tests/unit/schemas/pagination/paginate_empty.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Container, include="all", paginate=[])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/paginate_specific_fields.py b/tests/unit/schemas/pagination/paginate_specific_fields.py
new file mode 100644
index 00000000..d829b834
--- /dev/null
+++ b/tests/unit/schemas/pagination/paginate_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy
+from tests.unit.models import Container
+
+strawchemy = Strawchemy("postgresql")
+
+
+@strawchemy.type(Container, include="all", paginate=["fruits"])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/pagination_config_default.py b/tests/unit/schemas/pagination/pagination_config_default.py
index 46134b59..559b7645 100644
--- a/tests/unit/schemas/pagination/pagination_config_default.py
+++ b/tests/unit/schemas/pagination/pagination_config_default.py
@@ -5,7 +5,7 @@
from strawchemy import Strawchemy, StrawchemyConfig
from tests.unit.models import Fruit
-strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination=True))
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination="all"))
@strawchemy.type(Fruit, include="all")
diff --git a/tests/unit/schemas/pagination/pagination_config_empty.py b/tests/unit/schemas/pagination/pagination_config_empty.py
new file mode 100644
index 00000000..9d219425
--- /dev/null
+++ b/tests/unit/schemas/pagination/pagination_config_empty.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Container
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination=[]))
+
+
+@strawchemy.type(Container, include="all")
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py b/tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py
new file mode 100644
index 00000000..dfd1c96c
--- /dev/null
+++ b/tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Container
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination=[]))
+
+
+@strawchemy.type(Container, include="all", paginate=["fruits"])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/pagination_config_specific_fields.py b/tests/unit/schemas/pagination/pagination_config_specific_fields.py
new file mode 100644
index 00000000..69f7b738
--- /dev/null
+++ b/tests/unit/schemas/pagination/pagination_config_specific_fields.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Container
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination=["fruits", "vegetables"]))
+
+
+@strawchemy.type(Container, include="all")
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/pagination_config_with_type_override.py b/tests/unit/schemas/pagination/pagination_config_with_type_override.py
new file mode 100644
index 00000000..5c557728
--- /dev/null
+++ b/tests/unit/schemas/pagination/pagination_config_with_type_override.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Container
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination=["fruits"]))
+
+
+@strawchemy.type(Container, include="all", paginate=["vegetables"])
+class ContainerType:
+ pass
+
+
+@strawberry.type
+class Query:
+ container: list[ContainerType] = strawchemy.field()
diff --git a/tests/unit/schemas/pagination/pagination_default_limit.py b/tests/unit/schemas/pagination/pagination_default_limit.py
index d815418c..36ac8f21 100644
--- a/tests/unit/schemas/pagination/pagination_default_limit.py
+++ b/tests/unit/schemas/pagination/pagination_default_limit.py
@@ -8,7 +8,7 @@
strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination_default_limit=5))
-@strawchemy.type(Fruit, include="all", child_pagination=True)
+@strawchemy.type(Fruit, include="all", paginate="all")
class FruitType:
pass
diff --git a/tests/unit/schemas/pagination/pagination_default_offset.py b/tests/unit/schemas/pagination/pagination_default_offset.py
new file mode 100644
index 00000000..a0719eb7
--- /dev/null
+++ b/tests/unit/schemas/pagination/pagination_default_offset.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+import strawberry
+
+from strawchemy import Strawchemy, StrawchemyConfig
+from tests.unit.models import Fruit
+
+strawchemy = Strawchemy(StrawchemyConfig("postgresql", pagination_default_offset=5))
+
+
+@strawchemy.type(Fruit, include="all", paginate="all")
+class FruitType:
+ pass
+
+
+@strawberry.type
+class Query:
+ fruits: list[FruitType] = strawchemy.field(pagination=True)