From 1263e21eb5e973462e4e2de9b12ef75b84f88cf3 Mon Sep 17 00:00:00 2001 From: pablonyx Date: Mon, 24 Feb 2025 09:44:18 -0800 Subject: [PATCH] k (#4102) --- backend/onyx/server/manage/slack_bot.py | 49 ++++++++------- .../SlackChannelConfigCreationForm.tsx | 12 ++-- .../channels/SlackChannelConfigFormFields.tsx | 60 +++++++++++-------- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/backend/onyx/server/manage/slack_bot.py b/backend/onyx/server/manage/slack_bot.py index ae06bc813..53e0d0bbe 100644 --- a/backend/onyx/server/manage/slack_bot.py +++ b/backend/onyx/server/manage/slack_bot.py @@ -1,5 +1,3 @@ -from typing import Any - from fastapi import APIRouter from fastapi import Depends from fastapi import HTTPException @@ -345,6 +343,9 @@ def list_bot_configs( ] +MAX_CHANNELS = 200 + + @router.get( "/admin/slack-app/bots/{bot_id}/channels", ) @@ -353,38 +354,40 @@ def get_all_channels_from_slack_api( db_session: Session = Depends(get_session), _: User | None = Depends(current_admin_user), ) -> list[SlackChannel]: + """ + Fetches all channels from the Slack API. + If the workspace has 200 or more channels, we raise an error. + """ tokens = fetch_slack_bot_tokens(db_session, bot_id) if not tokens or "bot_token" not in tokens: raise HTTPException( status_code=404, detail="Bot token not found for the given bot ID" ) - bot_token = tokens["bot_token"] - client = WebClient(token=bot_token) + client = WebClient(token=tokens["bot_token"]) try: - channels = [] - cursor = None - while True: - response = client.conversations_list( - types="public_channel,private_channel", - exclude_archived=True, - limit=1000, - cursor=cursor, - ) - for channel in response["channels"]: - channels.append(SlackChannel(id=channel["id"], name=channel["name"])) + response = client.conversations_list( + types="public_channel,private_channel", + exclude_archived=True, + limit=MAX_CHANNELS, + ) - response_metadata: dict[str, Any] = response.get("response_metadata", {}) - if isinstance(response_metadata, dict): - cursor = response_metadata.get("next_cursor") - if not cursor: - break - else: - break + channels = [ + SlackChannel(id=channel["id"], name=channel["name"]) + for channel in response["channels"] + ] + + if len(channels) == MAX_CHANNELS: + raise HTTPException( + status_code=400, + detail=f"Workspace has {MAX_CHANNELS} or more channels.", + ) return channels + except SlackApiError as e: raise HTTPException( - status_code=500, detail=f"Error fetching channels from Slack API: {str(e)}" + status_code=500, + detail=f"Error fetching channels from Slack API: {str(e)}", ) diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx index f4bfff0d5..503771d98 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx @@ -1,7 +1,7 @@ "use client"; -import React, { useMemo, useState, useEffect } from "react"; -import { Formik, Form, Field } from "formik"; +import React, { useMemo } from "react"; +import { Formik, Form } from "formik"; import * as Yup from "yup"; import { usePopup } from "@/components/admin/connectors/Popup"; import { @@ -13,17 +13,13 @@ import { createSlackChannelConfig, isPersonaASlackBotPersona, updateSlackChannelConfig, - fetchSlackChannels, } from "../lib"; import CardSection from "@/components/admin/CardSection"; import { useRouter } from "next/navigation"; import { Persona } from "@/app/admin/assistants/interfaces"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; -import { SEARCH_TOOL_ID, SEARCH_TOOL_NAME } from "@/app/chat/tools/constants"; -import { - SlackChannelConfigFormFields, - SlackChannelConfigFormFieldsProps, -} from "./SlackChannelConfigFormFields"; +import { SEARCH_TOOL_ID } from "@/app/chat/tools/constants"; +import { SlackChannelConfigFormFields } from "./SlackChannelConfigFormFields"; export const SlackChannelConfigCreationForm = ({ slack_bot_id, diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx index 3372756e9..13a80f6bb 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx @@ -1,13 +1,7 @@ "use client"; import React, { useState, useEffect, useMemo } from "react"; -import { - FieldArray, - Form, - useFormikContext, - ErrorMessage, - Field, -} from "formik"; +import { FieldArray, useFormikContext, ErrorMessage, Field } from "formik"; import { CCPairDescriptor, DocumentSet } from "@/lib/types"; import { Label, @@ -18,14 +12,13 @@ import { } from "@/components/admin/connectors/Field"; import { Button } from "@/components/ui/button"; import { Persona } from "@/app/admin/assistants/interfaces"; -import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle"; import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable"; import CollapsibleSection from "@/app/admin/assistants/CollapsibleSection"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; import { StandardAnswerCategoryDropdownField } from "@/components/standardAnswers/StandardAnswerCategoryDropdown"; import { RadioGroup } from "@/components/ui/radio-group"; import { RadioGroupItemField } from "@/components/ui/RadioGroupItemField"; -import { AlertCircle, View } from "lucide-react"; +import { AlertCircle } from "lucide-react"; import { useRouter } from "next/navigation"; import { Tooltip, @@ -50,6 +43,7 @@ import { import { Separator } from "@/components/ui/separator"; import { CheckFormField } from "@/components/ui/CheckField"; +import { Input } from "@/components/ui/input"; export interface SlackChannelConfigFormFieldsProps { isUpdate: boolean; @@ -178,9 +172,13 @@ export function SlackChannelConfigFormFields({ ); }, [documentSets]); - const { data: channelOptions, isLoading } = useSWR( + const { + data: channelOptions, + error, + isLoading, + } = useSWR( `/api/manage/admin/slack-app/bots/${slack_bot_id}/channels`, - async (url: string) => { + async () => { const channels = await fetchSlackChannels(slack_bot_id); return channels.map((channel: any) => ({ name: channel.name, @@ -227,20 +225,34 @@ export function SlackChannelConfigFormFields({ > Select A Slack Channel: {" "} - - {({ field, form }: { field: any; form: any }) => ( - { - form.setFieldValue("channel_name", selected.name); - }} - initialSearchTerm={field.value} - onSearchTermChange={(term) => { - form.setFieldValue("channel_name", term); - }} + {error ? ( +
+
+ {error.message || "Unable to fetch Slack channels."} + {" Please enter the channel name manually."} +
+ - )} - +
+ ) : ( + + {({ field, form }: { field: any; form: any }) => ( + { + form.setFieldValue("channel_name", selected.name); + }} + initialSearchTerm={field.value} + onSearchTermChange={(term) => { + form.setFieldValue("channel_name", term); + }} + /> + )} + + )} )}