diff --git a/baseapp-profiles/baseapp_profiles/graphql/filters.py b/baseapp-profiles/baseapp_profiles/graphql/filters.py index ef8da931..822fb917 100644 --- a/baseapp-profiles/baseapp_profiles/graphql/filters.py +++ b/baseapp-profiles/baseapp_profiles/graphql/filters.py @@ -1,8 +1,9 @@ import django_filters import swapper -from django.db.models import Q +from django.db.models import Case, IntegerField, Q, Value, When Profile = swapper.load_model("baseapp_profiles", "Profile") +ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") class ProfileFilter(django_filters.FilterSet): @@ -22,3 +23,35 @@ class Meta: def filter_q(self, queryset, name, value): return queryset.filter(Q(name__icontains=value) | Q(url_paths__path__icontains=value)) + + +class MemberOrderingFilter(django_filters.OrderingFilter): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.extra["choices"] += [ + ("status", "Status"), + ] + + def filter(self, qs, value): + if value is None: + return qs + + if any(v == "status" for v in value): + status_order = Case( + When(status=ProfileUserRole.ProfileRoleStatus.PENDING.value, then=Value(1)), + When(status=ProfileUserRole.ProfileRoleStatus.INACTIVE.value, then=Value(2)), + When(status=ProfileUserRole.ProfileRoleStatus.ACTIVE.value, then=Value(3)), + default=Value(4), + output_field=IntegerField(), + ) + return qs.order_by(status_order) + + return super().filter(qs, value) + + +class MemberFilter(django_filters.FilterSet): + order_by = MemberOrderingFilter() + + class Meta: + model = ProfileUserRole + fields = ["role", "order_by"] diff --git a/baseapp-profiles/baseapp_profiles/graphql/object_types.py b/baseapp-profiles/baseapp_profiles/graphql/object_types.py index d31137f9..eaed59e2 100644 --- a/baseapp-profiles/baseapp_profiles/graphql/object_types.py +++ b/baseapp-profiles/baseapp_profiles/graphql/object_types.py @@ -12,7 +12,7 @@ from graphene import relay from graphene_django.filter import DjangoFilterConnectionField -from .filters import ProfileFilter +from .filters import MemberFilter, ProfileFilter Profile = swapper.load_model("baseapp_profiles", "Profile") ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") @@ -30,7 +30,7 @@ class Meta: model = ProfileUserRole interfaces = [relay.Node] fields = ["id", "pk", "user", "role", "created", "modified", "status"] - filter_fields = ["role"] + filterset_class = MemberFilter class ProfileUserRoleObjectType(DjangoObjectType, BaseProfileUserRoleObjectType): @@ -89,7 +89,9 @@ class BaseProfileObjectType: target = graphene.Field(lambda: ProfileInterface) image = ThumbnailField(required=False) banner_image = ThumbnailField(required=False) - members = DjangoFilterConnectionField(get_object_type_for_model(ProfileUserRole)) + members = DjangoFilterConnectionField( + get_object_type_for_model(ProfileUserRole), + ) class Meta: interfaces = interfaces @@ -124,6 +126,7 @@ def resolve_metadata(cls, instance, info): def resolve_members(cls, instance, info, **kwargs): if not info.context.user.has_perm("baseapp_profiles.view_profile_members", instance): return instance.members.none() + return instance.members.all() diff --git a/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py b/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py index 1559a77e..d784da00 100644 --- a/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py +++ b/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py @@ -3,7 +3,8 @@ from baseapp_pages.tests.factories import URLPathFactory from django.contrib.contenttypes.models import ContentType -from .factories import ProfileFactory +from ..models import ProfileUserRole +from .factories import ProfileFactory, ProfileUserRoleFactory pytestmark = pytest.mark.django_db @@ -144,6 +145,98 @@ def test_another_user_cant_view_members(graphql_user_client): assert content["data"]["profile"]["members"] +def test_members_ordered_by_status(django_user_client, graphql_user_client): + user = django_user_client.user + profile = ProfileFactory(owner=user) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.ACTIVE, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.PENDING, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.ACTIVE, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.INACTIVE, + ) + + response = graphql_user_client( + query=""" + query Profile($id: ID!, $orderBy: String) { + profile(id: $id) { + members(orderBy: $orderBy) { + edges { + node { + id + status + } + } + } + } + } + """, + variables={"id": profile.relay_id, "orderBy": "status"}, + ) + + content = response.json() + + members = content["data"]["profile"]["members"]["edges"] + statuses = [member["node"]["status"] for member in members] + + assert statuses == ["PENDING", "INACTIVE", "ACTIVE", "ACTIVE"] + + +def test_members_not_ordered_by_status(django_user_client, graphql_user_client): + user = django_user_client.user + profile = ProfileFactory(owner=user) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.ACTIVE, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.PENDING, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.ACTIVE, + ) + ProfileUserRoleFactory( + profile=profile, + status=ProfileUserRole.ProfileRoleStatus.INACTIVE, + ) + + response = graphql_user_client( + query=""" + query Profile($id: ID!) { + profile(id: $id) { + members { + edges { + node { + id + status + } + } + } + } + } + """, + variables={"id": profile.relay_id}, + ) + + content = response.json() + + members = content["data"]["profile"]["members"]["edges"] + statuses = [member["node"]["status"] for member in members] + + assert statuses == ["ACTIVE", "PENDING", "ACTIVE", "INACTIVE"] + + def test_search_profiles(graphql_user_client): profile1 = ProfileFactory(name="David") profile2 = ProfileFactory(name="Daniel") diff --git a/baseapp-profiles/setup.cfg b/baseapp-profiles/setup.cfg index 9f224bea..5a69b252 100644 --- a/baseapp-profiles/setup.cfg +++ b/baseapp-profiles/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = baseapp_profiles -version = 0.3.1 +version = 0.3.2 description = BaseApp Profiles long_description = file: README.md long_description_content_type = text/markdown