diff --git a/backend/danswer/connectors/factory.py b/backend/danswer/connectors/factory.py index bc477e69a..102e29470 100644 --- a/backend/danswer/connectors/factory.py +++ b/backend/danswer/connectors/factory.py @@ -25,8 +25,8 @@ from danswer.connectors.slab.connector import SlabConnector from danswer.connectors.slack.connector import SlackLoadConnector from danswer.connectors.slack.connector import SlackPollConnector from danswer.connectors.web.connector import WebConnector -from danswer.connectors.zulip.connector import ZulipConnector from danswer.connectors.zendesk.connector import ZendeskConnector +from danswer.connectors.zulip.connector import ZulipConnector class ConnectorMissingException(Exception): diff --git a/backend/danswer/connectors/zendesk/connector.py b/backend/danswer/connectors/zendesk/connector.py index aa967b6dd..9ee95dd01 100644 --- a/backend/danswer/connectors/zendesk/connector.py +++ b/backend/danswer/connectors/zendesk/connector.py @@ -1,27 +1,28 @@ from typing import Any -from zenpy import Zenpy -from zenpy.lib.api_objects.help_centre_objects import Article + +from zenpy import Zenpy # type: ignore +from zenpy.lib.api_objects.help_centre_objects import Article # type: ignore from danswer.configs.app_configs import INDEX_BATCH_SIZE from danswer.configs.constants import DocumentSource -from danswer.connectors.models import Document, Section -from danswer.connectors.interfaces import GenerateDocumentsOutput, LoadConnector, PollConnector, SecondsSinceUnixEpoch +from danswer.connectors.interfaces import GenerateDocumentsOutput +from danswer.connectors.interfaces import LoadConnector +from danswer.connectors.interfaces import PollConnector +from danswer.connectors.interfaces import SecondsSinceUnixEpoch +from danswer.connectors.models import Document +from danswer.connectors.models import Section + class ZendeskClientNotSetUpError(PermissionError): def __init__(self) -> None: - super().__init__( - "Zendesk Client is not set up, was load_credentials called?" - ) + super().__init__("Zendesk Client is not set up, was load_credentials called?") class ZendeskConnector(LoadConnector, PollConnector): - def __init__( - self, - batch_size: int = INDEX_BATCH_SIZE - ) -> None: + def __init__(self, batch_size: int = INDEX_BATCH_SIZE) -> None: self.batch_size = batch_size self.zendesk_client: Zenpy | None = None - + def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None: self.zendesk_client = Zenpy( subdomain=credentials["zendesk_subdomain"], @@ -29,10 +30,10 @@ class ZendeskConnector(LoadConnector, PollConnector): token=credentials["zendesk_token"], ) return None - + def load_from_state(self) -> GenerateDocumentsOutput: return self.poll_source(None, None) - + def _article_to_document(self, article: Article) -> Document: return Document( id=f"article:{article.id}", @@ -42,15 +43,22 @@ class ZendeskConnector(LoadConnector, PollConnector): metadata={ "type": "article", "updated_at": article.updated_at, - } + }, ) - - def poll_source(self, start: SecondsSinceUnixEpoch | None, end: SecondsSinceUnixEpoch | None) -> GenerateDocumentsOutput: + def poll_source( + self, start: SecondsSinceUnixEpoch | None, end: SecondsSinceUnixEpoch | None + ) -> GenerateDocumentsOutput: if self.zendesk_client is None: raise ZendeskClientNotSetUpError() - - articles = self.zendesk_client.help_center.articles(cursor_pagination=True) if start is None else self.zendesk_client.help_center.articles.incremental(start_time=int(start)) + + articles = ( + self.zendesk_client.help_center.articles(cursor_pagination=True) + if start is None + else self.zendesk_client.help_center.articles.incremental( + start_time=int(start) + ) + ) doc_batch = [] for article in articles: if article.body is None: @@ -60,4 +68,3 @@ class ZendeskConnector(LoadConnector, PollConnector): if len(doc_batch) >= self.batch_size: yield doc_batch doc_batch.clear() - diff --git a/backend/scripts/reset_indexes.py b/backend/scripts/reset_indexes.py index d606336bc..bff988c30 100644 --- a/backend/scripts/reset_indexes.py +++ b/backend/scripts/reset_indexes.py @@ -1,15 +1,16 @@ # This file is purely for development use, not included in any builds -import requests import os import sys +import requests + +# makes it so `PYTHONPATH=.` is not required when running this script parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(parent_dir) - -from danswer.configs.app_configs import DOCUMENT_INDEX_NAME -from danswer.document_index.vespa.index import DOCUMENT_ID_ENDPOINT -from danswer.utils.logger import setup_logger +from danswer.configs.app_configs import DOCUMENT_INDEX_NAME # noqa: E402 +from danswer.document_index.vespa.index import DOCUMENT_ID_ENDPOINT # noqa: E402 +from danswer.utils.logger import setup_logger # noqa: E402 logger = setup_logger() diff --git a/backend/scripts/reset_postgres.py b/backend/scripts/reset_postgres.py index e33b57b00..1eb0ca3b0 100644 --- a/backend/scripts/reset_postgres.py +++ b/backend/scripts/reset_postgres.py @@ -1,17 +1,18 @@ -import psycopg2 - import os import sys +import psycopg2 + +# makes it so `PYTHONPATH=.` is not required when running this script parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(parent_dir) -from danswer.configs.app_configs import POSTGRES_DB -from danswer.configs.app_configs import POSTGRES_HOST -from danswer.configs.app_configs import POSTGRES_PASSWORD -from danswer.configs.app_configs import POSTGRES_PORT -from danswer.configs.app_configs import POSTGRES_USER -from danswer.db.credentials import create_initial_public_credential +from danswer.configs.app_configs import POSTGRES_DB # noqa: E402 +from danswer.configs.app_configs import POSTGRES_HOST # noqa: E402 +from danswer.configs.app_configs import POSTGRES_PASSWORD # noqa: E402 +from danswer.configs.app_configs import POSTGRES_PORT # noqa: E402 +from danswer.configs.app_configs import POSTGRES_USER # noqa: E402 +from danswer.db.credentials import create_initial_public_credential # noqa: E402 def wipe_all_rows(database: str) -> None: diff --git a/web/src/app/admin/connectors/zendesk/page.tsx b/web/src/app/admin/connectors/zendesk/page.tsx index 9ebaa7bff..c8692e251 100644 --- a/web/src/app/admin/connectors/zendesk/page.tsx +++ b/web/src/app/admin/connectors/zendesk/page.tsx @@ -103,12 +103,13 @@ const Main = () => { <>

