Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: api webhooks #2543

Merged
merged 63 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
ab1bc71
dev: initiate external apis
pablohashescobar Oct 18, 2023
c3bd91b
dev: external api
pablohashescobar Oct 18, 2023
5f3ec70
dev: external public api implementation
pablohashescobar Oct 18, 2023
cbb7423
dev: add prefix to all api tokens
pablohashescobar Oct 19, 2023
de4bfc2
dev: flag to enable disable api token api access
pablohashescobar Oct 19, 2023
3dc5896
dev: webhook model create and apis
pablohashescobar Oct 19, 2023
7984274
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
pablohashescobar Oct 19, 2023
72b1819
dev: webhook settings
pablohashescobar Oct 19, 2023
9959ca5
fix: webhook logs
NarayanBavisetti Oct 23, 2023
e9321af
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Oct 23, 2023
2696a3f
chore: removed drf spectacular
NarayanBavisetti Oct 23, 2023
055520a
dev: remove retry_count and fix api logging for get requests
pablohashescobar Oct 23, 2023
24864a7
dev: refactor webhook logic
pablohashescobar Oct 23, 2023
80243b0
fix: celery retry mechanism
NarayanBavisetti Oct 25, 2023
273a2af
chore: event and action change
NarayanBavisetti Oct 25, 2023
c06507f
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Oct 25, 2023
ee5ec1a
chore: migrations changes
NarayanBavisetti Oct 25, 2023
f8c4c5a
dev: proxy setup for apis
pablohashescobar Oct 25, 2023
698eb25
Merge branch 'feat/api_webhooks' of github.com:makeplane/plane into f…
pablohashescobar Oct 25, 2023
7bc9dc4
chore: changed retry time and cleanup
NarayanBavisetti Oct 25, 2023
ea9a611
chore: added issue comment and inbox issue api endpoints
NarayanBavisetti Oct 26, 2023
674f3be
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Oct 27, 2023
52bfc65
fix: migration files
NarayanBavisetti Oct 27, 2023
8cd19b0
fix: added env variables
NarayanBavisetti Oct 30, 2023
3b7c9e7
fix: removed issue attachment from proxy
NarayanBavisetti Oct 30, 2023
2258836
fix: added new migration file
NarayanBavisetti Oct 30, 2023
c3c1600
fix: restricted wehbook access
NarayanBavisetti Oct 30, 2023
62e7d4e
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Oct 30, 2023
39b08b0
chore: changed urls
NarayanBavisetti Oct 30, 2023
fce68e0
chore: fixed porject serializer
NarayanBavisetti Oct 30, 2023
844988c
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Oct 30, 2023
f617593
fix: set expire for api token
NarayanBavisetti Oct 31, 2023
dba494a
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
pablohashescobar Oct 31, 2023
0d53347
Merge branch 'feat/api_webhooks' of github.com:makeplane/plane into f…
pablohashescobar Oct 31, 2023
09aea27
fix: retrive endpoint for api token
NarayanBavisetti Oct 31, 2023
2704d7a
Merge branch 'feat/api_webhooks' of github.com:makeplane/plane into f…
NarayanBavisetti Oct 31, 2023
83f7330
feat: Api Token screens & api integration
1akhanBaheti Oct 31, 2023
2ea0136
dev: webhook endpoint changes
pablohashescobar Oct 31, 2023
beb9fd0
dev: add fields for webhook updates
pablohashescobar Oct 31, 2023
5fe4013
feat: Download Api secret key
1akhanBaheti Oct 31, 2023
7c628b4
Merge branch 'feat/api_webhooks' of https://github.com/makeplane/plan…
1akhanBaheti Nov 1, 2023
5af95a2
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
NarayanBavisetti Nov 1, 2023
31ba8c2
chore: removed BASE API URL
NarayanBavisetti Nov 1, 2023
9b22ae2
Merge branch 'develop' of https://github.com/makeplane/plane into fea…
1akhanBaheti Nov 1, 2023
6a283d2
feat: revoke token access
1akhanBaheti Nov 1, 2023
7bc5a8d
chore: added string for issue
NarayanBavisetti Nov 3, 2023
a7f6872
Merge branch 'feat/api_webhooks' of github.com:makeplane/plane into f…
NarayanBavisetti Nov 3, 2023
cddeece
dev: migration fixes
NarayanBavisetti Nov 3, 2023
be14d0f
feat: workspace webhooks (#2748)
rahulramesha Nov 10, 2023
d41ff42
Merge branch 'develop' into feat/api_webhooks
pablohashescobar Nov 10, 2023
7219c3a
fix: url validation added
sriramveeraghanta Nov 10, 2023
fbac18c
fix: seperated env for webhook and api
NarayanBavisetti Nov 13, 2023
fb5e4d4
Web hooks refactoring
rahulramesha Nov 13, 2023
0edb256
pull from develop and resolve merge conflicts
rahulramesha Nov 13, 2023
d415cc5
Merge branch 'feat/api_webhooks' of github.com:makeplane/plane into f…
rahulramesha Nov 13, 2023
9d3b9eb
add show option for generated hook key
rahulramesha Nov 14, 2023
fe9b622
Api token restructure
rahulramesha Nov 14, 2023
c6a1419
webhook minor fixes
rahulramesha Nov 14, 2023
d6d7d7e
fix build errors
rahulramesha Nov 14, 2023
02b0a33
chore: improvements in file structring
sriramveeraghanta Nov 14, 2023
9b94285
dev: rate limiting the open apis
pablohashescobar Nov 15, 2023
8a8bef9
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
pablohashescobar Nov 15, 2023
14f3cb7
Merge branch 'develop' of github.com:makeplane/plane into feat/api_we…
pablohashescobar Nov 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ USE_MINIO=1
# Nginx Configuration
NGINX_PORT=80

# Set it to 0, to disable it
ENABLE_WEBHOOK=1

# Set it to 0, to disable it
ENABLE_API=1
9 changes: 8 additions & 1 deletion apiserver/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,12 @@ ENABLE_MAGIC_LINK_LOGIN="0"
# Email redirections and minio domain settings
WEB_URL="http://localhost"

# Set it to 0, to disable it
ENABLE_WEBHOOK=1

# Set it to 0, to disable it
ENABLE_API=1

# Gunicorn Workers
GUNICORN_WORKERS=2
GUNICORN_WORKERS=2

19 changes: 17 additions & 2 deletions apiserver/plane/api/permissions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
from .workspace import WorkSpaceBasePermission, WorkSpaceAdminPermission, WorkspaceEntityPermission, WorkspaceViewerPermission, WorkspaceUserPermission
from .project import ProjectBasePermission, ProjectEntityPermission, ProjectMemberPermission, ProjectLitePermission

from .workspace import (
WorkSpaceBasePermission,
WorkspaceOwnerPermission,
WorkSpaceAdminPermission,
WorkspaceEntityPermission,
WorkspaceViewerPermission,
WorkspaceUserPermission,
)
from .project import (
ProjectBasePermission,
ProjectEntityPermission,
ProjectMemberPermission,
ProjectLitePermission,
)


18 changes: 16 additions & 2 deletions apiserver/plane/api/permissions/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ def has_permission(self, request, view):
).exists()


class WorkspaceOwnerPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_anonymous:
return False

return WorkspaceMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role=Owner,
).exists()


class WorkSpaceAdminPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_anonymous:
Expand Down Expand Up @@ -93,10 +105,12 @@ def has_permission(self, request, view):


class WorkspaceUserPermission(BasePermission):

def has_permission(self, request, view):
if request.user.is_anonymous:
return False

return WorkspaceMember.objects.filter(
member=request.user,
workspace__slug=view.workspace_slug,
is_active=True,
)
).exists()
4 changes: 3 additions & 1 deletion apiserver/plane/api/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
ModuleFavoriteSerializer,
)

from .api_token import APITokenSerializer
from .api import APITokenSerializer, APITokenReadSerializer

from .integration import (
IntegrationSerializer,
Expand Down Expand Up @@ -100,3 +100,5 @@
from .notification import NotificationSerializer

from .exporter import ExporterHistorySerializer

from .webhook import WebhookSerializer, WebhookLogSerializer
31 changes: 31 additions & 0 deletions apiserver/plane/api/serializers/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from .base import BaseSerializer
from plane.db.models import APIToken, APIActivityLog


class APITokenSerializer(BaseSerializer):

class Meta:
model = APIToken
fields = "__all__"
read_only_fields = [
"token",
"expired_at",
"created_at",
"updated_at",
"workspace",
"user",
]


class APITokenReadSerializer(BaseSerializer):

class Meta:
model = APIToken
exclude = ('token',)


class APIActivityLogSerializer(BaseSerializer):

class Meta:
model = APIActivityLog
fields = "__all__"
14 changes: 0 additions & 14 deletions apiserver/plane/api/serializers/api_token.py

This file was deleted.

2 changes: 1 addition & 1 deletion apiserver/plane/api/serializers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def get_members(self, obj):
"member__display_name",
"member__avatar",
)
return project_members
return list(project_members)

class Meta:
model = Project
Expand Down
30 changes: 30 additions & 0 deletions apiserver/plane/api/serializers/webhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Third party imports
from rest_framework import serializers

# Module imports
from .base import DynamicBaseSerializer
from plane.db.models import Webhook, WebhookLog
from plane.db.models.webhook import validate_domain, validate_schema

class WebhookSerializer(DynamicBaseSerializer):
url = serializers.URLField(validators=[validate_schema, validate_domain])

class Meta:
model = Webhook
fields = "__all__"
read_only_fields = [
"workspace",
"secret_key",
]


