Add connector deletion failure message (#2392)

This commit is contained in:
pablodanswer 2024-09-11 22:38:15 -07:00 committed by GitHub
parent f69922fff7
commit 58bdf9d684
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 110 additions and 8 deletions

View File

@ -0,0 +1,27 @@
"""add ccpair deletion failure message
Revision ID: 0ebb1d516877
Revises: 52a219fb5233
Create Date: 2024-09-10 15:03:48.233926
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "0ebb1d516877"
down_revision = "52a219fb5233"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"connector_credential_pair",
sa.Column("deletion_failure_message", sa.String(), nullable=True),
)
def downgrade() -> None:
op.drop_column("connector_credential_pair", "deletion_failure_message")

View File

@ -1,4 +1,5 @@
import json
import traceback
from datetime import timedelta
from typing import Any
from typing import cast
@ -40,6 +41,7 @@ from danswer.configs.constants import POSTGRES_CELERY_WORKER_APP_NAME
from danswer.configs.constants import PostgresAdvisoryLocks
from danswer.connectors.factory import instantiate_connector
from danswer.connectors.models import InputType
from danswer.db.connector_credential_pair import add_deletion_failure_message
from danswer.db.connector_credential_pair import (
get_connector_credential_pair,
)
@ -97,6 +99,7 @@ def cleanup_connector_credential_pair_task(
Needs to potentially update a large number of Postgres and Vespa docs, including deleting them
or updating the ACL"""
engine = get_sqlalchemy_engine()
with Session(engine) as db_session:
# validate that the connector / credential pair is deletable
cc_pair = get_connector_credential_pair(
@ -109,14 +112,13 @@ def cleanup_connector_credential_pair_task(
f"Cannot run deletion attempt - connector_credential_pair with Connector ID: "
f"{connector_id} and Credential ID: {credential_id} does not exist."
)
deletion_attempt_disallowed_reason = check_deletion_attempt_is_allowed(
connector_credential_pair=cc_pair, db_session=db_session
)
if deletion_attempt_disallowed_reason:
raise ValueError(deletion_attempt_disallowed_reason)
try:
deletion_attempt_disallowed_reason = check_deletion_attempt_is_allowed(
connector_credential_pair=cc_pair, db_session=db_session
)
if deletion_attempt_disallowed_reason:
raise ValueError(deletion_attempt_disallowed_reason)
# The bulk of the work is in here, updates Postgres and Vespa
curr_ind_name, sec_ind_name = get_both_index_names(db_session)
document_index = get_default_document_index(
@ -127,10 +129,15 @@ def cleanup_connector_credential_pair_task(
document_index=document_index,
cc_pair=cc_pair,
)
except Exception as e:
stack_trace = traceback.format_exc()
error_message = f"Error: {str(e)}\n\nStack Trace:\n{stack_trace}"
add_deletion_failure_message(db_session, cc_pair.id, error_message)
task_logger.exception(
f"Failed to run connector_deletion. "
f"connector_id={connector_id} credential_id={credential_id}"
f"connector_id={connector_id} credential_id={credential_id}\n"
f"Stack Trace:\n{stack_trace}"
)
raise e
@ -410,6 +417,7 @@ def check_for_cc_pair_deletion_task() -> None:
task_logger.info(
f"Deleting the {cc_pair.name} connector credential pair"
)
cleanup_connector_credential_pair_task.apply_async(
kwargs=dict(
connector_id=cc_pair.connector.id,

View File

@ -98,6 +98,18 @@ def get_connector_credential_pairs(
return list(results.all())
def add_deletion_failure_message(
db_session: Session,
cc_pair_id: int,
failure_message: str,
) -> None:
cc_pair = get_connector_credential_pair_from_id(cc_pair_id, db_session)
if not cc_pair:
return
cc_pair.deletion_failure_message = failure_message
db_session.commit()
def get_cc_pair_groups_for_ids(
db_session: Session,
cc_pair_ids: list[int],

View File

@ -375,6 +375,9 @@ class ConnectorCredentialPair(Base):
connector_id: Mapped[int] = mapped_column(
ForeignKey("connector.id"), primary_key=True
)
deletion_failure_message: Mapped[str | None] = mapped_column(String, nullable=True)
credential_id: Mapped[int] = mapped_column(
ForeignKey("credential.id"), primary_key=True
)

View File

@ -220,6 +220,7 @@ class CCPairFullInfo(BaseModel):
latest_deletion_attempt: DeletionAttemptSnapshot | None
is_public: bool
is_editable_for_current_user: bool
deletion_failure_message: str | None
@classmethod
def from_models(
@ -262,6 +263,7 @@ class CCPairFullInfo(BaseModel):
latest_deletion_attempt=latest_deletion_attempt,
is_public=cc_pair_model.is_public,
is_editable_for_current_user=is_editable_for_current_user,
deletion_failure_message=cc_pair_model.deletion_failure_message,
)

View File

@ -0,0 +1,25 @@
import { FiInfo } from "react-icons/fi";
export default function DeletionErrorStatus({
deletion_failure_message,
}: {
deletion_failure_message: string;
}) {
return (
<div className="mt-2 rounded-md border border-error-300 bg-error-50 p-4 text-error-600 max-w-3xl">
<div className="flex items-center">
<h3 className="text-base font-medium">Deletion Error</h3>
<div className="ml-2 relative group">
<FiInfo className="h-4 w-4 text-error-600 cursor-help" />
<div className="absolute z-10 w-64 p-2 mt-2 text-sm bg-white rounded-md shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 border border-gray-200">
This error occurred while attempting to delete the connector. You
may re-attempt a deletion by clicking the &quot;Delete&quot; button.
</div>
</div>
</div>
<div className="mt-2 text-sm">
<p>{deletion_failure_message}</p>
</div>
</div>
);
}

View File

@ -22,6 +22,7 @@ import { useEffect, useRef, useState } from "react";
import { CheckmarkIcon, EditIcon, XIcon } from "@/components/icons/icons";
import { usePopup } from "@/components/admin/connectors/Popup";
import { updateConnectorCredentialPairName } from "@/lib/connector";
import DeletionErrorStatus from "./DeletionErrorStatus";
// since the uploaded files are cleaned up after some period of time
// re-indexing will not work for the file connector. Also, it would not
@ -184,6 +185,17 @@ function Main({ ccPairId }: { ccPairId: number }) {
: "This connector belongs to groups where you don't have curator permissions, so it's not editable."}
</div>
)}
{ccPair.deletion_failure_message &&
ccPair.status === ConnectorCredentialPairStatus.DELETING && (
<>
<div className="mt-6" />
<DeletionErrorStatus
deletion_failure_message={ccPair.deletion_failure_message}
/>
</>
)}
{credentialTemplates[ccPair.connector.source] &&
ccPair.is_editable_for_current_user && (
<>

View File

@ -24,6 +24,7 @@ export interface CCPairFullInfo {
latest_deletion_attempt: DeletionAttemptSnapshot | null;
is_public: boolean;
is_editable_for_current_user: boolean;
deletion_failure_message: string | null;
}
export interface PaginatedIndexAttempts {

View File

@ -88,6 +88,18 @@ module.exports = {
input: "#ffffff",
// Error colors
"error-50": "#fef2f2",
"error-100": "#fee2e2",
"error-200": "#fecaca",
"error-300": "#fca5a5",
"error-400": "#f87171",
"error-500": "#ef4444",
"error-600": "#dc2626",
"error-700": "#b91c1c",
"error-800": "#991b1b",
"error-900": "#7f1d1d",
background: "#fafafa", // 50
"background-100": "#f5f5f5", // neutral-100
"background-125": "#F1F2F4", // gray-125