chore: ran both frontend and backend linting

This commit is contained in:
Anish Sarkar 2026-01-06 15:49:31 +05:30
parent 6ea6e752f6
commit c7c5caf559
15 changed files with 177 additions and 94 deletions

View file

@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
class ConfluenceHistoryConnector:
"""
Confluence connector with OAuth support and automatic token refresh.
This connector uses OAuth 2.0 access tokens to authenticate with the
Confluence API. It automatically refreshes expired tokens when needed.
Also supports legacy API token authentication for backward compatibility.
@ -81,8 +81,10 @@ class ConfluenceHistoryConnector:
config_data = connector.config.copy()
# Check if using OAuth or legacy API token
is_oauth = config_data.get("_token_encrypted", False) or config_data.get("access_token")
is_oauth = config_data.get("_token_encrypted", False) or config_data.get(
"access_token"
)
if is_oauth:
# OAuth 2.0 authentication
# Decrypt credentials if they are encrypted
@ -93,12 +95,16 @@ class ConfluenceHistoryConnector:
# Decrypt sensitive fields
if config_data.get("access_token"):
config_data["access_token"] = token_encryption.decrypt_token(
config_data["access_token"]
config_data["access_token"] = (
token_encryption.decrypt_token(
config_data["access_token"]
)
)
if config_data.get("refresh_token"):
config_data["refresh_token"] = token_encryption.decrypt_token(
config_data["refresh_token"]
config_data["refresh_token"] = (
token_encryption.decrypt_token(
config_data["refresh_token"]
)
)
logger.info(
@ -113,13 +119,19 @@ class ConfluenceHistoryConnector:
) from e
try:
self._credentials = AtlassianAuthCredentialsBase.from_dict(config_data)
self._credentials = AtlassianAuthCredentialsBase.from_dict(
config_data
)
# Store cloud_id and base_url for API calls (with backward compatibility for site_url)
self._cloud_id = config_data.get("cloud_id")
self._base_url = config_data.get("base_url") or config_data.get("site_url")
self._base_url = config_data.get("base_url") or config_data.get(
"site_url"
)
self._use_oauth = True
except Exception as e:
raise ValueError(f"Invalid Confluence OAuth credentials: {e!s}") from e
raise ValueError(
f"Invalid Confluence OAuth credentials: {e!s}"
) from e
else:
# Legacy API token authentication
self._legacy_email = config_data.get("CONFLUENCE_EMAIL")
@ -127,11 +139,21 @@ class ConfluenceHistoryConnector:
self._base_url = config_data.get("CONFLUENCE_BASE_URL")
self._use_oauth = False
if not self._legacy_email or not self._legacy_api_token or not self._base_url:
raise ValueError("Confluence credentials not found in connector config")
if (
not self._legacy_email
or not self._legacy_api_token
or not self._base_url
):
raise ValueError(
"Confluence credentials not found in connector config"
)
# Check if token is expired and refreshable (only for OAuth)
if self._use_oauth and self._credentials.is_expired and self._credentials.is_refreshable:
if (
self._use_oauth
and self._credentials.is_expired
and self._credentials.is_refreshable
):
try:
logger.info(
f"Confluence token expired for connector {self._connector_id}, refreshing..."
@ -170,7 +192,9 @@ class ConfluenceHistoryConnector:
self._credentials = AtlassianAuthCredentialsBase.from_dict(config_data)
self._cloud_id = config_data.get("cloud_id")
# Handle backward compatibility: check both base_url and site_url
self._base_url = config_data.get("base_url") or config_data.get("site_url")
self._base_url = config_data.get("base_url") or config_data.get(
"site_url"
)
# Invalidate cached client so it's recreated with new token
if self._http_client:
@ -230,10 +254,10 @@ class ConfluenceHistoryConnector:
if not self._use_oauth:
# For legacy auth, use the base_url directly
return self._base_url or ""
if not self._cloud_id:
raise ValueError("Cloud ID not available. Cannot construct API URL.")
# Use the Atlassian API format: https://api.atlassian.com/ex/confluence/{cloudid}
return f"https://api.atlassian.com/ex/confluence/{self._cloud_id}"
@ -261,11 +285,12 @@ class ConfluenceHistoryConnector:
# For now, we'll use the legacy client's make_api_request method
# But since it's sync, we'll need to wrap it
import asyncio
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None, client.make_api_request, endpoint, params
)
# OAuth flow
token = await self._get_valid_token()
base_url = await self._get_base_url()
@ -446,7 +471,9 @@ class ConfluenceHistoryConnector:
if cursor:
params["cursor"] = cursor
result = await self._make_api_request(f"pages/{page_id}/{comment_type}", params)
result = await self._make_api_request(
f"pages/{page_id}/{comment_type}", params
)
if not isinstance(result, dict) or "results" not in result:
break # No comments or invalid response
@ -495,6 +522,7 @@ class ConfluenceHistoryConnector:
await self._get_valid_token()
# ConfluenceConnector.get_pages_by_date_range is synchronous
import asyncio
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
@ -504,7 +532,7 @@ class ConfluenceHistoryConnector:
space_ids,
include_comments,
)
# OAuth flow
all_pages = []
@ -562,4 +590,3 @@ class ConfluenceHistoryConnector:
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Async context manager exit."""
await self.close()

