Merge branch 'develop' into chore/date_filters

This commit is contained in:
Nikhil 2023-10-09 16:00:52 +05:30 committed by GitHub
commit 0f31585620
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
207 changed files with 8295 additions and 2729 deletions

View file

@ -17,6 +17,7 @@ from .project import (
ProjectMemberEndpoint,
WorkspaceProjectDeployBoardEndpoint,
LeaveProjectEndpoint,
ProjectPublicCoverImagesEndpoint,
)
from .user import (
UserEndpoint,
@ -147,16 +148,13 @@ from .page import (
from .search import GlobalSearchEndpoint, IssueSearchEndpoint
from .gpt import GPTIntegrationEndpoint
from .external import GPTIntegrationEndpoint, ReleaseNotesEndpoint, UnsplashEndpoint
from .estimate import (
ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint,
)
from .release import ReleaseNotesEndpoint
from .inbox import InboxViewSet, InboxIssueViewSet, InboxIssuePublicViewSet
from .analytic import (
@ -169,4 +167,6 @@ from .analytic import (
from .notification import NotificationViewSet, UnreadNotificationEndpoint, MarkAllReadNotificationViewSet
from .exporter import ExportIssuesEndpoint
from .exporter import ExportIssuesEndpoint
from .config import ConfigurationEndpoint

View file

@ -0,0 +1,40 @@
# Python imports
import os
# Django imports
from django.conf import settings
# Third party imports
from rest_framework.permissions import AllowAny
from rest_framework import status
from rest_framework.response import Response
from sentry_sdk import capture_exception
# Module imports
from .base import BaseAPIView
class ConfigurationEndpoint(BaseAPIView):
permission_classes = [
AllowAny,
]
def get(self, request):
try:
data = {}
data["google"] = os.environ.get("GOOGLE_CLIENT_ID", None)
data["github"] = os.environ.get("GITHUB_CLIENT_ID", None)
data["github_app_name"] = os.environ.get("GITHUB_APP_NAME", None)
data["magic_login"] = (
bool(settings.EMAIL_HOST_USER) and bool(settings.EMAIL_HOST_PASSWORD)
) and os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "0") == "1"
data["email_password_login"] = (
os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1"
)
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)

View file

@ -2,9 +2,10 @@
import requests
# Third party imports
import openai
from rest_framework.response import Response
from rest_framework import status
import openai
from rest_framework.permissions import AllowAny
from sentry_sdk import capture_exception
# Django imports
@ -15,6 +16,7 @@ from .base import BaseAPIView
from plane.api.permissions import ProjectEntityPermission
from plane.db.models import Workspace, Project
from plane.api.serializers import ProjectLiteSerializer, WorkspaceLiteSerializer
from plane.utils.integrations.github import get_release_notes
class GPTIntegrationEndpoint(BaseAPIView):
@ -73,3 +75,44 @@ class GPTIntegrationEndpoint(BaseAPIView):
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class ReleaseNotesEndpoint(BaseAPIView):
def get(self, request):
try:
release_notes = get_release_notes()
return Response(release_notes, status=status.HTTP_200_OK)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class UnsplashEndpoint(BaseAPIView):
def get(self, request):
try:
query = request.GET.get("query", False)
page = request.GET.get("page", 1)
per_page = request.GET.get("per_page", 20)
url = (
f"https://api.unsplash.com/search/photos/?client_id={settings.UNSPLASH_ACCESS_KEY}&query={query}&page=${page}&per_page={per_page}"
if query
else f"https://api.unsplash.com/photos/?client_id={settings.UNSPLASH_ACCESS_KEY}&page={page}&per_page={per_page}"
)
headers = {
"Content-Type": "application/json",
}
resp = requests.get(url=url, headers=headers)
return Response(resp.json(), status=status.HTTP_200_OK)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)

View file

@ -370,6 +370,11 @@ class InboxIssueViewSet(BaseViewSet):
if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(request.user.id):
return Response({"error": "You cannot delete inbox issue"}, status=status.HTTP_400_BAD_REQUEST)
# Check the issue status
if inbox_issue.status in [-2, -1, 0, 2]:
# Delete the issue also
Issue.objects.filter(workspace__slug=slug, project_id=project_id, pk=inbox_issue.issue_id).delete()
inbox_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except InboxIssue.DoesNotExist:

View file

@ -710,10 +710,18 @@ class LabelViewSet(BaseViewSet):
ProjectMemberPermission,
]
def perform_create(self, serializer):
serializer.save(
project_id=self.kwargs.get("project_id"),
)
def create(self, request, slug, project_id):
try:
serializer = LabelSerializer(data=request.data)
if serializer.is_valid():
serializer.save(project_id=project_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except IntegrityError:
return Response({"error": "Label with the same name already exists in the project"}, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
capture_exception(e)
return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST)
def get_queryset(self):
return self.filter_queryset(
@ -2396,27 +2404,6 @@ class IssueDraftViewSet(BaseViewSet):
]
serializer_class = IssueFlatSerializer
model = Issue
def perform_update(self, serializer):
requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder)
current_instance = (
self.get_queryset().filter(pk=self.kwargs.get("pk", None)).first()
)
if current_instance is not None:
issue_activity.delay(
type="issue_draft.activity.updated",
requested_data=requested_data,
actor_id=str(self.request.user.id),
issue_id=str(self.kwargs.get("pk", None)),
project_id=str(self.kwargs.get("project_id", None)),
current_instance=json.dumps(
IssueSerializer(current_instance).data, cls=DjangoJSONEncoder
),
epoch=int(timezone.now().timestamp())
)
return super().perform_update(serializer)
def perform_destroy(self, instance):
@ -2610,6 +2597,47 @@ class IssueDraftViewSet(BaseViewSet):
)
def partial_update(self, request, slug, project_id, pk):
try:
issue = Issue.objects.get(
workspace__slug=slug, project_id=project_id, pk=pk
)
serializer = IssueSerializer(
issue, data=request.data, partial=True
)
if serializer.is_valid():
if(request.data.get("is_draft") is not None and not request.data.get("is_draft")):
serializer.save(created_at=timezone.now(), updated_at=timezone.now())
else:
serializer.save()
issue_activity.delay(
type="issue_draft.activity.updated",
requested_data=json.dumps(request.data, cls=DjangoJSONEncoder),
actor_id=str(self.request.user.id),
issue_id=str(self.kwargs.get("pk", None)),
project_id=str(self.kwargs.get("project_id", None)),
current_instance=json.dumps(
IssueSerializer(issue).data,
cls=DjangoJSONEncoder,
),
epoch=int(timezone.now().timestamp())
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except Issue.DoesNotExist:
return Response(
{"error": "Issue does not exists"},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def retrieve(self, request, slug, project_id, pk=None):
try:
issue = Issue.objects.get(

View file

@ -1,5 +1,6 @@
# Python imports
import jwt
import boto3
from datetime import datetime
# Django imports
@ -495,7 +496,7 @@ class ProjectMemberViewSet(BaseViewSet):
serializer_class = ProjectMemberAdminSerializer
model = ProjectMember
permission_classes = [
ProjectBasePermission,
ProjectMemberPermission,
]
search_fields = [
@ -617,7 +618,8 @@ class ProjectMemberViewSet(BaseViewSet):
return Response(status=status.HTTP_204_NO_CONTENT)
except ProjectMember.DoesNotExist:
return Response(
{"error": "Project Member does not exist"}, status=status.HTTP_400_BAD_REQUEST
{"error": "Project Member does not exist"},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
capture_exception(e)
@ -1094,7 +1096,7 @@ class ProjectMemberEndpoint(BaseAPIView):
project_id=project_id,
workspace__slug=slug,
member__is_bot=False,
).select_related("project", "member")
).select_related("project", "member", "workspace")
serializer = ProjectMemberSerializer(project_members, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
except Exception as e:
@ -1209,3 +1211,38 @@ class LeaveProjectEndpoint(BaseAPIView):
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class ProjectPublicCoverImagesEndpoint(BaseAPIView):
permission_classes = [
AllowAny,
]
def get(self, request):
try:
files = []
s3 = boto3.client(
"s3",
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
)
params = {
"Bucket": settings.AWS_S3_BUCKET_NAME,
"Prefix": "static/project-cover/",
}
response = s3.list_objects_v2(**params)
# Extracting file keys from the response
if "Contents" in response:
for content in response["Contents"]:
if not content["Key"].endswith(
"/"
): # This line ensures we're only getting files, not "sub-folders"
files.append(
f"https://{settings.AWS_S3_BUCKET_NAME}.s3.{settings.AWS_REGION}.amazonaws.com/{content['Key']}"
)
return Response(files, status=status.HTTP_200_OK)
except Exception as e:
capture_exception(e)
return Response([], status=status.HTTP_200_OK)

View file

@ -1,21 +0,0 @@
# Third party imports
from rest_framework.response import Response
from rest_framework import status
from sentry_sdk import capture_exception
# Module imports
from .base import BaseAPIView
from plane.utils.integrations.github import get_release_notes
class ReleaseNotesEndpoint(BaseAPIView):
def get(self, request):
try:
release_notes = get_release_notes()
return Response(release_notes, status=status.HTTP_200_OK)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)

View file

@ -61,7 +61,7 @@ class GlobalViewViewSet(BaseViewSet):
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.select_related("workspace")
.order_by("-created_at")
.order_by(self.request.GET.get("order_by", "-created_at"))
.distinct()
)

View file

@ -1197,7 +1197,7 @@ class WorkspaceUserActivityEndpoint(BaseAPIView):
projects = request.query_params.getlist("project", [])
queryset = IssueActivity.objects.filter(
~Q(field__in=["comment", "vote", "reaction"]),
~Q(field__in=["comment", "vote", "reaction", "draft"]),
workspace__slug=slug,
project__project_projectmember__member=request.user,
actor=user_id,