This commit is contained in:
pablonyx 2025-02-24 09:44:18 -08:00 committed by GitHub
parent f0c13b6558
commit 1263e21eb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 55 deletions

View File

@ -1,5 +1,3 @@
from typing import Any
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Depends from fastapi import Depends
from fastapi import HTTPException from fastapi import HTTPException
@ -345,6 +343,9 @@ def list_bot_configs(
] ]
MAX_CHANNELS = 200
@router.get( @router.get(
"/admin/slack-app/bots/{bot_id}/channels", "/admin/slack-app/bots/{bot_id}/channels",
) )
@ -353,38 +354,40 @@ def get_all_channels_from_slack_api(
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
_: User | None = Depends(current_admin_user), _: User | None = Depends(current_admin_user),
) -> list[SlackChannel]: ) -> 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) tokens = fetch_slack_bot_tokens(db_session, bot_id)
if not tokens or "bot_token" not in tokens: if not tokens or "bot_token" not in tokens:
raise HTTPException( raise HTTPException(
status_code=404, detail="Bot token not found for the given bot ID" status_code=404, detail="Bot token not found for the given bot ID"
) )
bot_token = tokens["bot_token"] client = WebClient(token=tokens["bot_token"])
client = WebClient(token=bot_token)
try: try:
channels = [] response = client.conversations_list(
cursor = None types="public_channel,private_channel",
while True: exclude_archived=True,
response = client.conversations_list( limit=MAX_CHANNELS,
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_metadata: dict[str, Any] = response.get("response_metadata", {}) channels = [
if isinstance(response_metadata, dict): SlackChannel(id=channel["id"], name=channel["name"])
cursor = response_metadata.get("next_cursor") for channel in response["channels"]
if not cursor: ]
break
else: if len(channels) == MAX_CHANNELS:
break raise HTTPException(
status_code=400,
detail=f"Workspace has {MAX_CHANNELS} or more channels.",
)
return channels return channels
except SlackApiError as e: except SlackApiError as e:
raise HTTPException( 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)}",
) )

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useMemo, useState, useEffect } from "react"; import React, { useMemo } from "react";
import { Formik, Form, Field } from "formik"; import { Formik, Form } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { usePopup } from "@/components/admin/connectors/Popup"; import { usePopup } from "@/components/admin/connectors/Popup";
import { import {
@ -13,17 +13,13 @@ import {
createSlackChannelConfig, createSlackChannelConfig,
isPersonaASlackBotPersona, isPersonaASlackBotPersona,
updateSlackChannelConfig, updateSlackChannelConfig,
fetchSlackChannels,
} from "../lib"; } from "../lib";
import CardSection from "@/components/admin/CardSection"; import CardSection from "@/components/admin/CardSection";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Persona } from "@/app/admin/assistants/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces";
import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE";
import { SEARCH_TOOL_ID, SEARCH_TOOL_NAME } from "@/app/chat/tools/constants"; import { SEARCH_TOOL_ID } from "@/app/chat/tools/constants";
import { import { SlackChannelConfigFormFields } from "./SlackChannelConfigFormFields";
SlackChannelConfigFormFields,
SlackChannelConfigFormFieldsProps,
} from "./SlackChannelConfigFormFields";
export const SlackChannelConfigCreationForm = ({ export const SlackChannelConfigCreationForm = ({
slack_bot_id, slack_bot_id,

View File

@ -1,13 +1,7 @@
"use client"; "use client";
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect, useMemo } from "react";
import { import { FieldArray, useFormikContext, ErrorMessage, Field } from "formik";
FieldArray,
Form,
useFormikContext,
ErrorMessage,
Field,
} from "formik";
import { CCPairDescriptor, DocumentSet } from "@/lib/types"; import { CCPairDescriptor, DocumentSet } from "@/lib/types";
import { import {
Label, Label,
@ -18,14 +12,13 @@ import {
} from "@/components/admin/connectors/Field"; } from "@/components/admin/connectors/Field";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Persona } from "@/app/admin/assistants/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces";
import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle";
import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable"; import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable";
import CollapsibleSection from "@/app/admin/assistants/CollapsibleSection"; import CollapsibleSection from "@/app/admin/assistants/CollapsibleSection";
import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE";
import { StandardAnswerCategoryDropdownField } from "@/components/standardAnswers/StandardAnswerCategoryDropdown"; import { StandardAnswerCategoryDropdownField } from "@/components/standardAnswers/StandardAnswerCategoryDropdown";
import { RadioGroup } from "@/components/ui/radio-group"; import { RadioGroup } from "@/components/ui/radio-group";
import { RadioGroupItemField } from "@/components/ui/RadioGroupItemField"; import { RadioGroupItemField } from "@/components/ui/RadioGroupItemField";
import { AlertCircle, View } from "lucide-react"; import { AlertCircle } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { import {
Tooltip, Tooltip,
@ -50,6 +43,7 @@ import {
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { CheckFormField } from "@/components/ui/CheckField"; import { CheckFormField } from "@/components/ui/CheckField";
import { Input } from "@/components/ui/input";
export interface SlackChannelConfigFormFieldsProps { export interface SlackChannelConfigFormFieldsProps {
isUpdate: boolean; isUpdate: boolean;
@ -178,9 +172,13 @@ export function SlackChannelConfigFormFields({
); );
}, [documentSets]); }, [documentSets]);
const { data: channelOptions, isLoading } = useSWR( const {
data: channelOptions,
error,
isLoading,
} = useSWR(
`/api/manage/admin/slack-app/bots/${slack_bot_id}/channels`, `/api/manage/admin/slack-app/bots/${slack_bot_id}/channels`,
async (url: string) => { async () => {
const channels = await fetchSlackChannels(slack_bot_id); const channels = await fetchSlackChannels(slack_bot_id);
return channels.map((channel: any) => ({ return channels.map((channel: any) => ({
name: channel.name, name: channel.name,
@ -227,20 +225,34 @@ export function SlackChannelConfigFormFields({
> >
Select A Slack Channel: Select A Slack Channel:
</label>{" "} </label>{" "}
<Field name="channel_name"> {error ? (
{({ field, form }: { field: any; form: any }) => ( <div>
<SearchMultiSelectDropdown <div className="text-red-600 text-sm mb-4">
options={channelOptions || []} {error.message || "Unable to fetch Slack channels."}
onSelect={(selected) => { {" Please enter the channel name manually."}
form.setFieldValue("channel_name", selected.name); </div>
}} <TextFormField
initialSearchTerm={field.value} name="channel_name"
onSearchTermChange={(term) => { label="Channel Name"
form.setFieldValue("channel_name", term); placeholder="Enter channel name"
}}
/> />
)} </div>
</Field> ) : (
<Field name="channel_name">
{({ field, form }: { field: any; form: any }) => (
<SearchMultiSelectDropdown
options={channelOptions || []}
onSelect={(selected) => {
form.setFieldValue("channel_name", selected.name);
}}
initialSearchTerm={field.value}
onSearchTermChange={(term) => {
form.setFieldValue("channel_name", term);
}}
/>
)}
</Field>
)}
</> </>
)} )}
<div className="space-y-2 mt-4"> <div className="space-y-2 mt-4">