To get started you'll need API token details for your Zendesk - instance. You can generate this by access the Admin Center of your instance - (e.g. https://<subdomain>.zendesk.com/admin/). Proceed to the "Apps and - Integrations" section and "Zendesk API" page. Add a new API token and provide - it with a name. You will also need to provide the e-mail address of a user that - the system will impersonate. This is of little consequence as we are only performing - read actions. + instance. You can generate this by access the Admin Center of your + instance (e.g. https://<subdomain>.zendesk.com/admin/). + Proceed to the "Apps and Integrations" section and + "Zendesk API" page. Add a new API token and provide it + with a name. You will also need to provide the e-mail address of a + user that the system will impersonate. This is of little consequence + as we are only performing read actions.

@@ -189,29 +190,28 @@ const Main = () => { )} - {zendeskCredential && - zendeskConnectorIndexingStatuses.length === 0 && ( - <> -
-

Create Connection

-

- Press connect below to start the connection to your Zendesk - instance. -

- - nameBuilder={(values) => `ZendeskConnector`} - ccPairNameBuilder={(values) => `ZendeskConnector`} - source="zendesk" - inputType="poll" - formBody={<>} - validationSchema={Yup.object().shape({})} - initialValues={{}} - refreshFreq={10 * 60} // 10 minutes - credentialId={zendeskCredential.id} - /> -
- - )} + {zendeskCredential && zendeskConnectorIndexingStatuses.length === 0 && ( + <> +
+

Create Connection

+

+ Press connect below to start the connection to your Zendesk + instance. +

+ + nameBuilder={(values) => `ZendeskConnector`} + ccPairNameBuilder={(values) => `ZendeskConnector`} + source="zendesk" + inputType="poll" + formBody={<>} + validationSchema={Yup.object().shape({})} + initialValues={{}} + refreshFreq={10 * 60} // 10 minutes + credentialId={zendeskCredential.id} + /> +
+ + )} {!zendeskCredential && ( <> diff --git a/web/src/components/admin/Layout.tsx b/web/src/components/admin/Layout.tsx index 8d426c068..45d8aac6c 100644 --- a/web/src/components/admin/Layout.tsx +++ b/web/src/components/admin/Layout.tsx @@ -26,7 +26,7 @@ import { GoogleSitesIcon, GongIcon, ZoomInIcon, - ZendeskIcon + ZendeskIcon, } from "@/components/icons/icons"; import { getAuthDisabledSS, getCurrentUserSS } from "@/lib/userSS"; import { redirect } from "next/navigation"; @@ -240,7 +240,7 @@ export async function Layout({ children }: { children: React.ReactNode }) {
), link: "/admin/connectors/zendesk", - } + }, ], }, { diff --git a/web/src/components/icons/icons.tsx b/web/src/components/icons/icons.tsx index 1695ec590..3d62cd7bd 100644 --- a/web/src/components/icons/icons.tsx +++ b/web/src/components/icons/icons.tsx @@ -492,4 +492,4 @@ export const ZendeskIcon = ({ > Logo -); \ No newline at end of file +); diff --git a/web/src/components/source.tsx b/web/src/components/source.tsx index fa6ee3f66..7162512ea 100644 --- a/web/src/components/source.tsx +++ b/web/src/components/source.tsx @@ -142,7 +142,7 @@ export const getSourceMetadata = (sourceType: ValidSources): SourceMetadata => { icon: ZendeskIcon, displayName: "Zendesk", adminPageLink: "/admin/connectors/zendesk", - } + }; default: throw new Error("Invalid source type"); } diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index 61420d96f..028c9c253 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -129,7 +129,7 @@ export interface GoogleSitesConfig { base_url: string; } -export interface ZendeskConfig{} +export interface ZendeskConfig {} export interface IndexAttemptSnapshot { id: number;