View file

@ -283,11 +283,11 @@ class JiraConnector:
# Query issues that were either created OR updated within the date range
# Use end_date + 1 day with < operator to include the full end date
from datetime import datetime, timedelta
# Parse end_date and add 1 day for inclusive end date
end_date_obj = datetime.strptime(end_date, "%Y-%m-%d")
end_date_next = (end_date_obj + timedelta(days=1)).strftime("%Y-%m-%d")
# Check both created and updated dates to catch all relevant issues
# Use 'created' and 'updated' (standard JQL field names)
date_filter = (
@ -297,9 +297,7 @@ class JiraConnector:
jql = f"{date_filter} ORDER BY created DESC"
if project_key:
jql = (
f'project = "{project_key}" AND ({date_filter}) ORDER BY created DESC'
)
jql = f'project = "{project_key}" AND ({date_filter}) ORDER BY created DESC'
# Define fields to retrieve
fields = [

View file

@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
class JiraHistoryConnector:
"""
Jira connector with OAuth support and automatic token refresh.
This connector uses OAuth 2.0 access tokens to authenticate with the
Jira API. It automatically refreshes expired tokens when needed.
Also supports legacy API token authentication for backward compatibility.
@ -80,8 +80,10 @@ class JiraHistoryConnector:
config_data = connector.config.copy()
# Check if using OAuth or legacy API token
is_oauth = config_data.get("_token_encrypted", False) or config_data.get("access_token")
is_oauth = config_data.get("_token_encrypted", False) or config_data.get(
"access_token"
)
if is_oauth:
# OAuth 2.0 authentication
if not config.SECRET_KEY:
@ -118,7 +120,9 @@ class JiraHistoryConnector:
) from e
try:
self._credentials = AtlassianAuthCredentialsBase.from_dict(config_data)
self._credentials = AtlassianAuthCredentialsBase.from_dict(
config_data
)
self._cloud_id = config_data.get("cloud_id")
self._base_url = config_data.get("base_url")
self._use_oauth = True
@ -131,11 +135,19 @@ class JiraHistoryConnector:
self._base_url = config_data.get("JIRA_BASE_URL")
self._use_oauth = False
if not self._legacy_email or not self._legacy_api_token or not self._base_url:
if (
not self._legacy_email
or not self._legacy_api_token
or not self._base_url
):
raise ValueError("Jira credentials not found in connector config")
# Check if token is expired and refreshable (only for OAuth)
if self._use_oauth and self._credentials.is_expired and self._credentials.is_refreshable:
if (
self._use_oauth
and self._credentials.is_expired
and self._credentials.is_refreshable
):
try:
logger.info(
f"Jira token expired for connector {self._connector_id}, refreshing..."
@ -206,7 +218,7 @@ class JiraHistoryConnector:
if self._use_oauth:
# Ensure we have valid token (will refresh if needed)
await self._get_valid_token()
self._jira_client = JiraConnector(
base_url=self._base_url,
access_token=self._credentials.access_token,
@ -230,7 +242,7 @@ class JiraHistoryConnector:
access_token=self._credentials.access_token,
cloud_id=self._cloud_id,
)
return self._jira_client
async def get_issues_by_date_range(
@ -256,10 +268,10 @@ class JiraHistoryConnector:
# Ensure token is valid (will refresh if needed)
if self._use_oauth:
await self._get_valid_token()
# Get client with valid credentials
client = await self._get_jira_client()
# JiraConnector methods are synchronous, so we call them directly
# Token refresh has already been handled above
return client.get_issues_by_date_range(
@ -317,4 +329,3 @@ class JiraHistoryConnector:
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Async context manager exit."""
await self.close()

View file

@ -4,6 +4,7 @@ from .airtable_add_connector_route import (
router as airtable_add_connector_router,
)
from .circleback_webhook_route import router as circleback_webhook_router
from .confluence_add_connector_route import router as confluence_add_connector_router
from .discord_add_connector_route import router as discord_add_connector_router
from .documents_routes import router as documents_router
from .editor_routes import router as editor_router
@ -17,7 +18,6 @@ from .google_gmail_add_connector_route import (
router as google_gmail_add_connector_router,
)
from .jira_add_connector_route import router as jira_add_connector_router
from .confluence_add_connector_route import router as confluence_add_connector_router
from .linear_add_connector_route import router as linear_add_connector_router
from .logs_routes import router as logs_router
from .luma_add_connector_route import router as luma_add_connector_router

View file

@ -87,7 +87,9 @@ async def connect_confluence(space_id: int, user: User = Depends(current_active_
raise HTTPException(status_code=400, detail="space_id is required")
if not config.ATLASSIAN_CLIENT_ID:
raise HTTPException(status_code=500, detail="Atlassian OAuth not configured.")
raise HTTPException(
status_code=500, detail="Atlassian OAuth not configured."
)
if not config.SECRET_KEY:
raise HTTPException(
@ -113,7 +115,9 @@ async def connect_confluence(space_id: int, user: User = Depends(current_active_
auth_url = f"{AUTHORIZATION_URL}?{urlencode(auth_params)}"
logger.info(f"Generated Confluence OAuth URL for user {user.id}, space {space_id}")
logger.info(
f"Generated Confluence OAuth URL for user {user.id}, space {space_id}"
)
return {"auth_url": auth_url}
except Exception as e:
@ -216,7 +220,9 @@ async def confluence_callback(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
raise HTTPException(
@ -252,7 +258,9 @@ async def confluence_callback(
break
if not cloud_id:
logger.warning("Could not determine Confluence cloud ID from accessible resources")
logger.warning(
"Could not determine Confluence cloud ID from accessible resources"
)
# Calculate expiration time (UTC, tz-aware)
expires_at = None
@ -409,7 +417,9 @@ async def refresh_confluence_token(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
raise HTTPException(
@ -431,7 +441,8 @@ async def refresh_confluence_token(
if not access_token:
raise HTTPException(
status_code=400, detail="No access token received from Confluence refresh"
status_code=400,
detail="No access token received from Confluence refresh",
)
# Update credentials object with encrypted tokens
@ -449,7 +460,9 @@ async def refresh_confluence_token(
credentials.cloud_id = connector.config.get("cloud_id")
if not credentials.base_url:
# Check both base_url and site_url for backward compatibility
credentials.base_url = connector.config.get("base_url") or connector.config.get("site_url")
credentials.base_url = connector.config.get(
"base_url"
) or connector.config.get("site_url")
# Update connector config with encrypted tokens
credentials_dict = credentials.to_dict()
@ -458,7 +471,9 @@ async def refresh_confluence_token(
await session.commit()
await session.refresh(connector)
logger.info(f"Successfully refreshed Confluence token for connector {connector.id}")
logger.info(
f"Successfully refreshed Confluence token for connector {connector.id}"
)
return connector
except HTTPException:
@ -468,4 +483,3 @@ async def refresh_confluence_token(
raise HTTPException(
status_code=500, detail=f"Failed to refresh Confluence token: {e!s}"
) from e

View file

@ -217,7 +217,9 @@ async def discord_callback(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
raise HTTPException(
@ -263,7 +265,9 @@ async def discord_callback(
# Store the bot token from config and OAuth metadata
connector_config = {
"bot_token": token_encryption.encrypt_token(bot_token), # Use bot token from config
"bot_token": token_encryption.encrypt_token(
bot_token
), # Use bot token from config
"oauth_access_token": token_encryption.encrypt_token(oauth_access_token)
if oauth_access_token
else None, # Store OAuth token for reference
@ -356,7 +360,7 @@ async def refresh_discord_token(
) -> SearchSourceConnector:
"""
Refresh the Discord OAuth tokens for a connector.
Note: Bot tokens from config don't expire, but OAuth access tokens might.
This function refreshes OAuth tokens if needed, but always uses bot token from config.
@ -400,7 +404,9 @@ async def refresh_discord_token(
f"No refresh token available for connector {connector.id}. Using bot token from config."
)
# Update bot token from config (in case it was changed)
credentials.bot_token = token_encryption.encrypt_token(config.DISCORD_BOT_TOKEN)
credentials.bot_token = token_encryption.encrypt_token(
config.DISCORD_BOT_TOKEN
)
credentials_dict = credentials.to_dict()
credentials_dict["_token_encrypted"] = True
connector.config = credentials_dict
@ -428,7 +434,9 @@ async def refresh_discord_token(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
# If refresh fails, bot token from config is still valid
@ -437,7 +445,9 @@ async def refresh_discord_token(
"Using bot token from config."
)
# Update bot token from config
credentials.bot_token = token_encryption.encrypt_token(config.DISCORD_BOT_TOKEN)
credentials.bot_token = token_encryption.encrypt_token(
config.DISCORD_BOT_TOKEN
)
credentials.refresh_token = None # Clear invalid refresh token
credentials_dict = credentials.to_dict()
credentials_dict["_token_encrypted"] = True
@ -463,7 +473,7 @@ async def refresh_discord_token(
# Always use bot token from config (bot tokens don't expire)
credentials.bot_token = token_encryption.encrypt_token(config.DISCORD_BOT_TOKEN)
# Update OAuth tokens if available
if oauth_access_token:
# Store OAuth access token for reference
@ -493,7 +503,9 @@ async def refresh_discord_token(
await session.commit()
await session.refresh(connector)
logger.info(f"Successfully refreshed Discord OAuth tokens for connector {connector.id}")
logger.info(
f"Successfully refreshed Discord OAuth tokens for connector {connector.id}"
)
return connector
except HTTPException:
@ -506,4 +518,3 @@ async def refresh_discord_token(
raise HTTPException(
status_code=500, detail=f"Failed to refresh Discord tokens: {e!s}"
) from e

View file

@ -86,7 +86,9 @@ async def connect_jira(space_id: int, user: User = Depends(current_active_user))
raise HTTPException(status_code=400, detail="space_id is required")
if not config.ATLASSIAN_CLIENT_ID:
raise HTTPException(status_code=500, detail="Atlassian OAuth not configured.")
raise HTTPException(
status_code=500, detail="Atlassian OAuth not configured."
)
if not config.SECRET_KEY:
raise HTTPException(
@ -215,7 +217,9 @@ async def jira_callback(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
raise HTTPException(
@ -254,9 +258,7 @@ async def jira_callback(
# Filter for Jira instances (resources with type "jira" or id field)
jira_instances = [
r
for r in resources
if r.get("id") and (r.get("name") or r.get("url"))
r for r in resources if r.get("id") and (r.get("name") or r.get("url"))
]
if not jira_instances:
@ -270,7 +272,7 @@ async def jira_callback(
jira_instance = jira_instances[0]
cloud_id = jira_instance["id"]
base_url = jira_instance.get("url")
# If URL is not provided, construct it from cloud_id
if not base_url:
# Try to extract from name or construct default format
@ -433,7 +435,9 @@ async def refresh_jira_token(
error_detail = token_response.text
try:
error_json = token_response.json()
error_detail = error_json.get("error_description", error_json.get("error", error_detail))
error_detail = error_json.get(
"error_description", error_json.get("error", error_detail)
)
except Exception:
pass
raise HTTPException(

View file

@ -13,7 +13,7 @@ from pydantic import BaseModel, field_validator
class AtlassianAuthCredentialsBase(BaseModel):
"""
Base model for Atlassian OAuth 2.0 credentials.
Used for both Jira and Confluence connectors since they share
the same Atlassian OAuth infrastructure and token structure.
"""
@ -84,4 +84,3 @@ class AtlassianAuthCredentialsBase(BaseModel):
if isinstance(v, datetime):
return v if v.tzinfo else v.replace(tzinfo=UTC)
return v

View file

@ -73,4 +73,3 @@ class DiscordAuthCredentialsBase(BaseModel):
if isinstance(v, datetime):
return v if v.tzinfo else v.replace(tzinfo=UTC)
return v

View file

@ -90,9 +90,11 @@ async def index_confluence_pages(
{"stage": "client_initialization"},
)
confluence_client: ConfluenceHistoryConnector | None = ConfluenceHistoryConnector(
session=session,
connector_id=connector_id,
confluence_client: ConfluenceHistoryConnector | None = (
ConfluenceHistoryConnector(
session=session,
connector_id=connector_id,
)
)
# Calculate date range
@ -421,11 +423,11 @@ async def index_confluence_pages(
logger.info(
f"Confluence indexing completed: {documents_indexed} new pages, {documents_skipped} skipped"
)
# Close the client connection
if confluence_client:
await confluence_client.close()
return (
total_processed,
None,

View file

@ -69,7 +69,9 @@ async def index_discord_messages(
try:
# Normalize date parameters - handle 'undefined' strings from frontend
if start_date and (start_date.lower() == "undefined" or start_date.strip() == ""):
if start_date and (
start_date.lower() == "undefined" or start_date.strip() == ""
):
start_date = None
if end_date and (end_date.lower() == "undefined" or end_date.strip() == ""):
end_date = None
@ -118,12 +120,13 @@ async def index_discord_messages(
elif has_legacy:
# Backward compatibility: use legacy token format
discord_token = connector.config.get("DISCORD_BOT_TOKEN")
# Decrypt token if it's encrypted (legacy tokens might be encrypted)
token_encrypted = connector.config.get("_token_encrypted", False)
if token_encrypted and config.SECRET_KEY and discord_token:
try:
from app.utils.oauth_security import TokenEncryption
token_encryption = TokenEncryption(config.SECRET_KEY)
discord_token = token_encryption.decrypt_token(discord_token)
logger.info(
@ -135,7 +138,7 @@ async def index_discord_messages(
"Trying to use token as-is (might be unencrypted)."
)
# Continue with token as-is - might be unencrypted legacy token
discord_client = DiscordConnector(token=discord_token)
else:
await task_logger.log_task_failure(
@ -210,11 +213,16 @@ async def index_discord_messages(
f"Date parsing error: {e!s}",
{"error_type": "InvalidDateFormat", "start_date": start_date},
)
return 0, f"Invalid start_date format: {start_date}. Expected YYYY-MM-DD format."
return (
0,
f"Invalid start_date format: {start_date}. Expected YYYY-MM-DD format.",
)
try:
end_date_iso = (
datetime.strptime(end_date, "%Y-%m-%d").replace(tzinfo=UTC).isoformat()
datetime.strptime(end_date, "%Y-%m-%d")
.replace(tzinfo=UTC)
.isoformat()
)
except ValueError as e:
await task_logger.log_task_failure(
@ -223,7 +231,10 @@ async def index_discord_messages(
f"Date parsing error: {e!s}",
{"error_type": "InvalidDateFormat", "end_date": end_date},
)
return 0, f"Invalid end_date format: {end_date}. Expected YYYY-MM-DD format."
return (
0,
f"Invalid end_date format: {end_date}. Expected YYYY-MM-DD format.",
)
logger.info(
f"Indexing Discord messages from {start_date_iso} to {end_date_iso}"
@ -384,8 +395,10 @@ async def index_discord_messages(
)
# Check if document with this unique identifier already exists
existing_document = await check_document_by_unique_identifier(
session, unique_identifier_hash
existing_document = (
await check_document_by_unique_identifier(
session, unique_identifier_hash
)
)
if existing_document:
@ -406,8 +419,10 @@ async def index_discord_messages(
chunks = await create_document_chunks(
combined_document_string
)
doc_embedding = config.embedding_model_instance.embed(
combined_document_string
doc_embedding = (
config.embedding_model_instance.embed(
combined_document_string
)
)
# Update existing document
@ -429,7 +444,9 @@ async def index_discord_messages(
# Delete old chunks and add new ones
existing_document.chunks = chunks
existing_document.updated_at = get_current_timestamp()
existing_document.updated_at = (
get_current_timestamp()
)
documents_indexed += 1
logger.info(
@ -439,7 +456,9 @@ async def index_discord_messages(
# Document doesn't exist - create new one
# Process chunks
chunks = await create_document_chunks(combined_document_string)
chunks = await create_document_chunks(
combined_document_string
)
doc_embedding = config.embedding_model_instance.embed(
combined_document_string
)

View file

@ -95,9 +95,7 @@ async def index_jira_issues(
# Create connector with session and connector_id for internal refresh
# Token refresh will happen automatically when needed
jira_client = JiraHistoryConnector(
session=session, connector_id=connector_id
)
jira_client = JiraHistoryConnector(session=session, connector_id=connector_id)
# Calculate date range
# Handle "undefined" strings from frontend
@ -395,10 +393,10 @@ async def index_jira_issues(
logger.info(
f"JIRA indexing completed: {documents_indexed} new issues, {documents_skipped} skipped"
)
# Clean up the connector
await jira_client.close()
return (
total_processed,
None,
@ -435,4 +433,4 @@ async def index_jira_issues(
await jira_client.close()
except Exception:
pass
return 0, f"Failed to index JIRA issues: {e!s}"
return 0, f"Failed to index JIRA issues: {e!s}"

View file

@ -80,7 +80,8 @@ export const ConfluenceConfig: FC<ConfluenceConfigProps> = ({
// For OAuth connectors, show simple info message
if (isOAuth) {
const siteUrl = (connector.config?.base_url as string) || (connector.config?.site_url as string) || "Unknown";
const siteUrl =
(connector.config?.base_url as string) || (connector.config?.site_url as string) || "Unknown";
return (
<div className="space-y-6">
{/* OAuth Info */}

View file

@ -18,9 +18,9 @@ export const DiscordConfig: FC<DiscordConfigProps> = () => {
<div className="text-xs sm:text-sm">
<p className="font-medium text-xs sm:text-sm">Add Bot to Servers</p>
<p className="text-muted-foreground mt-1 text-[10px] sm:text-sm">
Before indexing, make sure the Discord bot has been added to the servers (guilds) you want to
index. The bot can only access messages from servers it's been added to. Use the OAuth
authorization flow to add the bot to your servers.
Before indexing, make sure the Discord bot has been added to the servers (guilds) you
want to index. The bot can only access messages from servers it's been added to. Use the
OAuth authorization flow to add the bot to your servers.
</p>
</div>
</div>

View file

@ -449,13 +449,13 @@ export function useConnectorEditPage(connectorId: number, searchSpaceId: string)
case "JIRA_CONNECTOR": {
// Check if this is an OAuth connector (has access_token or _token_encrypted flag)
const isJiraOAuth = !!(originalConfig.access_token || originalConfig._token_encrypted);
if (isJiraOAuth) {
// OAuth connectors don't allow editing credentials through the form
// Only allow name changes, which are handled separately
break;
}
// Legacy API token connector - allow editing credentials
if (
formData.JIRA_BASE_URL !== originalConfig.JIRA_BASE_URL ||