mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: slack integration workflow (#2675)
* fix: slack integration workflow * dev: add slack client id as configuration * fix: clean up * fix: added env to turbo --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
984b36f45a
commit
b372ccfdb3
@ -1,7 +1,7 @@
|
||||
from .analytic import urlpatterns as analytic_urls
|
||||
from .asset import urlpatterns as asset_urls
|
||||
from .authentication import urlpatterns as authentication_urls
|
||||
from .configuration import urlpatterns as configuration_urls
|
||||
from .config import urlpatterns as configuration_urls
|
||||
from .cycle import urlpatterns as cycle_urls
|
||||
from .estimate import urlpatterns as estimate_urls
|
||||
from .gpt import urlpatterns as gpt_urls
|
||||
|
@ -30,4 +30,5 @@ class ConfigurationEndpoint(BaseAPIView):
|
||||
data["email_password_login"] = (
|
||||
os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1"
|
||||
)
|
||||
data["slack"] = os.environ.get("SLACK_CLIENT_ID", None)
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Python improts
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
# Django imports
|
||||
from django.contrib.auth.hashers import make_password
|
||||
|
||||
@ -25,7 +25,7 @@ from plane.utils.integrations.github import (
|
||||
delete_github_installation,
|
||||
)
|
||||
from plane.api.permissions import WorkSpaceAdminPermission
|
||||
|
||||
from plane.utils.integrations.slack import slack_oauth
|
||||
|
||||
class IntegrationViewSet(BaseViewSet):
|
||||
serializer_class = IntegrationSerializer
|
||||
@ -98,12 +98,19 @@ class WorkspaceIntegrationViewSet(BaseViewSet):
|
||||
config = {"installation_id": installation_id}
|
||||
|
||||
if provider == "slack":
|
||||
metadata = request.data.get("metadata", {})
|
||||
code = request.data.get("code", False)
|
||||
|
||||
if not code:
|
||||
return Response({"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
slack_response = slack_oauth(code=code)
|
||||
|
||||
metadata = slack_response
|
||||
access_token = metadata.get("access_token", False)
|
||||
team_id = metadata.get("team", {}).get("id", False)
|
||||
if not metadata or not access_token or not team_id:
|
||||
return Response(
|
||||
{"error": "Access token and team id is required"},
|
||||
{"error": "Slack could not be installed. Please try again later"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
config = {"team_id": team_id, "access_token": access_token}
|
||||
|
@ -11,6 +11,7 @@ from plane.api.views import BaseViewSet, BaseAPIView
|
||||
from plane.db.models import SlackProjectSync, WorkspaceIntegration, ProjectMember
|
||||
from plane.api.serializers import SlackProjectSyncSerializer
|
||||
from plane.api.permissions import ProjectBasePermission, ProjectEntityPermission
|
||||
from plane.utils.integrations.slack import slack_oauth
|
||||
|
||||
|
||||
class SlackProjectSyncViewSet(BaseViewSet):
|
||||
@ -32,25 +33,46 @@ class SlackProjectSyncViewSet(BaseViewSet):
|
||||
)
|
||||
|
||||
def create(self, request, slug, project_id, workspace_integration_id):
|
||||
serializer = SlackProjectSyncSerializer(data=request.data)
|
||||
try:
|
||||
code = request.data.get("code", False)
|
||||
|
||||
if not code:
|
||||
return Response(
|
||||
{"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
slack_response = slack_oauth(code=code)
|
||||
|
||||
workspace_integration = WorkspaceIntegration.objects.get(
|
||||
workspace__slug=slug, pk=workspace_integration_id
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
serializer.save(
|
||||
project_id=project_id,
|
||||
workspace_integration_id=workspace_integration_id,
|
||||
)
|
||||
|
||||
workspace_integration = WorkspaceIntegration.objects.get(
|
||||
pk=workspace_integration_id, workspace__slug=slug
|
||||
)
|
||||
|
||||
slack_project_sync = SlackProjectSync.objects.create(
|
||||
access_token=slack_response.get("access_token"),
|
||||
scopes=slack_response.get("scope"),
|
||||
bot_user_id=slack_response.get("bot_user_id"),
|
||||
webhook_url=slack_response.get("incoming_webhook", {}).get("url"),
|
||||
data=slack_response,
|
||||
team_id=slack_response.get("team", {}).get("id"),
|
||||
team_name=slack_response.get("team", {}).get("name"),
|
||||
workspace_integration=workspace_integration,
|
||||
)
|
||||
_ = ProjectMember.objects.get_or_create(
|
||||
member=workspace_integration.actor, role=20, project_id=project_id
|
||||
)
|
||||
|
||||
serializer = SlackProjectSyncSerializer(slack_project_sync)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
except IntegrityError as e:
|
||||
if "already exists" in str(e):
|
||||
return Response(
|
||||
{"error": "Slack is already installed for the project"},
|
||||
status=status.HTTP_410_GONE,
|
||||
)
|
||||
capture_exception(e)
|
||||
return Response(
|
||||
{"error": "Slack could not be installed. Please try again later"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
20
apiserver/plane/utils/integrations/slack.py
Normal file
20
apiserver/plane/utils/integrations/slack.py
Normal file
@ -0,0 +1,20 @@
|
||||
import os
|
||||
import requests
|
||||
|
||||
def slack_oauth(code):
|
||||
SLACK_OAUTH_URL = os.environ.get("SLACK_OAUTH_URL", False)
|
||||
SLACK_CLIENT_ID = os.environ.get("SLACK_CLIENT_ID", False)
|
||||
SLACK_CLIENT_SECRET = os.environ.get("SLACK_CLIENT_SECRET", False)
|
||||
|
||||
# Oauth Slack
|
||||
if SLACK_OAUTH_URL and SLACK_CLIENT_ID and SLACK_CLIENT_SECRET:
|
||||
response = requests.get(
|
||||
SLACK_OAUTH_URL,
|
||||
params={
|
||||
"code": code,
|
||||
"client_id": SLACK_CLIENT_ID,
|
||||
"client_secret": SLACK_CLIENT_SECRET,
|
||||
},
|
||||
)
|
||||
return response.json()
|
||||
return {}
|
@ -22,7 +22,8 @@
|
||||
"SLACK_CLIENT_SECRET",
|
||||
"JITSU_TRACKER_ACCESS_KEY",
|
||||
"JITSU_TRACKER_HOST",
|
||||
"UNSPLASH_ACCESS_KEY"
|
||||
"UNSPLASH_ACCESS_KEY",
|
||||
"NEXT_PUBLIC_SLACK_CLIENT_ID"
|
||||
],
|
||||
"pipeline": {
|
||||
"build": {
|
||||
|
@ -1,23 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handleSlackAuthorize(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { code } = req.body;
|
||||
|
||||
if (!code || code === "") return res.status(400).json({ message: "Code is required" });
|
||||
|
||||
const response = await axios({
|
||||
method: "post",
|
||||
url: process.env.SLACK_OAUTH_URL || "",
|
||||
params: {
|
||||
client_id: process.env.SLACK_CLIENT_ID,
|
||||
client_secret: process.env.SLACK_CLIENT_SECRET,
|
||||
code,
|
||||
},
|
||||
});
|
||||
res.status(200).json(response?.data);
|
||||
} catch (error) {
|
||||
res.status(200).json({ message: "Internal Server Error" });
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ const appInstallationService = new AppInstallationService();
|
||||
|
||||
const AppPostInstallation: NextPageWithLayout = () => {
|
||||
const router = useRouter();
|
||||
const { installation_id, setup_action, state, provider, code } = router.query;
|
||||
const { installation_id, state, provider, code } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
if (provider === "github" && state && installation_id) {
|
||||
@ -27,21 +27,15 @@ const AppPostInstallation: NextPageWithLayout = () => {
|
||||
console.log(err);
|
||||
});
|
||||
} else if (provider === "slack" && state && code) {
|
||||
appInstallationService
|
||||
.getSlackAuthDetails(code.toString())
|
||||
.then((res) => {
|
||||
const [workspaceSlug, projectId, integrationId] = state.toString().split(",");
|
||||
|
||||
if (!projectId) {
|
||||
const payload = {
|
||||
metadata: {
|
||||
...res,
|
||||
},
|
||||
code,
|
||||
};
|
||||
|
||||
appInstallationService
|
||||
.addInstallationApp(state.toString(), provider, payload)
|
||||
.then((r) => {
|
||||
.then(() => {
|
||||
window.opener = null;
|
||||
window.open("", "_self");
|
||||
window.close();
|
||||
@ -51,17 +45,11 @@ const AppPostInstallation: NextPageWithLayout = () => {
|
||||
});
|
||||
} else {
|
||||
const payload = {
|
||||
access_token: res.access_token,
|
||||
bot_user_id: res.bot_user_id,
|
||||
webhook_url: res.incoming_webhook.url,
|
||||
data: res,
|
||||
team_id: res.team.id,
|
||||
team_name: res.team.name,
|
||||
scopes: res.scope,
|
||||
code,
|
||||
};
|
||||
appInstallationService
|
||||
.addSlackChannel(workspaceSlug, projectId, integrationId, payload)
|
||||
.then((r) => {
|
||||
.then(() => {
|
||||
window.opener = null;
|
||||
window.open("", "_self");
|
||||
window.close();
|
||||
@ -70,10 +58,6 @@ const AppPostInstallation: NextPageWithLayout = () => {
|
||||
throw err.response;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}, [state, installation_id, provider, code]);
|
||||
|
||||
|
@ -60,16 +60,4 @@ export class AppInstallationService extends APIService {
|
||||
throw error?.response;
|
||||
});
|
||||
}
|
||||
|
||||
async getSlackAuthDetails(code: string): Promise<any> {
|
||||
const response = await this.request({
|
||||
method: "post",
|
||||
url: "/api/slack-redirect",
|
||||
data: {
|
||||
code,
|
||||
},
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user