Recursive complex filtering #181

This commit is contained in:
2025-09-28 01:09:46 +02:00
parent a12a4082db
commit 20e5da5770
16 changed files with 249 additions and 48 deletions

View File

@@ -3,6 +3,7 @@ from typing import Type, Dict, List
import strawberry
from cpl.core.typing import T
from cpl.dependency import get_provider
from cpl.graphql.abc.strawberry_protocol import StrawberryProtocol
@@ -14,7 +15,12 @@ class CollectionGraphTypeFactory:
if node_type in cls._cache:
return cls._cache[node_type]
gql_node = node_type().to_strawberry() if hasattr(node_type, "to_strawberry") else node_type
node_t = get_provider().get_service(node_type)
if not node_t:
raise ValueError(f"Node type '{node_type.__name__}' not registered in service provider")
gql_node = node_t.to_strawberry() if hasattr(node_type, "to_strawberry") else node_type
gql_type = strawberry.type(
type(

View File

@@ -1,3 +1,5 @@
from typing import Type
from cpl.core.typing import T
from cpl.graphql.schema.filter.bool_filter import BoolFilter
from cpl.graphql.schema.filter.date_filter import DateFilter
@@ -10,6 +12,9 @@ class Filter(Input[T]):
def __init__(self):
Input.__init__(self)
def filter_field(self, name: str, filter_type: Type["Filter"]):
self.field(name, filter_type())
def string_field(self, name: str):
self.field(name, StringFilter())

View File

@@ -5,6 +5,7 @@ import strawberry
from cpl.core.typing import T
from cpl.graphql.abc.strawberry_protocol import StrawberryProtocol
from cpl.graphql.schema.field import Field
from cpl.graphql.utils.type_collector import TypeCollector
_PYTHON_KEYWORDS = {"in", "not", "is", "and", "or"}
@@ -18,12 +19,10 @@ class Input(StrawberryProtocol, Generic[T]):
def field(self, name: str, typ: Union[type, "Input"], optional: bool = True):
self._fields[name] = Field(name, typ, optional=optional)
_registry: dict[type, Type] = {}
def to_strawberry(self) -> Type:
cls = self.__class__
if cls in self._registry:
return self._registry[cls]
if TypeCollector.has(cls):
return TypeCollector.get(cls)
annotations = {}
namespace = {}
@@ -50,5 +49,5 @@ class Input(StrawberryProtocol, Generic[T]):
namespace["__annotations__"] = annotations
gql_type = strawberry.input(type(f"{cls.__name__}", (), namespace))
Input._registry[cls] = gql_type
TypeCollector.set(cls, gql_type)
return gql_type

View File

@@ -12,6 +12,7 @@ from cpl.graphql.schema.collection import Collection, CollectionGraphTypeFactory
from cpl.graphql.schema.field import Field
from cpl.graphql.schema.sort.sort_order import SortOrder
from cpl.graphql.typing import Resolver
from cpl.graphql.utils.type_collector import TypeCollector
class Query(StrawberryProtocol):
@@ -54,6 +55,9 @@ class Query(StrawberryProtocol):
def list_field(self, name: str, t: type, resolver: Resolver = None) -> Field:
return self.field(name, list[t], resolver)
def object_field(self, name: str, t: Type[StrawberryProtocol], resolver: Resolver = None) -> Field:
return self.field(name, t().to_strawberry(), resolver)
def with_query(self, name: str, subquery_cls: Type["Query"]):
sub = self._provider.get_service(subquery_cls)
if not sub:
@@ -221,6 +225,10 @@ class Query(StrawberryProtocol):
) from e
def to_strawberry(self) -> Type:
cls = self.__class__
if TypeCollector.has(cls):
return TypeCollector.get(cls)
annotations: dict[str, Any] = {}
namespace: dict[str, Any] = {}
@@ -229,4 +237,6 @@ class Query(StrawberryProtocol):
namespace[name] = self._field_to_strawberry(f)
namespace["__annotations__"] = annotations
return strawberry.type(type(f"{self.__class__.__name__.replace("GraphType", "")}", (), namespace))
gql_type = strawberry.type(type(f"{self.__class__.__name__.replace("GraphType", "")}", (), namespace))
TypeCollector.set(cls, gql_type)
return gql_type

View File

@@ -0,0 +1,17 @@
from typing import Type
class TypeCollector:
_registry: dict[type, Type] = {}
@classmethod
def has(cls, base: type) -> bool:
return base in cls._registry
@classmethod
def get(cls, base: type) -> Type:
return cls._registry[base]
@classmethod
def set(cls, base: type, gql_type: Type):
cls._registry[base] = gql_type