mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-20 13:05:49 +02:00
Add connector deletion failure message (#2392)
This commit is contained in:
@@ -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")
|
@@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
import traceback
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import cast
|
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.configs.constants import PostgresAdvisoryLocks
|
||||||
from danswer.connectors.factory import instantiate_connector
|
from danswer.connectors.factory import instantiate_connector
|
||||||
from danswer.connectors.models import InputType
|
from danswer.connectors.models import InputType
|
||||||
|
from danswer.db.connector_credential_pair import add_deletion_failure_message
|
||||||
from danswer.db.connector_credential_pair import (
|
from danswer.db.connector_credential_pair import (
|
||||||
get_connector_credential_pair,
|
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
|
Needs to potentially update a large number of Postgres and Vespa docs, including deleting them
|
||||||
or updating the ACL"""
|
or updating the ACL"""
|
||||||
engine = get_sqlalchemy_engine()
|
engine = get_sqlalchemy_engine()
|
||||||
|
|
||||||
with Session(engine) as db_session:
|
with Session(engine) as db_session:
|
||||||
# validate that the connector / credential pair is deletable
|
# validate that the connector / credential pair is deletable
|
||||||
cc_pair = get_connector_credential_pair(
|
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"Cannot run deletion attempt - connector_credential_pair with Connector ID: "
|
||||||
f"{connector_id} and Credential ID: {credential_id} does not exist."
|
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:
|
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
|
# The bulk of the work is in here, updates Postgres and Vespa
|
||||||
curr_ind_name, sec_ind_name = get_both_index_names(db_session)
|
curr_ind_name, sec_ind_name = get_both_index_names(db_session)
|
||||||
document_index = get_default_document_index(
|
document_index = get_default_document_index(
|
||||||
@@ -127,10 +129,15 @@ def cleanup_connector_credential_pair_task(
|
|||||||
document_index=document_index,
|
document_index=document_index,
|
||||||
cc_pair=cc_pair,
|
cc_pair=cc_pair,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
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(
|
task_logger.exception(
|
||||||
f"Failed to run connector_deletion. "
|
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
|
raise e
|
||||||
|
|
||||||
@@ -410,6 +417,7 @@ def check_for_cc_pair_deletion_task() -> None:
|
|||||||
task_logger.info(
|
task_logger.info(
|
||||||
f"Deleting the {cc_pair.name} connector credential pair"
|
f"Deleting the {cc_pair.name} connector credential pair"
|
||||||
)
|
)
|
||||||
|
|
||||||
cleanup_connector_credential_pair_task.apply_async(
|
cleanup_connector_credential_pair_task.apply_async(
|
||||||
kwargs=dict(
|
kwargs=dict(
|
||||||
connector_id=cc_pair.connector.id,
|
connector_id=cc_pair.connector.id,
|
||||||
|
@@ -98,6 +98,18 @@ def get_connector_credential_pairs(
|
|||||||
return list(results.all())
|
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(
|
def get_cc_pair_groups_for_ids(
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
cc_pair_ids: list[int],
|
cc_pair_ids: list[int],
|
||||||
|
@@ -375,6 +375,9 @@ class ConnectorCredentialPair(Base):
|
|||||||
connector_id: Mapped[int] = mapped_column(
|
connector_id: Mapped[int] = mapped_column(
|
||||||
ForeignKey("connector.id"), primary_key=True
|
ForeignKey("connector.id"), primary_key=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
deletion_failure_message: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||||
|
|
||||||
credential_id: Mapped[int] = mapped_column(
|
credential_id: Mapped[int] = mapped_column(
|
||||||
ForeignKey("credential.id"), primary_key=True
|
ForeignKey("credential.id"), primary_key=True
|
||||||
)
|
)
|
||||||
|
@@ -220,6 +220,7 @@ class CCPairFullInfo(BaseModel):
|
|||||||
latest_deletion_attempt: DeletionAttemptSnapshot | None
|
latest_deletion_attempt: DeletionAttemptSnapshot | None
|
||||||
is_public: bool
|
is_public: bool
|
||||||
is_editable_for_current_user: bool
|
is_editable_for_current_user: bool
|
||||||
|
deletion_failure_message: str | None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_models(
|
def from_models(
|
||||||
@@ -262,6 +263,7 @@ class CCPairFullInfo(BaseModel):
|
|||||||
latest_deletion_attempt=latest_deletion_attempt,
|
latest_deletion_attempt=latest_deletion_attempt,
|
||||||
is_public=cc_pair_model.is_public,
|
is_public=cc_pair_model.is_public,
|
||||||
is_editable_for_current_user=is_editable_for_current_user,
|
is_editable_for_current_user=is_editable_for_current_user,
|
||||||
|
deletion_failure_message=cc_pair_model.deletion_failure_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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 "Delete" button.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-sm">
|
||||||
|
<p>{deletion_failure_message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -22,6 +22,7 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
import { CheckmarkIcon, EditIcon, XIcon } from "@/components/icons/icons";
|
import { CheckmarkIcon, EditIcon, XIcon } from "@/components/icons/icons";
|
||||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||||
import { updateConnectorCredentialPairName } from "@/lib/connector";
|
import { updateConnectorCredentialPairName } from "@/lib/connector";
|
||||||
|
import DeletionErrorStatus from "./DeletionErrorStatus";
|
||||||
|
|
||||||
// since the uploaded files are cleaned up after some period of time
|
// 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
|
// 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."}
|
: "This connector belongs to groups where you don't have curator permissions, so it's not editable."}
|
||||||
</div>
|
</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] &&
|
{credentialTemplates[ccPair.connector.source] &&
|
||||||
ccPair.is_editable_for_current_user && (
|
ccPair.is_editable_for_current_user && (
|
||||||
<>
|
<>
|
||||||
|
@@ -24,6 +24,7 @@ export interface CCPairFullInfo {
|
|||||||
latest_deletion_attempt: DeletionAttemptSnapshot | null;
|
latest_deletion_attempt: DeletionAttemptSnapshot | null;
|
||||||
is_public: boolean;
|
is_public: boolean;
|
||||||
is_editable_for_current_user: boolean;
|
is_editable_for_current_user: boolean;
|
||||||
|
deletion_failure_message: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginatedIndexAttempts {
|
export interface PaginatedIndexAttempts {
|
||||||
|
@@ -88,6 +88,18 @@ module.exports = {
|
|||||||
|
|
||||||
input: "#ffffff",
|
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: "#fafafa", // 50
|
||||||
"background-100": "#f5f5f5", // neutral-100
|
"background-100": "#f5f5f5", // neutral-100
|
||||||
"background-125": "#F1F2F4", // gray-125
|
"background-125": "#F1F2F4", // gray-125
|
||||||
|
Reference in New Issue
Block a user