mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: implement lazy imports for token refresh in Confluence and Jira connectors
- Refactored token refresh logic in ConfluenceHistoryConnector and JiraHistoryConnector to use lazy imports, avoiding circular dependencies. - Enhanced the ComposerAction component to manage tool availability based on connected types, adding support for Jira and Confluence tools. - Updated tool icon management to include Jira and Confluence, improving the user interface for tool interactions.
This commit is contained in:
parent
e71eae26fc
commit
79bc123439
5 changed files with 77 additions and 6 deletions
|
|
@ -14,7 +14,6 @@ from sqlalchemy.future import select
|
|||
from app.config import config
|
||||
from app.connectors.confluence_connector import ConfluenceConnector
|
||||
from app.db import SearchSourceConnector
|
||||
from app.routes.confluence_add_connector_route import refresh_confluence_token
|
||||
from app.schemas.atlassian_auth_credentials import AtlassianAuthCredentialsBase
|
||||
from app.utils.oauth_security import TokenEncryption
|
||||
|
||||
|
|
@ -190,7 +189,9 @@ class ConfluenceHistoryConnector:
|
|||
f"Connector {self._connector_id} not found; cannot refresh token."
|
||||
)
|
||||
|
||||
# Refresh token
|
||||
# Lazy import to avoid circular dependency
|
||||
from app.routes.confluence_add_connector_route import refresh_confluence_token
|
||||
|
||||
connector = await refresh_confluence_token(self._session, connector)
|
||||
|
||||
# Reload credentials after refresh
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ from sqlalchemy.future import select
|
|||
from app.config import config
|
||||
from app.connectors.jira_connector import JiraConnector
|
||||
from app.db import SearchSourceConnector
|
||||
from app.routes.jira_add_connector_route import refresh_jira_token
|
||||
from app.schemas.atlassian_auth_credentials import AtlassianAuthCredentialsBase
|
||||
from app.utils.oauth_security import TokenEncryption
|
||||
|
||||
|
|
@ -184,7 +183,9 @@ class JiraHistoryConnector:
|
|||
f"Connector {self._connector_id} not found; cannot refresh token."
|
||||
)
|
||||
|
||||
# Refresh token
|
||||
# Lazy import to avoid circular dependency
|
||||
from app.routes.jira_add_connector_route import refresh_jira_token
|
||||
|
||||
connector = await refresh_jira_token(self._session, connector)
|
||||
|
||||
# Reload credentials after refresh
|
||||
|
|
|
|||
|
|
@ -880,6 +880,12 @@ async def _stream_agent_events(
|
|||
"create_calendar_event",
|
||||
"update_calendar_event",
|
||||
"delete_calendar_event",
|
||||
"create_jira_issue",
|
||||
"update_jira_issue",
|
||||
"delete_jira_issue",
|
||||
"create_confluence_page",
|
||||
"update_confluence_page",
|
||||
"delete_confluence_page",
|
||||
):
|
||||
yield streaming_service.format_tool_output_available(
|
||||
tool_call_id,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
|||
import { Switch } from "@/components/ui/switch";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||
import { CONNECTOR_TOOL_ICON_PATHS, getToolIcon } from "@/contracts/enums/toolIcons";
|
||||
import { CONNECTOR_ICON_TO_TYPES, CONNECTOR_TOOL_ICON_PATHS, getToolIcon } from "@/contracts/enums/toolIcons";
|
||||
import type { Document } from "@/contracts/types/document.types";
|
||||
import { useBatchCommentsPreload } from "@/hooks/use-comments";
|
||||
import { useCommentsElectric } from "@/hooks/use-comments-electric";
|
||||
|
|
@ -603,6 +603,12 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
|||
const setDisabledTools = useSetAtom(disabledToolsAtom);
|
||||
const hydrateDisabled = useSetAtom(hydrateDisabledToolsAtom);
|
||||
|
||||
const { data: connectors } = useAtomValue(connectorsAtom);
|
||||
const connectedTypes = useMemo(
|
||||
() => new Set<string>((connectors ?? []).map((c) => c.connector_type)),
|
||||
[connectors]
|
||||
);
|
||||
|
||||
const toggleToolGroup = useCallback(
|
||||
(toolNames: string[]) => {
|
||||
const allDisabled = toolNames.every((name) => disabledTools.includes(name));
|
||||
|
|
@ -628,6 +634,15 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
|||
const placed = new Set<string>();
|
||||
|
||||
for (const group of TOOL_GROUPS) {
|
||||
if (group.connectorIcon) {
|
||||
const requiredTypes = CONNECTOR_ICON_TO_TYPES[group.connectorIcon];
|
||||
const isConnected = requiredTypes?.some((t) => connectedTypes.has(t));
|
||||
if (!isConnected) {
|
||||
for (const name of group.tools) placed.add(name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const matched = group.tools.flatMap((name) => {
|
||||
const tool = toolsByName.get(name);
|
||||
if (!tool) return [];
|
||||
|
|
@ -645,7 +660,7 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
|||
}
|
||||
|
||||
return result;
|
||||
}, [filteredTools]);
|
||||
}, [filteredTools, connectedTypes]);
|
||||
|
||||
const { visibleTotal, visibleEnabled } = useMemo(() => {
|
||||
let total = 0;
|
||||
|
|
@ -669,6 +684,30 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
|||
hydrateDisabled();
|
||||
}, [hydrateDisabled]);
|
||||
|
||||
useEffect(() => {
|
||||
const unavailable: string[] = [];
|
||||
for (const group of TOOL_GROUPS) {
|
||||
if (!group.connectorIcon) continue;
|
||||
const requiredTypes = CONNECTOR_ICON_TO_TYPES[group.connectorIcon];
|
||||
const isConnected = requiredTypes?.some((t) => connectedTypes.has(t));
|
||||
if (!isConnected) {
|
||||
unavailable.push(...group.tools);
|
||||
}
|
||||
}
|
||||
if (unavailable.length === 0) return;
|
||||
setDisabledTools((prev) => {
|
||||
const next = new Set(prev);
|
||||
let changed = false;
|
||||
for (const name of unavailable) {
|
||||
if (!next.has(name)) {
|
||||
next.add(name);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed ? [...next] : prev;
|
||||
});
|
||||
}, [connectedTypes, setDisabledTools]);
|
||||
|
||||
const hasModelConfigured = useMemo(() => {
|
||||
if (!preferences) return false;
|
||||
const agentLlmId = preferences.agent_llm_id;
|
||||
|
|
@ -1092,6 +1131,18 @@ const TOOL_GROUPS: ToolGroup[] = [
|
|||
connectorIcon: "linear",
|
||||
tooltip: "Create, update, and delete issues in Linear.",
|
||||
},
|
||||
{
|
||||
label: "Jira",
|
||||
tools: ["create_jira_issue", "update_jira_issue", "delete_jira_issue"],
|
||||
connectorIcon: "jira",
|
||||
tooltip: "Create, update, and delete issues in Jira.",
|
||||
},
|
||||
{
|
||||
label: "Confluence",
|
||||
tools: ["create_confluence_page", "update_confluence_page", "delete_confluence_page"],
|
||||
connectorIcon: "confluence",
|
||||
tooltip: "Create, update, and delete pages in Confluence.",
|
||||
},
|
||||
];
|
||||
|
||||
const MessageError: FC = () => {
|
||||
|
|
|
|||
|
|
@ -37,4 +37,16 @@ export const CONNECTOR_TOOL_ICON_PATHS: Record<string, { src: string; alt: strin
|
|||
google_drive: { src: "/connectors/google-drive.svg", alt: "Google Drive" },
|
||||
notion: { src: "/connectors/notion.svg", alt: "Notion" },
|
||||
linear: { src: "/connectors/linear.svg", alt: "Linear" },
|
||||
jira: { src: "/connectors/jira.svg", alt: "Jira" },
|
||||
confluence: { src: "/connectors/confluence.svg", alt: "Confluence" },
|
||||
};
|
||||
|
||||
export const CONNECTOR_ICON_TO_TYPES: Record<string, string[]> = {
|
||||
gmail: ["GOOGLE_GMAIL_CONNECTOR", "COMPOSIO_GMAIL_CONNECTOR"],
|
||||
google_calendar: ["GOOGLE_CALENDAR_CONNECTOR", "COMPOSIO_GOOGLE_CALENDAR_CONNECTOR"],
|
||||
google_drive: ["GOOGLE_DRIVE_CONNECTOR", "COMPOSIO_GOOGLE_DRIVE_CONNECTOR"],
|
||||
notion: ["NOTION_CONNECTOR"],
|
||||
linear: ["LINEAR_CONNECTOR"],
|
||||
jira: ["JIRA_CONNECTOR"],
|
||||
confluence: ["CONFLUENCE_CONNECTOR"],
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue