feat: removed created_by from read_only serializer field, and ProjectMemberEndpoint updates (#5260)

* feat: removed created by and created_at as readonly fields from issue serializers

* feat: modified serializers for accepting created_by, and changed workspacememberendpoint to projectmemberendpoint

* fix: code suggestions

* chore: resolved code review

* chore: removed unused imports

* fix: passed default user if created_by is absent, and permission classes

* fix: default value for the issue creation

* dev: fix nomenclature

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
This commit is contained in:
Henit Chobisa 2024-07-30 13:03:14 +05:30 committed by GitHub
parent 35e58e9ec7
commit 3c2aec2776
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 132 additions and 71 deletions

View file

@ -53,7 +53,6 @@ class IssueSerializer(BaseSerializer):
"id",
"workspace",
"project",
"created_by",
"updated_by",
"updated_at",
]
@ -338,9 +337,7 @@ class IssueAttachmentSerializer(BaseSerializer):
"workspace",
"project",
"issue",
"created_by",
"updated_by",
"created_at",
"updated_at",
]
@ -433,3 +430,4 @@ class IssueExpandSerializer(BaseSerializer):
"created_at",
"updated_at",
]

View file

@ -1,13 +1,13 @@
from django.urls import path
from plane.api.views import (
WorkspaceMemberAPIEndpoint,
ProjectMemberAPIEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/members/",
WorkspaceMemberAPIEndpoint.as_view(),
"workspaces/<str:slug>/projects/<str:project_id>/members/",
ProjectMemberAPIEndpoint.as_view(),
name="users",
),
]

View file

@ -25,6 +25,7 @@ from .module import (
ModuleArchiveUnarchiveAPIEndpoint,
)
from .member import WorkspaceMemberAPIEndpoint
from .member import ProjectMemberAPIEndpoint
from .inbox import InboxIssueAPIEndpoint

View file

