From aef7af0a9a5937d0ec175ce8bc37e50d43798781 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 4 Feb 2025 18:35:34 +0000 Subject: [PATCH] feat: refactor Slack utility imports and add new Slack utility class --- Common/Server/API/SlackAPI.ts | 9 ++ Common/Server/Services/ProjectService.ts | 2 +- Common/Server/Services/UserService.ts | 2 +- .../Components/Slack/SendMessageToChannel.ts | 2 +- Common/Server/Utils/{ => Slack}/Slack.ts | 0 .../Server/Utils/Slack}/app-manifest.json | 0 .../Pages/UserSettings/SlackIntegration.tsx | 120 ++++++++++++++---- 7 files changed, 107 insertions(+), 28 deletions(-) rename Common/Server/Utils/{ => Slack}/Slack.ts (100%) rename {Slack => Common/Server/Utils/Slack}/app-manifest.json (100%) diff --git a/Common/Server/API/SlackAPI.ts b/Common/Server/API/SlackAPI.ts index fb7f9a7c162..12c5192a776 100644 --- a/Common/Server/API/SlackAPI.ts +++ b/Common/Server/API/SlackAPI.ts @@ -48,14 +48,23 @@ export default class SlackAPI { }); router.post("/slack/interactive", SlackAuthorization.isAuthorizedSlackRequest, (req: ExpressRequest, res: ExpressResponse) => { + return Response.sendJsonObjectResponse(req, res, { + "response_action": "clear" + }); }); // options load endpoint. router.post("/slack/options-load", SlackAuthorization.isAuthorizedSlackRequest, (req: ExpressRequest, res: ExpressResponse) => { + return Response.sendJsonObjectResponse(req, res, { + "response_action": "clear" + }); }); router.post("/slack/command", SlackAuthorization.isAuthorizedSlackRequest, (req: ExpressRequest, res: ExpressResponse) => { + return Response.sendJsonObjectResponse(req, res, { + "response_action": "clear" + }); }); diff --git a/Common/Server/Services/ProjectService.ts b/Common/Server/Services/ProjectService.ts index 7e79cc74b40..2908e7ad27b 100755 --- a/Common/Server/Services/ProjectService.ts +++ b/Common/Server/Services/ProjectService.ts @@ -67,7 +67,7 @@ import AlertSeverity from "../../Models/DatabaseModels/AlertSeverity"; import AlertSeverityService from "./AlertSeverityService"; import AlertState from "../../Models/DatabaseModels/AlertState"; import AlertStateService from "./AlertStateService"; -import SlackUtil from "../Utils/Slack"; +import SlackUtil from "../Utils/Slack/Slack"; import URL from "../../Types/API/URL"; import Exception from "../../Types/Exception/Exception"; diff --git a/Common/Server/Services/UserService.ts b/Common/Server/Services/UserService.ts index 00ed043a755..c20eab01ace 100755 --- a/Common/Server/Services/UserService.ts +++ b/Common/Server/Services/UserService.ts @@ -28,7 +28,7 @@ import Text from "../../Types/Text"; import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken"; import TeamMember from "Common/Models/DatabaseModels/TeamMember"; import Model from "Common/Models/DatabaseModels/User"; -import SlackUtil from "../Utils/Slack"; +import SlackUtil from "../Utils/Slack/Slack"; import UserTwoFactorAuth from "Common/Models/DatabaseModels/UserTwoFactorAuth"; import UserTwoFactorAuthService from "./UserTwoFactorAuthService"; import BadDataException from "../../Types/Exception/BadDataException"; diff --git a/Common/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts b/Common/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts index 8dc96685c47..7e8cb1b5a1f 100644 --- a/Common/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +++ b/Common/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts @@ -8,7 +8,7 @@ import { JSONObject } from "Common/Types/JSON"; import ComponentMetadata, { Port } from "Common/Types/Workflow/Component"; import ComponentID from "Common/Types/Workflow/ComponentID"; import SlackComponents from "Common/Types/Workflow/Components/Slack"; -import SlackUtil from "../../../../Utils/Slack"; +import SlackUtil from "../../../../Utils/Slack/Slack"; export default class SendMessageToChannel extends ComponentCode { public constructor() { diff --git a/Common/Server/Utils/Slack.ts b/Common/Server/Utils/Slack/Slack.ts similarity index 100% rename from Common/Server/Utils/Slack.ts rename to Common/Server/Utils/Slack/Slack.ts diff --git a/Slack/app-manifest.json b/Common/Server/Utils/Slack/app-manifest.json similarity index 100% rename from Slack/app-manifest.json rename to Common/Server/Utils/Slack/app-manifest.json diff --git a/Dashboard/src/Pages/UserSettings/SlackIntegration.tsx b/Dashboard/src/Pages/UserSettings/SlackIntegration.tsx index bdba8eb4847..0a9fb5679b4 100644 --- a/Dashboard/src/Pages/UserSettings/SlackIntegration.tsx +++ b/Dashboard/src/Pages/UserSettings/SlackIntegration.tsx @@ -4,6 +4,7 @@ import React, { Fragment, FunctionComponent, ReactElement, + useEffect, } from "react"; import Card from "Common/UI/Components/Card/Card"; import { ButtonStyleType } from "Common/UI/Components/Button/Button"; @@ -14,45 +15,114 @@ import { APP_API_URL, HOME_URL, SlackAppClientId } from "Common/UI/Config"; import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage"; import Link from "Common/UI/Components/Link/Link"; import Route from "Common/Types/API/Route"; +import ObjectID from "Common/Types/ObjectID"; +import ProjectUtil from "Common/UI/Utils/Project"; +import UserUtil from "Common/UI/Utils/User"; +import { JSONObject } from "Common/Types/JSON"; +import API from "Common/Utils/API"; +import Exception from "Common/Types/Exception/Exception"; +import PageLoader from "Common/UI/Components/Loader/PageLoader"; const Settings: FunctionComponent = (): ReactElement => { const [error, setError] = React.useState(null); + const [isLoading, setIsLoading] = React.useState(true); - if(error){ - return + const [manifest, setManifest] = React.useState(null); + + const fetchAppManifest = async () => { + try { + setError(null); + setIsLoading(true); + const response = await API.get(URL.fromString("/api/slack/app-manifest")); + setManifest(response.data); + } catch (error) { + setError( +
+ {API.getFriendlyErrorMessage(error as Error)} +
+ ); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + fetchAppManifest().catch((error: Exception) => { + setError( +
+ {API.getFriendlyErrorMessage(error)} +
+ ); + }); + }, []); + + + if(isLoading){ + return ; } + if (error) { + return + } return (
- { - if(SlackAppClientId){ - - const redirect_uri: string = `${APP_API_URL}/slack/auth` - - Navigation.navigate(URL.fromString(`https://slack.com/oauth/v2/authorize?scope=incoming-webhook&client_id=${SlackAppClientId}&redirect_uri=${redirect_uri}`)) - }else{ - setError( -
- Looks like the Slack App Client ID is not set in the environment variables when you installed OneUptime. For more information, please check this guide to set up Slack App properly: Slack Integration -
); - } + { + if (SlackAppClientId) { + + const projectId: ObjectID | null = ProjectUtil.getCurrentProjectId(); + const userId: ObjectID | null = UserUtil.getUserId(); + + if (!projectId) { + setError( +
+ Looks like you have not selected any project. Please select a project to continue. +
); + return; + } + + if (!userId) { + setError( +
+ Looks like you are not logged in. Please login to continue. +
); + return; + } + + + const scopes: Array =[]; + + if(manifest && manifest["oauth_config"] && ((manifest["oauth_config"] as JSONObject)["scopes"] as JSONObject) && ((manifest["oauth_config"] as JSONObject)["scopes"] as JSONObject)["user"] && (((manifest["oauth_config"] as JSONObject)["scopes"] as JSONObject)["user"] as Array).length > 0){ + scopes.push(...(((manifest["oauth_config"] as JSONObject)["scopes"] as JSONObject)["user"] as Array)); + } + + const redirect_uri: string = `${APP_API_URL}/slack/auth?projectId=${projectId.toString()}&userId=${userId.toString()}`; + + Navigation.navigate(URL.fromString(`https://slack.com/oauth/v2/authorize?scope=${ + scopes.join(",") + }&client_id=${SlackAppClientId}&redirect_uri=${redirect_uri}`)) + } else { + setError( +
+ Looks like the Slack App Client ID is not set in the environment variables when you installed OneUptime. For more information, please check this guide to set up Slack App properly: Slack Integration +
); + } + }, + icon: IconProp.Slack, }, - icon: IconProp.Slack, - }, - ]} - /> + ]} + />
);