diff --git a/terraso_backend/apps/graphql/schema/schema.graphql b/terraso_backend/apps/graphql/schema/schema.graphql index b75ec24d1..a495d3dbf 100644 --- a/terraso_backend/apps/graphql/schema/schema.graphql +++ b/terraso_backend/apps/graphql/schema/schema.graphql @@ -556,15 +556,15 @@ type StoryMapNode implements Node { createdBy: UserNode isPublished: Boolean! publishedAt: DateTime - membershipList: CollaborationMembershipListNode + membershipList: ProjectMembershipListNode } -type CollaborationMembershipListNode implements Node { +type ProjectMembershipListNode implements Node { membershipType: CollaborationMembershipListMembershipTypeChoices! - memberships(offset: Int, before: String, after: String, first: Int, last: Int, user: ID, user_In: [ID], userRole: String, user_Email_Icontains: String, user_Email_In: [String], membershipStatus: CollaborationMembershipMembershipStatusChoices, user_Email_Not: String): CollaborationMembershipNodeConnection! + + """The ID of the object""" id: ID! - accountMembership: CollaborationMembershipNode - membershipsCount: Int + memberships: ProjectMembershipNode! } """An enumeration.""" @@ -576,33 +576,15 @@ enum CollaborationMembershipListMembershipTypeChoices { CLOSED } -type CollaborationMembershipNodeConnection { - """Pagination data for this connection.""" - pageInfo: PageInfo! - - """Contains the nodes in this connection.""" - edges: [CollaborationMembershipNodeEdge!]! - totalCount: Int! -} - -""" -A Relay edge containing a `CollaborationMembershipNode` and its cursor. -""" -type CollaborationMembershipNodeEdge { - """The item at the end of the edge""" - node: CollaborationMembershipNode! - - """A cursor for use in pagination""" - cursor: String! -} - -type CollaborationMembershipNode implements Node { - membershipList: CollaborationMembershipListNode! +type ProjectMembershipNode implements Node { + membershipList: ProjectMembershipListNode! user: UserNode - userRole: String! membershipStatus: CollaborationMembershipMembershipStatusChoices! pendingEmail: String + + """The ID of the object""" id: ID! + userRole: UserRole! } """An enumeration.""" @@ -614,6 +596,12 @@ enum CollaborationMembershipMembershipStatusChoices { PENDING } +enum UserRole { + VIEWER + CONTRIBUTOR + MANAGER +} + type StoryMapNodeConnection { """Pagination data for this connection.""" pageInfo: PageInfo! @@ -636,7 +624,6 @@ type ProjectNode implements Node { updatedAt: DateTime! name: String! description: String! - membershipList: CollaborationMembershipListNode! privacy: ProjectManagementProjectPrivacyChoices! archived: Boolean! siteSet( @@ -655,6 +642,7 @@ type ProjectNode implements Node { orderBy: String ): SiteNodeConnection! id: ID! + membershipList: ProjectMembershipListNode! } """An enumeration.""" @@ -1653,6 +1641,15 @@ type StoryMapMembershipSaveMutationPayload { clientMutationId: String } +type CollaborationMembershipNode implements Node { + membershipList: ProjectMembershipListNode! + user: UserNode + userRole: String! + membershipStatus: CollaborationMembershipMembershipStatusChoices! + pendingEmail: String + id: ID! +} + input StoryMapMembershipSaveMutationInput { userRole: String userEmails: [String]! diff --git a/terraso_backend/apps/project_management/graphql/projects.py b/terraso_backend/apps/project_management/graphql/projects.py index 6f8eb8daf..bf93475d8 100644 --- a/terraso_backend/apps/project_management/graphql/projects.py +++ b/terraso_backend/apps/project_management/graphql/projects.py @@ -25,10 +25,11 @@ from graphene_django.filter import TypedFilter from apps.audit_logs import api as log_api +from apps.collaboration.graphql.memberships import CollaborationMembershipFilterSet from apps.collaboration.graphql.memberships import ( CollaborationMembershipNode as MembershipNode, ) -from apps.collaboration.models import Membership +from apps.collaboration.models import Membership, MembershipList from apps.core.models import User from apps.graphql.exceptions import GraphQLValidationException from apps.graphql.schema.commons import ( @@ -42,6 +43,7 @@ membership_deleted_signal, membership_updated_signal, ) +from apps.project_management import collaboration_roles from apps.project_management.models import Project from apps.project_management.models.sites import Site @@ -54,6 +56,44 @@ class Meta: fields = {"name": ["exact", "icontains"]} +class UserRole(graphene.Enum): + VIEWER = collaboration_roles.ROLE_VIEWER + CONTRIBUTOR = collaboration_roles.ROLE_CONTRIBUTOR + MANAGER = collaboration_roles.ROLE_MANAGER + + +class ProjectMembershipNode(DjangoObjectType): + class Meta: + model = Membership + fields = ("membership_list", "user", "membership_status", "pending_email") + filterset_class = CollaborationMembershipFilterSet + interfaces = (relay.Node,) + connection_class = TerrasoConnection + + user_role = graphene.Field(UserRole, required=True) + + def resolve_user_role(self, info): + match self.user_role: + case "VIEWER": + return UserRole.VIEWER + case "CONTRIBUTOR": + return UserRole.CONTRIBUTOR + case "MANAGER": + return UserRole.MANAGER + case _: + raise Exception(f"Unexpected user role: {self.user_role}") + + +class ProjectMembershipListNode(DjangoObjectType): + memberships = graphene.Field(ProjectMembershipNode, required=True) + + class Meta: + model = MembershipList + fields = ("membership_type",) + interfaces = (relay.Node,) + connection_class = TerrasoConnection + + class ProjectNode(DjangoObjectType): id = graphene.ID(source="pk", required=True) @@ -66,7 +106,6 @@ class Meta: "privacy", "description", "updated_at", - "membership_list", "site_set", "archived", ) @@ -74,6 +113,8 @@ class Meta: interfaces = (relay.Node,) connection_class = TerrasoConnection + membership_list = graphene.Field(ProjectMembershipListNode, required=True) + class ProjectPrivacy(graphene.Enum): PRIVATE = Project.PRIVATE diff --git a/terraso_backend/apps/project_management/models/projects.py b/terraso_backend/apps/project_management/models/projects.py index eec01e454..0f02bc6c9 100644 --- a/terraso_backend/apps/project_management/models/projects.py +++ b/terraso_backend/apps/project_management/models/projects.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see https://www.gnu.org/licenses/. -from typing import Literal, Optional - from django.db import models from django.utils.translation import gettext_lazy as _ @@ -91,20 +89,6 @@ def create_membership_list() -> MembershipList: enroll_method=MembershipList.ENROLL_METHOD_JOIN, ) - @classmethod - def create_project( - cls, - user: User, - name: str, - description: Optional[str], - privacy: Literal["private", "public"], - ): - membership_list = cls.create_membership_list() - Membership.objects.create( - membership_list=membership_list, user=user, user_role=ROLE_MANAGER, pending_email=None - ) - return cls.objects.create(name=name, description=description, privacy=privacy) - def is_manager(self, user: User) -> bool: return self.manager_memberships.filter(user=user).exists() diff --git a/terraso_backend/apps/project_management/permission_rules.py b/terraso_backend/apps/project_management/permission_rules.py index e3e49f3fc..cd7cbbf46 100644 --- a/terraso_backend/apps/project_management/permission_rules.py +++ b/terraso_backend/apps/project_management/permission_rules.py @@ -91,7 +91,6 @@ def allowed_to_change_user_project_role(user, context): project = context["project"] requester_membership = context["requester_membership"] target_membership = context["target_membership"] - print(requester_membership.user_role) return ( project.membership_list == requester_membership.membership_list