@ -661,61 +661,74 @@ class CycleIssueAPIEndpoint(BaseAPIView):
workspace__slug=slug, project_id=project_id, pk=cycle_id
)
issues = Issue.objects.filter(
pk__in=issues, workspace__slug=slug, project_id=project_id
).values_list("id", flat=True)
if (
cycle.end_date is not None
and cycle.end_date < timezone.now().date()
):
return Response(
{
"error": "The Cycle has already been completed so no new issues can be added"
},
status=status.HTTP_400_BAD_REQUEST,
)
# Get all CycleIssues already created
cycle_issues = list(CycleIssue.objects.filter(issue_id__in=issues))
update_cycle_issue_activity = []
record_to_create = []
records_to_update = []
for issue in issues:
cycle_issue = [
cycle_issue
for cycle_issue in cycle_issues
if str(cycle_issue.issue_id) in issues
]
# Update only when cycle changes
if len(cycle_issue):
if cycle_issue[0].cycle_id != cycle_id:
update_cycle_issue_activity.append(
{
"old_cycle_id": str(cycle_issue[0].cycle_id),
"new_cycle_id": str(cycle_id),
"issue_id": str(cycle_issue[0].issue_id),
}
)
cycle_issue[0].cycle_id = cycle_id
records_to_update.append(cycle_issue[0])
else:
record_to_create.append(
CycleIssue(
project_id=project_id,
workspace=cycle.workspace,
created_by=request.user,
updated_by=request.user,
cycle=cycle,
issue_id=issue,
)
)
CycleIssue.objects.bulk_create(
record_to_create,
batch_size=10,
ignore_conflicts=True,
cycle_issues = list(
CycleIssue.objects.filter(
~Q(cycle_id=cycle_id), issue_id__in=issues
)
)
CycleIssue.objects.bulk_update(
records_to_update,
["cycle"],
existing_issues = [
str(cycle_issue.issue_id)
for cycle_issue in cycle_issues
if str(cycle_issue.issue_id) in issues
]
new_issues = list(set(issues) - set(existing_issues))
# New issues to create
created_records = CycleIssue.objects.bulk_create(
[
CycleIssue(
project_id=project_id,
workspace_id=cycle.workspace_id,
cycle_id=cycle_id,
issue_id=issue,
)
for issue in new_issues
],
ignore_conflicts=True,
batch_size=10,
)
# Updated Issues
updated_records = []
update_cycle_issue_activity = []
# Iterate over each cycle_issue in cycle_issues
for cycle_issue in cycle_issues:
old_cycle_id = cycle_issue.cycle_id
# Update the cycle_issue's cycle_id
cycle_issue.cycle_id = cycle_id
# Add the modified cycle_issue to the records_to_update list
updated_records.append(cycle_issue)
# Record the update activity
update_cycle_issue_activity.append(
{
"old_cycle_id": str(old_cycle_id),
"new_cycle_id": str(cycle_id),
"issue_id": str(cycle_issue.issue_id),
}
)
# Update the cycle issues
CycleIssue.objects.bulk_update(
updated_records, ["cycle_id"], batch_size=100
)
# Capture Issue Activity
issue_activity.delay(
type="cycle.activity.created",
requested_data=json.dumps({"cycles_list": str(issues)}),
requested_data=json.dumps({"cycles_list": issues}),
actor_id=str(self.request.user.id),
issue_id=None,
project_id=str(self.kwargs.get("project_id", None)),
@ -723,13 +736,14 @@ class CycleIssueAPIEndpoint(BaseAPIView):
{
"updated_cycle_issues": update_cycle_issue_activity,
"created_cycle_issues": serializers.serialize(
"json", record_to_create
"json", created_records
),
}
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
# Return all Cycle Issues
return Response(
CycleIssueSerializer(self.get_queryset(), many=True).data,

View file

@ -151,6 +151,25 @@ class IssueAPIEndpoint(BaseAPIView):
).distinct()
def get(self, request, slug, project_id, pk=None):
external_id = request.GET.get("external_id")
external_source = request.GET.get("external_source")
if external_id and external_source:
issue = Issue.objects.get(
external_id=external_id,
external_source=external_source,
workspace__slug=slug,
project_id=project_id,
)
return Response(
IssueSerializer(
issue,
fields=self.fields,
expand=self.expand,
).data,
status=status.HTTP_200_OK,
)
if pk:
issue = Issue.issue_objects.annotate(
sub_issues_count=Issue.issue_objects.filter(
@ -315,8 +334,11 @@ class IssueAPIEndpoint(BaseAPIView):
project_id=project_id,
pk=serializer.data["id"],
).first()
issue.created_at = request.data.get("created_at")
issue.save(update_fields=["created_at"])
issue.created_at = request.data.get("created_at", timezone.now())
issue.created_by_id = request.data.get(
"created_by", request.user.id
)
issue.save(update_fields=["created_at", "created_by"])
# Track the issue
issue_activity.delay(
@ -610,14 +632,20 @@ class IssueLinkAPIEndpoint(BaseAPIView):
project_id=project_id,
issue_id=issue_id,
)
link = IssueLink.objects.get(pk=serializer.data["id"])
link.created_by_id = request.data.get(
"created_by", request.user.id
)
link.save(update_fields=["created_by"])
issue_activity.delay(
type="link.activity.created",
requested_data=json.dumps(
serializer.data, cls=DjangoJSONEncoder
),
actor_id=str(self.request.user.id),
issue_id=str(self.kwargs.get("issue_id")),
project_id=str(self.kwargs.get("project_id")),
actor_id=str(link.created_by_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
)
@ -771,12 +799,24 @@ class IssueCommentAPIEndpoint(BaseAPIView):
issue_id=issue_id,
actor=request.user,
)
issue_comment = IssueComment.objects.get(
pk=serializer.data.get("id")
)
# Update the created_at and the created_by and save the comment
issue_comment.created_at = request.data.get(
"created_at", timezone.now()
)
issue_comment.created_by_id = request.data.get(
"created_by", request.user.id
)
issue_comment.save(update_fields=["created_at", "created_by"])
issue_activity.delay(
type="comment.activity.created",
requested_data=json.dumps(
serializer.data, cls=DjangoJSONEncoder
),
actor_id=str(self.request.user.id),
actor_id=str(issue_comment.created_by_id),
issue_id=str(self.kwargs.get("issue_id")),
project_id=str(self.kwargs.get("project_id")),
current_instance=None,

View file

@ -21,11 +21,19 @@ from plane.db.models import (
ProjectMember,
)
from plane.app.permissions import (
ProjectMemberPermission,
)
# API endpoint to get and insert users inside the workspace
class WorkspaceMemberAPIEndpoint(BaseAPIView):
class ProjectMemberAPIEndpoint(BaseAPIView):
permission_classes = [
ProjectMemberPermission,
]
# Get all the users that are present inside the workspace
def get(self, request, slug):
def get(self, request, slug, project_id):
# Check if the workspace exists
if not Workspace.objects.filter(slug=slug).exists():
return Response(
@ -34,14 +42,14 @@ class WorkspaceMemberAPIEndpoint(BaseAPIView):
)
# Get the workspace members that are present inside the workspace
workspace_members = WorkspaceMember.objects.filter(
workspace__slug=slug
)
project_members = ProjectMember.objects.filter(
project_id=project_id, workspace__slug=slug
).values_list("member_id", flat=True)
# Get all the users that are present inside the workspace
users = UserLiteSerializer(
User.objects.filter(
id__in=workspace_members.values_list("member_id", flat=True)
id__in=project_members,
),
many=True,
).data
@ -49,14 +57,13 @@ class WorkspaceMemberAPIEndpoint(BaseAPIView):
return Response(users, status=status.HTTP_200_OK)
# Insert a new user inside the workspace, and assign the user to the project
def post(self, request, slug):
def post(self, request, slug, project_id):
# Check if user with email already exists, and send bad request if it's
# not present, check for workspace and valid project mandat
# ------------------- Validation -------------------
if (
request.data.get("email") is None
or request.data.get("display_name") is None
or request.data.get("project_id") is None
):
return Response(
{
@ -76,9 +83,7 @@ class WorkspaceMemberAPIEndpoint(BaseAPIView):
)
workspace = Workspace.objects.filter(slug=slug).first()
project = Project.objects.filter(
pk=request.data.get("project_id")
).first()
project = Project.objects.filter(pk=project_id).first()
if not all([workspace, project]):
return Response(
@ -145,3 +150,4 @@ class WorkspaceMemberAPIEndpoint(BaseAPIView):
user_data = UserLiteSerializer(user).data
return Response(user_data, status=status.HTTP_201_CREATED)

View file

@ -593,7 +593,8 @@ def create_issue_activity(
epoch=epoch,
)
issue_activity.created_at = issue.created_at
issue_activity.save(update_fields=["created_at"])
issue_activity.actor_id = issue.created_by_id
issue_activity.save(update_fields=["created_at", "actor_id"])
requested_data = (
json.loads(requested_data) if requested_data is not None else None
)
@ -1773,3 +1774,4 @@ def issue_activity(
except Exception as e:
log_exception(e)
return