class WebhookLogSerializer(DynamicBaseSerializer):

class Meta:
model = WebhookLog
fields = "__all__"
read_only_fields = [
"workspace",
"webhook"
]

12 changes: 12 additions & 0 deletions apiserver/plane/api/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
from .user import urlpatterns as user_urls
from .views import urlpatterns as view_urls
from .workspace import urlpatterns as workspace_urls
from .api import urlpatterns as api_urls
from .webhook import urlpatterns as webhook_urls


# Django imports
from django.conf import settings


urlpatterns = [
Expand All @@ -44,3 +50,9 @@
*view_urls,
*workspace_urls,
]

if settings.ENABLE_WEBHOOK:
urlpatterns += webhook_urls

if settings.ENABLE_API:
urlpatterns += api_urls
17 changes: 17 additions & 0 deletions apiserver/plane/api/urls/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.urls import path
from plane.api.views import ApiTokenEndpoint

urlpatterns = [
# API Tokens
path(
"workspaces/<str:slug>/api-tokens/",
ApiTokenEndpoint.as_view(),
name="api-tokens",
),
path(
"workspaces/<str:slug>/api-tokens/<uuid:pk>/",
ApiTokenEndpoint.as_view(),
name="api-tokens",
),
## End API Tokens
]
31 changes: 31 additions & 0 deletions apiserver/plane/api/urls/webhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.urls import path

from plane.api.views import (
WebhookEndpoint,
WebhookLogsEndpoint,
WebhookSecretRegenerateEndpoint,
)


urlpatterns = [
path(
"workspaces/<str:slug>/webhooks/",
WebhookEndpoint.as_view(),
name="webhooks",
),
path(
"workspaces/<str:slug>/webhooks/<uuid:pk>/",
WebhookEndpoint.as_view(),
name="webhooks",
),
path(
"workspaces/<str:slug>/webhooks/<uuid:pk>/regenerate/",
WebhookSecretRegenerateEndpoint.as_view(),
name="webhooks",
),
path(
"workspaces/<str:slug>/webhook-logs/<uuid:webhook_id>/",
WebhookLogsEndpoint.as_view(),
name="webhooks",
),
]
6 changes: 4 additions & 2 deletions apiserver/plane/api/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from .oauth import OauthEndpoint

from .base import BaseAPIView, BaseViewSet
from .base import BaseAPIView, BaseViewSet, WebhookMixin

from .workspace import (
WorkSpaceViewSet,
Expand Down Expand Up @@ -115,7 +115,7 @@
ModuleFavoriteViewSet,
)

from .api_token import ApiTokenEndpoint
from .api import ApiTokenEndpoint

from .integration import (
WorkspaceIntegrationViewSet,
Expand Down Expand Up @@ -172,3 +172,5 @@
from .exporter import ExportIssuesEndpoint

from .config import ConfigurationEndpoint

from .webhook import WebhookEndpoint, WebhookLogsEndpoint, WebhookSecretRegenerateEndpoint
78 changes: 78 additions & 0 deletions apiserver/plane/api/views/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Python import
from uuid import uuid4

# Third party
from rest_framework.response import Response
from rest_framework import status

# Module import
from .base import BaseAPIView
from plane.db.models import APIToken, Workspace
from plane.api.serializers import APITokenSerializer, APITokenReadSerializer
from plane.api.permissions import WorkspaceOwnerPermission


class ApiTokenEndpoint(BaseAPIView):
permission_classes = [
WorkspaceOwnerPermission,
]

def post(self, request, slug):
label = request.data.get("label", str(uuid4().hex))
description = request.data.get("description", "")
workspace = Workspace.objects.get(slug=slug)
expired_at = request.data.get("expired_at", None)

# Check the user type
user_type = 1 if request.user.is_bot else 0

api_token = APIToken.objects.create(
label=label,
description=description,
user=request.user,
workspace=workspace,
user_type=user_type,
expired_at=expired_at,
)

serializer = APITokenSerializer(api_token)
# Token will be only visible while creating
return Response(
serializer.data,
status=status.HTTP_201_CREATED,
)

def get(self, request, slug, pk=None):
if pk == None:
api_tokens = APIToken.objects.filter(
user=request.user, workspace__slug=slug
)
serializer = APITokenReadSerializer(api_tokens, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
else:
api_tokens = APIToken.objects.get(
user=request.user, workspace__slug=slug, pk=pk
)
serializer = APITokenReadSerializer(api_tokens)
return Response(serializer.data, status=status.HTTP_200_OK)

def delete(self, request, slug, pk):
api_token = APIToken.objects.get(
workspace__slug=slug,
user=request.user,
pk=pk,
)
api_token.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

def patch(self, request, slug, pk):
api_token = APIToken.objects.get(
workspace__slug=slug,
user=request.user,
pk=pk,
)
serializer = APITokenSerializer(api_token, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Loading
Loading