mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-05-11 12:20:24 +02:00
Improved google connector flow (#4155)
* fix handling * k * k * fix function * k * k
This commit is contained in:
parent
909403a648
commit
e80a0f2716
@ -360,18 +360,13 @@ def backend_update_credential_json(
|
|||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
|
|
||||||
def delete_credential(
|
def _delete_credential_internal(
|
||||||
|
credential: Credential,
|
||||||
credential_id: int,
|
credential_id: int,
|
||||||
user: User | None,
|
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
credential = fetch_credential_by_id_for_user(credential_id, user, db_session)
|
"""Internal utility function to handle the actual deletion of a credential"""
|
||||||
if credential is None:
|
|
||||||
raise ValueError(
|
|
||||||
f"Credential by provided id {credential_id} does not exist or does not belong to user"
|
|
||||||
)
|
|
||||||
|
|
||||||
associated_connectors = (
|
associated_connectors = (
|
||||||
db_session.query(ConnectorCredentialPair)
|
db_session.query(ConnectorCredentialPair)
|
||||||
.filter(ConnectorCredentialPair.credential_id == credential_id)
|
.filter(ConnectorCredentialPair.credential_id == credential_id)
|
||||||
@ -416,6 +411,35 @@ def delete_credential(
|
|||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_credential_for_user(
|
||||||
|
credential_id: int,
|
||||||
|
user: User,
|
||||||
|
db_session: Session,
|
||||||
|
force: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Delete a credential that belongs to a specific user"""
|
||||||
|
credential = fetch_credential_by_id_for_user(credential_id, user, db_session)
|
||||||
|
if credential is None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Credential by provided id {credential_id} does not exist or does not belong to user"
|
||||||
|
)
|
||||||
|
|
||||||
|
_delete_credential_internal(credential, credential_id, db_session, force)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_credential(
|
||||||
|
credential_id: int,
|
||||||
|
db_session: Session,
|
||||||
|
force: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Delete a credential regardless of ownership (admin function)"""
|
||||||
|
credential = fetch_credential_by_id(credential_id, db_session)
|
||||||
|
if credential is None:
|
||||||
|
raise ValueError(f"Credential by provided id {credential_id} does not exist")
|
||||||
|
|
||||||
|
_delete_credential_internal(credential, credential_id, db_session, force)
|
||||||
|
|
||||||
|
|
||||||
def create_initial_public_credential(db_session: Session) -> None:
|
def create_initial_public_credential(db_session: Session) -> None:
|
||||||
error_msg = (
|
error_msg = (
|
||||||
"DB is not in a valid initial state."
|
"DB is not in a valid initial state."
|
||||||
|
@ -13,6 +13,7 @@ from onyx.db.credentials import cleanup_gmail_credentials
|
|||||||
from onyx.db.credentials import create_credential
|
from onyx.db.credentials import create_credential
|
||||||
from onyx.db.credentials import CREDENTIAL_PERMISSIONS_TO_IGNORE
|
from onyx.db.credentials import CREDENTIAL_PERMISSIONS_TO_IGNORE
|
||||||
from onyx.db.credentials import delete_credential
|
from onyx.db.credentials import delete_credential
|
||||||
|
from onyx.db.credentials import delete_credential_for_user
|
||||||
from onyx.db.credentials import fetch_credential_by_id_for_user
|
from onyx.db.credentials import fetch_credential_by_id_for_user
|
||||||
from onyx.db.credentials import fetch_credentials_by_source_for_user
|
from onyx.db.credentials import fetch_credentials_by_source_for_user
|
||||||
from onyx.db.credentials import fetch_credentials_for_user
|
from onyx.db.credentials import fetch_credentials_for_user
|
||||||
@ -88,7 +89,7 @@ def delete_credential_by_id_admin(
|
|||||||
db_session: Session = Depends(get_session),
|
db_session: Session = Depends(get_session),
|
||||||
) -> StatusResponse:
|
) -> StatusResponse:
|
||||||
"""Same as the user endpoint, but can delete any credential (not just the user's own)"""
|
"""Same as the user endpoint, but can delete any credential (not just the user's own)"""
|
||||||
delete_credential(db_session=db_session, credential_id=credential_id, user=None)
|
delete_credential(db_session=db_session, credential_id=credential_id)
|
||||||
return StatusResponse(
|
return StatusResponse(
|
||||||
success=True, message="Credential deleted successfully", data=credential_id
|
success=True, message="Credential deleted successfully", data=credential_id
|
||||||
)
|
)
|
||||||
@ -242,7 +243,7 @@ def delete_credential_by_id(
|
|||||||
user: User = Depends(current_user),
|
user: User = Depends(current_user),
|
||||||
db_session: Session = Depends(get_session),
|
db_session: Session = Depends(get_session),
|
||||||
) -> StatusResponse:
|
) -> StatusResponse:
|
||||||
delete_credential(
|
delete_credential_for_user(
|
||||||
credential_id,
|
credential_id,
|
||||||
user,
|
user,
|
||||||
db_session,
|
db_session,
|
||||||
@ -259,7 +260,7 @@ def force_delete_credential_by_id(
|
|||||||
user: User = Depends(current_user),
|
user: User = Depends(current_user),
|
||||||
db_session: Session = Depends(get_session),
|
db_session: Session = Depends(get_session),
|
||||||
) -> StatusResponse:
|
) -> StatusResponse:
|
||||||
delete_credential(credential_id, user, db_session, True)
|
delete_credential_for_user(credential_id, user, db_session, True)
|
||||||
|
|
||||||
return StatusResponse(
|
return StatusResponse(
|
||||||
success=True, message="Credential deleted successfully", data=credential_id
|
success=True, message="Credential deleted successfully", data=credential_id
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||||
import { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useSWRConfig } from "swr";
|
import { useSWRConfig } from "swr";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@ -17,13 +17,18 @@ import {
|
|||||||
GoogleDriveCredentialJson,
|
GoogleDriveCredentialJson,
|
||||||
GoogleDriveServiceAccountCredentialJson,
|
GoogleDriveServiceAccountCredentialJson,
|
||||||
} from "@/lib/connectors/credentials";
|
} from "@/lib/connectors/credentials";
|
||||||
|
import { refreshAllGoogleData } from "@/lib/googleConnector";
|
||||||
|
import { ValidSources } from "@/lib/types";
|
||||||
|
import { buildSimilarCredentialInfoURL } from "@/app/admin/connector/[ccPairId]/lib";
|
||||||
|
|
||||||
type GoogleDriveCredentialJsonTypes = "authorized_user" | "service_account";
|
type GoogleDriveCredentialJsonTypes = "authorized_user" | "service_account";
|
||||||
|
|
||||||
export const DriveJsonUpload = ({
|
export const DriveJsonUpload = ({
|
||||||
setPopup,
|
setPopup,
|
||||||
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
setPopup: (popupSpec: PopupSpec | null) => void;
|
setPopup: (popupSpec: PopupSpec | null) => void;
|
||||||
|
onSuccess?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const [credentialJsonStr, setCredentialJsonStr] = useState<
|
const [credentialJsonStr, setCredentialJsonStr] = useState<
|
||||||
@ -62,7 +67,6 @@ export const DriveJsonUpload = ({
|
|||||||
<Button
|
<Button
|
||||||
disabled={!credentialJsonStr}
|
disabled={!credentialJsonStr}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// check if the JSON is a app credential or a service account credential
|
|
||||||
let credentialFileType: GoogleDriveCredentialJsonTypes;
|
let credentialFileType: GoogleDriveCredentialJsonTypes;
|
||||||
try {
|
try {
|
||||||
const appCredentialJson = JSON.parse(credentialJsonStr!);
|
const appCredentialJson = JSON.parse(credentialJsonStr!);
|
||||||
@ -99,6 +103,10 @@ export const DriveJsonUpload = ({
|
|||||||
message: "Successfully uploaded app credentials",
|
message: "Successfully uploaded app credentials",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
mutate("/api/manage/admin/connector/google-drive/app-credential");
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -106,7 +114,6 @@ export const DriveJsonUpload = ({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mutate("/api/manage/admin/connector/google-drive/app-credential");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (credentialFileType === "service_account") {
|
if (credentialFileType === "service_account") {
|
||||||
@ -122,19 +129,22 @@ export const DriveJsonUpload = ({
|
|||||||
);
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully uploaded app credentials",
|
message: "Successfully uploaded service account key",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
const errorMsg = await response.text();
|
|
||||||
setPopup({
|
|
||||||
message: `Failed to upload app credentials - ${errorMsg}`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mutate(
|
mutate(
|
||||||
"/api/manage/admin/connector/google-drive/service-account-key"
|
"/api/manage/admin/connector/google-drive/service-account-key"
|
||||||
);
|
);
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorMsg = await response.text();
|
||||||
|
setPopup({
|
||||||
|
message: `Failed to upload service account key - ${errorMsg}`,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -149,6 +159,7 @@ interface DriveJsonUploadSectionProps {
|
|||||||
appCredentialData?: { client_id: string };
|
appCredentialData?: { client_id: string };
|
||||||
serviceAccountCredentialData?: { service_account_email: string };
|
serviceAccountCredentialData?: { service_account_email: string };
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
onSuccess?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DriveJsonUploadSection = ({
|
export const DriveJsonUploadSection = ({
|
||||||
@ -156,17 +167,36 @@ export const DriveJsonUploadSection = ({
|
|||||||
appCredentialData,
|
appCredentialData,
|
||||||
serviceAccountCredentialData,
|
serviceAccountCredentialData,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
onSuccess,
|
||||||
}: DriveJsonUploadSectionProps) => {
|
}: DriveJsonUploadSectionProps) => {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [localServiceAccountData, setLocalServiceAccountData] = useState(
|
||||||
|
serviceAccountCredentialData
|
||||||
|
);
|
||||||
|
const [localAppCredentialData, setLocalAppCredentialData] =
|
||||||
|
useState(appCredentialData);
|
||||||
|
|
||||||
if (serviceAccountCredentialData?.service_account_email) {
|
useEffect(() => {
|
||||||
|
setLocalServiceAccountData(serviceAccountCredentialData);
|
||||||
|
setLocalAppCredentialData(appCredentialData);
|
||||||
|
}, [serviceAccountCredentialData, appCredentialData]);
|
||||||
|
|
||||||
|
const handleSuccess = () => {
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
refreshAllGoogleData(ValidSources.GoogleDrive);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (localServiceAccountData?.service_account_email) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 text-sm">
|
<div className="mt-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
Found existing service account key with the following <b>Email:</b>
|
Found existing service account key with the following <b>Email:</b>
|
||||||
<p className="italic mt-1">
|
<p className="italic mt-1">
|
||||||
{serviceAccountCredentialData.service_account_email}
|
{localServiceAccountData.service_account_email}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
@ -188,11 +218,15 @@ export const DriveJsonUploadSection = ({
|
|||||||
mutate(
|
mutate(
|
||||||
"/api/manage/admin/connector/google-drive/service-account-key"
|
"/api/manage/admin/connector/google-drive/service-account-key"
|
||||||
);
|
);
|
||||||
|
mutate(
|
||||||
|
buildSimilarCredentialInfoURL(ValidSources.GoogleDrive)
|
||||||
|
);
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully deleted service account key",
|
message: "Successfully deleted service account key",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
router.refresh();
|
setLocalServiceAccountData(undefined);
|
||||||
|
handleSuccess();
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -216,12 +250,12 @@ export const DriveJsonUploadSection = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCredentialData?.client_id) {
|
if (localAppCredentialData?.client_id) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 text-sm">
|
<div className="mt-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
Found existing app credentials with the following <b>Client ID:</b>
|
Found existing app credentials with the following <b>Client ID:</b>
|
||||||
<p className="italic mt-1">{appCredentialData.client_id}</p>
|
<p className="italic mt-1">{localAppCredentialData.client_id}</p>
|
||||||
</div>
|
</div>
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
<>
|
<>
|
||||||
@ -242,10 +276,15 @@ export const DriveJsonUploadSection = ({
|
|||||||
mutate(
|
mutate(
|
||||||
"/api/manage/admin/connector/google-drive/app-credential"
|
"/api/manage/admin/connector/google-drive/app-credential"
|
||||||
);
|
);
|
||||||
|
mutate(
|
||||||
|
buildSimilarCredentialInfoURL(ValidSources.GoogleDrive)
|
||||||
|
);
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully deleted app credentials",
|
message: "Successfully deleted app credentials",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
setLocalAppCredentialData(undefined);
|
||||||
|
handleSuccess();
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -297,7 +336,7 @@ export const DriveJsonUploadSection = ({
|
|||||||
Download the credentials JSON if choosing option (1) or the Service
|
Download the credentials JSON if choosing option (1) or the Service
|
||||||
Account key JSON if chooosing option (2), and upload it here.
|
Account key JSON if chooosing option (2), and upload it here.
|
||||||
</p>
|
</p>
|
||||||
<DriveJsonUpload setPopup={setPopup} />
|
<DriveJsonUpload setPopup={setPopup} onSuccess={handleSuccess} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -348,13 +387,41 @@ export const DriveAuthSection = ({
|
|||||||
appCredentialData,
|
appCredentialData,
|
||||||
setPopup,
|
setPopup,
|
||||||
refreshCredentials,
|
refreshCredentials,
|
||||||
connectorAssociated, // don't allow revoke if a connector / credential pair is active with the uploaded credential
|
connectorAssociated,
|
||||||
user,
|
user,
|
||||||
}: DriveCredentialSectionProps) => {
|
}: DriveCredentialSectionProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [localServiceAccountData, setLocalServiceAccountData] = useState(
|
||||||
|
serviceAccountKeyData
|
||||||
|
);
|
||||||
|
const [localAppCredentialData, setLocalAppCredentialData] =
|
||||||
|
useState(appCredentialData);
|
||||||
|
const [
|
||||||
|
localGoogleDrivePublicCredential,
|
||||||
|
setLocalGoogleDrivePublicCredential,
|
||||||
|
] = useState(googleDrivePublicUploadedCredential);
|
||||||
|
const [
|
||||||
|
localGoogleDriveServiceAccountCredential,
|
||||||
|
setLocalGoogleDriveServiceAccountCredential,
|
||||||
|
] = useState(googleDriveServiceAccountCredential);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalServiceAccountData(serviceAccountKeyData);
|
||||||
|
setLocalAppCredentialData(appCredentialData);
|
||||||
|
setLocalGoogleDrivePublicCredential(googleDrivePublicUploadedCredential);
|
||||||
|
setLocalGoogleDriveServiceAccountCredential(
|
||||||
|
googleDriveServiceAccountCredential
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
serviceAccountKeyData,
|
||||||
|
appCredentialData,
|
||||||
|
googleDrivePublicUploadedCredential,
|
||||||
|
googleDriveServiceAccountCredential,
|
||||||
|
]);
|
||||||
|
|
||||||
const existingCredential =
|
const existingCredential =
|
||||||
googleDrivePublicUploadedCredential || googleDriveServiceAccountCredential;
|
localGoogleDrivePublicCredential ||
|
||||||
|
localGoogleDriveServiceAccountCredential;
|
||||||
if (existingCredential) {
|
if (existingCredential) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -377,7 +444,7 @@ export const DriveAuthSection = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceAccountKeyData?.service_account_email) {
|
if (localServiceAccountData?.service_account_email) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Formik
|
<Formik
|
||||||
@ -438,7 +505,7 @@ export const DriveAuthSection = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCredentialData?.client_id) {
|
if (localAppCredentialData?.client_id) {
|
||||||
return (
|
return (
|
||||||
<div className="text-sm mb-4">
|
<div className="text-sm mb-4">
|
||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import useSWR, { mutate } from "swr";
|
import { FetchError } from "@/lib/fetcher";
|
||||||
import { FetchError, errorHandlingFetcher } from "@/lib/fetcher";
|
|
||||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||||
import { LoadingAnimation } from "@/components/Loading";
|
import { LoadingAnimation } from "@/components/Loading";
|
||||||
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
|
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
|
||||||
@ -15,22 +14,17 @@ import {
|
|||||||
GoogleDriveCredentialJson,
|
GoogleDriveCredentialJson,
|
||||||
GoogleDriveServiceAccountCredentialJson,
|
GoogleDriveServiceAccountCredentialJson,
|
||||||
} from "@/lib/connectors/credentials";
|
} from "@/lib/connectors/credentials";
|
||||||
import { ConnectorSnapshot } from "@/lib/connectors/connectors";
|
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
import { buildSimilarCredentialInfoURL } from "@/app/admin/connector/[ccPairId]/lib";
|
import {
|
||||||
|
useGoogleAppCredential,
|
||||||
const useConnectorsByCredentialId = (credential_id: number | null) => {
|
useGoogleServiceAccountKey,
|
||||||
let url: string | null = null;
|
useGoogleCredentials,
|
||||||
if (credential_id !== null) {
|
useConnectorsByCredentialId,
|
||||||
url = `/api/manage/admin/connector?credential=${credential_id}`;
|
checkCredentialsFetched,
|
||||||
}
|
filterUploadedCredentials,
|
||||||
const swrResponse = useSWR<ConnectorSnapshot[]>(url, errorHandlingFetcher);
|
checkConnectorsExist,
|
||||||
|
refreshAllGoogleData,
|
||||||
return {
|
} from "@/lib/googleConnector";
|
||||||
...swrResponse,
|
|
||||||
refreshConnectorsByCredentialId: () => mutate(url),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const GDriveMain = ({
|
const GDriveMain = ({
|
||||||
setPopup,
|
setPopup,
|
||||||
@ -39,27 +33,20 @@ const GDriveMain = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { isAdmin, user } = useUser();
|
const { isAdmin, user } = useUser();
|
||||||
|
|
||||||
// tries getting the uploaded credential json
|
// Get app credential and service account key
|
||||||
const {
|
const {
|
||||||
data: appCredentialData,
|
data: appCredentialData,
|
||||||
isLoading: isAppCredentialLoading,
|
isLoading: isAppCredentialLoading,
|
||||||
error: isAppCredentialError,
|
error: isAppCredentialError,
|
||||||
} = useSWR<{ client_id: string }, FetchError>(
|
} = useGoogleAppCredential("google_drive");
|
||||||
"/api/manage/admin/connector/google-drive/app-credential",
|
|
||||||
errorHandlingFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
// tries getting the uploaded service account key
|
|
||||||
const {
|
const {
|
||||||
data: serviceAccountKeyData,
|
data: serviceAccountKeyData,
|
||||||
isLoading: isServiceAccountKeyLoading,
|
isLoading: isServiceAccountKeyLoading,
|
||||||
error: isServiceAccountKeyError,
|
error: isServiceAccountKeyError,
|
||||||
} = useSWR<{ service_account_email: string }, FetchError>(
|
} = useGoogleServiceAccountKey("google_drive");
|
||||||
"/api/manage/admin/connector/google-drive/service-account-key",
|
|
||||||
errorHandlingFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
// gets all public credentials
|
// Get all public credentials
|
||||||
const {
|
const {
|
||||||
data: credentialsData,
|
data: credentialsData,
|
||||||
isLoading: isCredentialsLoading,
|
isLoading: isCredentialsLoading,
|
||||||
@ -67,33 +54,19 @@ const GDriveMain = ({
|
|||||||
refreshCredentials,
|
refreshCredentials,
|
||||||
} = usePublicCredentials();
|
} = usePublicCredentials();
|
||||||
|
|
||||||
// gets all credentials for source type google drive
|
// Get Google Drive-specific credentials
|
||||||
const {
|
const {
|
||||||
data: googleDriveCredentials,
|
data: googleDriveCredentials,
|
||||||
isLoading: isGoogleDriveCredentialsLoading,
|
isLoading: isGoogleDriveCredentialsLoading,
|
||||||
error: googleDriveCredentialsError,
|
error: googleDriveCredentialsError,
|
||||||
} = useSWR<Credential<any>[]>(
|
} = useGoogleCredentials(ValidSources.GoogleDrive);
|
||||||
buildSimilarCredentialInfoURL(ValidSources.GoogleDrive),
|
|
||||||
errorHandlingFetcher,
|
// Filter uploaded credentials and get credential ID
|
||||||
{ refreshInterval: 5000 }
|
const { credential_id, uploadedCredentials } = filterUploadedCredentials(
|
||||||
|
googleDriveCredentials
|
||||||
);
|
);
|
||||||
|
|
||||||
// filters down to just credentials that were created via upload (there should be only one)
|
// Get connectors for the credential ID
|
||||||
let credential_id = null;
|
|
||||||
if (googleDriveCredentials) {
|
|
||||||
const googleDriveUploadedCredentials: Credential<GoogleDriveCredentialJson>[] =
|
|
||||||
googleDriveCredentials.filter(
|
|
||||||
(googleDriveCredential) =>
|
|
||||||
googleDriveCredential.credential_json.authentication_method !==
|
|
||||||
"oauth_interactive"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (googleDriveUploadedCredentials.length > 0) {
|
|
||||||
credential_id = googleDriveUploadedCredentials[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieves all connectors for that credential id
|
|
||||||
const {
|
const {
|
||||||
data: googleDriveConnectors,
|
data: googleDriveConnectors,
|
||||||
isLoading: isGoogleDriveConnectorsLoading,
|
isLoading: isGoogleDriveConnectorsLoading,
|
||||||
@ -101,13 +74,25 @@ const GDriveMain = ({
|
|||||||
refreshConnectorsByCredentialId,
|
refreshConnectorsByCredentialId,
|
||||||
} = useConnectorsByCredentialId(credential_id);
|
} = useConnectorsByCredentialId(credential_id);
|
||||||
|
|
||||||
const appCredentialSuccessfullyFetched =
|
// Check if credentials were successfully fetched
|
||||||
appCredentialData ||
|
const {
|
||||||
(isAppCredentialError && isAppCredentialError.status === 404);
|
appCredentialSuccessfullyFetched,
|
||||||
const serviceAccountKeySuccessfullyFetched =
|
serviceAccountKeySuccessfullyFetched,
|
||||||
serviceAccountKeyData ||
|
} = checkCredentialsFetched(
|
||||||
(isServiceAccountKeyError && isServiceAccountKeyError.status === 404);
|
appCredentialData,
|
||||||
|
isAppCredentialError,
|
||||||
|
serviceAccountKeyData,
|
||||||
|
isServiceAccountKeyError
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle refresh of all data
|
||||||
|
const handleRefresh = () => {
|
||||||
|
refreshCredentials();
|
||||||
|
refreshConnectorsByCredentialId();
|
||||||
|
refreshAllGoogleData(ValidSources.GoogleDrive);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loading state
|
||||||
if (
|
if (
|
||||||
(!appCredentialSuccessfullyFetched && isAppCredentialLoading) ||
|
(!appCredentialSuccessfullyFetched && isAppCredentialLoading) ||
|
||||||
(!serviceAccountKeySuccessfullyFetched && isServiceAccountKeyLoading) ||
|
(!serviceAccountKeySuccessfullyFetched && isServiceAccountKeyLoading) ||
|
||||||
@ -122,6 +107,7 @@ const GDriveMain = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error states
|
||||||
if (credentialsError || !credentialsData) {
|
if (credentialsError || !credentialsData) {
|
||||||
return <ErrorCallout errorTitle="Failed to load credentials." />;
|
return <ErrorCallout errorTitle="Failed to load credentials." />;
|
||||||
}
|
}
|
||||||
@ -141,7 +127,16 @@ const GDriveMain = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the actual uploaded oauth or service account credentials
|
if (googleDriveConnectorsError) {
|
||||||
|
return (
|
||||||
|
<ErrorCallout errorTitle="Failed to load Google Drive associated connectors." />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if connectors exist
|
||||||
|
const connectorAssociated = checkConnectorsExist(googleDriveConnectors);
|
||||||
|
|
||||||
|
// Get the uploaded OAuth credential
|
||||||
const googleDrivePublicUploadedCredential:
|
const googleDrivePublicUploadedCredential:
|
||||||
| Credential<GoogleDriveCredentialJson>
|
| Credential<GoogleDriveCredentialJson>
|
||||||
| undefined = credentialsData.find(
|
| undefined = credentialsData.find(
|
||||||
@ -152,6 +147,7 @@ const GDriveMain = ({
|
|||||||
credential.credential_json.authentication_method !== "oauth_interactive"
|
credential.credential_json.authentication_method !== "oauth_interactive"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Get the service account credential
|
||||||
const googleDriveServiceAccountCredential:
|
const googleDriveServiceAccountCredential:
|
||||||
| Credential<GoogleDriveServiceAccountCredentialJson>
|
| Credential<GoogleDriveServiceAccountCredentialJson>
|
||||||
| undefined = credentialsData.find(
|
| undefined = credentialsData.find(
|
||||||
@ -160,19 +156,6 @@ const GDriveMain = ({
|
|||||||
credential.source === "google_drive"
|
credential.source === "google_drive"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (googleDriveConnectorsError) {
|
|
||||||
return (
|
|
||||||
<ErrorCallout errorTitle="Failed to load Google Drive associated connectors." />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let connectorAssociated = false;
|
|
||||||
if (googleDriveConnectors) {
|
|
||||||
if (googleDriveConnectors.length > 0) {
|
|
||||||
connectorAssociated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title className="mb-2 mt-6">Step 1: Provide your Credentials</Title>
|
<Title className="mb-2 mt-6">Step 1: Provide your Credentials</Title>
|
||||||
@ -181,14 +164,17 @@ const GDriveMain = ({
|
|||||||
appCredentialData={appCredentialData}
|
appCredentialData={appCredentialData}
|
||||||
serviceAccountCredentialData={serviceAccountKeyData}
|
serviceAccountCredentialData={serviceAccountKeyData}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
|
onSuccess={handleRefresh}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isAdmin && (
|
{isAdmin &&
|
||||||
|
(appCredentialData?.client_id ||
|
||||||
|
serviceAccountKeyData?.service_account_email) && (
|
||||||
<>
|
<>
|
||||||
<Title className="mb-2 mt-6">Step 2: Authenticate with Onyx</Title>
|
<Title className="mb-2 mt-6">Step 2: Authenticate with Onyx</Title>
|
||||||
<DriveAuthSection
|
<DriveAuthSection
|
||||||
setPopup={setPopup}
|
setPopup={setPopup}
|
||||||
refreshCredentials={refreshCredentials}
|
refreshCredentials={handleRefresh}
|
||||||
googleDrivePublicUploadedCredential={
|
googleDrivePublicUploadedCredential={
|
||||||
googleDrivePublicUploadedCredential
|
googleDrivePublicUploadedCredential
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||||
import { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useSWRConfig } from "swr";
|
import { useSWRConfig } from "swr";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@ -17,13 +17,18 @@ import {
|
|||||||
GmailCredentialJson,
|
GmailCredentialJson,
|
||||||
GmailServiceAccountCredentialJson,
|
GmailServiceAccountCredentialJson,
|
||||||
} from "@/lib/connectors/credentials";
|
} from "@/lib/connectors/credentials";
|
||||||
|
import { refreshAllGoogleData } from "@/lib/googleConnector";
|
||||||
|
import { ValidSources } from "@/lib/types";
|
||||||
|
import { buildSimilarCredentialInfoURL } from "@/app/admin/connector/[ccPairId]/lib";
|
||||||
|
|
||||||
type GmailCredentialJsonTypes = "authorized_user" | "service_account";
|
type GmailCredentialJsonTypes = "authorized_user" | "service_account";
|
||||||
|
|
||||||
const DriveJsonUpload = ({
|
const DriveJsonUpload = ({
|
||||||
setPopup,
|
setPopup,
|
||||||
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
setPopup: (popupSpec: PopupSpec | null) => void;
|
setPopup: (popupSpec: PopupSpec | null) => void;
|
||||||
|
onSuccess?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const [credentialJsonStr, setCredentialJsonStr] = useState<
|
const [credentialJsonStr, setCredentialJsonStr] = useState<
|
||||||
@ -72,7 +77,7 @@ const DriveJsonUpload = ({
|
|||||||
credentialFileType = "service_account";
|
credentialFileType = "service_account";
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Unknown credential type, expected 'OAuth Web application'"
|
"Unknown credential type, expected one of 'OAuth Web application' or 'Service Account'"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -99,6 +104,10 @@ const DriveJsonUpload = ({
|
|||||||
message: "Successfully uploaded app credentials",
|
message: "Successfully uploaded app credentials",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
mutate("/api/manage/admin/connector/gmail/app-credential");
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -106,7 +115,6 @@ const DriveJsonUpload = ({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mutate("/api/manage/admin/connector/gmail/app-credential");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (credentialFileType === "service_account") {
|
if (credentialFileType === "service_account") {
|
||||||
@ -122,17 +130,20 @@ const DriveJsonUpload = ({
|
|||||||
);
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully uploaded app credentials",
|
message: "Successfully uploaded service account key",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
mutate("/api/manage/admin/connector/gmail/service-account-key");
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
message: `Failed to upload app credentials - ${errorMsg}`,
|
message: `Failed to upload service account key - ${errorMsg}`,
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mutate("/api/manage/admin/connector/gmail/service-account-key");
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -147,6 +158,7 @@ interface DriveJsonUploadSectionProps {
|
|||||||
appCredentialData?: { client_id: string };
|
appCredentialData?: { client_id: string };
|
||||||
serviceAccountCredentialData?: { service_account_email: string };
|
serviceAccountCredentialData?: { service_account_email: string };
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
onSuccess?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GmailJsonUploadSection = ({
|
export const GmailJsonUploadSection = ({
|
||||||
@ -154,16 +166,37 @@ export const GmailJsonUploadSection = ({
|
|||||||
appCredentialData,
|
appCredentialData,
|
||||||
serviceAccountCredentialData,
|
serviceAccountCredentialData,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
onSuccess,
|
||||||
}: DriveJsonUploadSectionProps) => {
|
}: DriveJsonUploadSectionProps) => {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
|
const router = useRouter();
|
||||||
|
const [localServiceAccountData, setLocalServiceAccountData] = useState(
|
||||||
|
serviceAccountCredentialData
|
||||||
|
);
|
||||||
|
const [localAppCredentialData, setLocalAppCredentialData] =
|
||||||
|
useState(appCredentialData);
|
||||||
|
|
||||||
if (serviceAccountCredentialData?.service_account_email) {
|
// Update local state when props change
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalServiceAccountData(serviceAccountCredentialData);
|
||||||
|
setLocalAppCredentialData(appCredentialData);
|
||||||
|
}, [serviceAccountCredentialData, appCredentialData]);
|
||||||
|
|
||||||
|
const handleSuccess = () => {
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
refreshAllGoogleData(ValidSources.Gmail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (localServiceAccountData?.service_account_email) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 text-sm">
|
<div className="mt-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
Found existing service account key with the following <b>Email:</b>
|
Found existing service account key with the following <b>Email:</b>
|
||||||
<p className="italic mt-1">
|
<p className="italic mt-1">
|
||||||
{serviceAccountCredentialData.service_account_email}
|
{localServiceAccountData.service_account_email}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
@ -185,10 +218,15 @@ export const GmailJsonUploadSection = ({
|
|||||||
mutate(
|
mutate(
|
||||||
"/api/manage/admin/connector/gmail/service-account-key"
|
"/api/manage/admin/connector/gmail/service-account-key"
|
||||||
);
|
);
|
||||||
|
// Also mutate the credential endpoints to ensure Step 2 is reset
|
||||||
|
mutate(buildSimilarCredentialInfoURL(ValidSources.Gmail));
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully deleted service account key",
|
message: "Successfully deleted service account key",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
// Immediately update local state
|
||||||
|
setLocalServiceAccountData(undefined);
|
||||||
|
handleSuccess();
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -212,13 +250,15 @@ export const GmailJsonUploadSection = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCredentialData?.client_id) {
|
if (localAppCredentialData?.client_id) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 text-sm">
|
<div className="mt-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
Found existing app credentials with the following <b>Client ID:</b>
|
Found existing app credentials with the following <b>Client ID:</b>
|
||||||
<p className="italic mt-1">{appCredentialData.client_id}</p>
|
<p className="italic mt-1">{localAppCredentialData.client_id}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{isAdmin ? (
|
||||||
|
<>
|
||||||
<div className="mt-4 mb-1">
|
<div className="mt-4 mb-1">
|
||||||
If you want to update these credentials, delete the existing
|
If you want to update these credentials, delete the existing
|
||||||
credentials through the button below, and then upload a new
|
credentials through the button below, and then upload a new
|
||||||
@ -234,10 +274,15 @@ export const GmailJsonUploadSection = ({
|
|||||||
);
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
mutate("/api/manage/admin/connector/gmail/app-credential");
|
mutate("/api/manage/admin/connector/gmail/app-credential");
|
||||||
|
// Also mutate the credential endpoints to ensure Step 2 is reset
|
||||||
|
mutate(buildSimilarCredentialInfoURL(ValidSources.Gmail));
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully deleted service account key",
|
message: "Successfully deleted app credentials",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
// Immediately update local state
|
||||||
|
setLocalAppCredentialData(undefined);
|
||||||
|
handleSuccess();
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -249,6 +294,12 @@ export const GmailJsonUploadSection = ({
|
|||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4 mb-1">
|
||||||
|
To change these credentials, please contact an administrator.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -276,14 +327,14 @@ export const GmailJsonUploadSection = ({
|
|||||||
>
|
>
|
||||||
here
|
here
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
to either (1) setup a google OAuth App in your company workspace or (2)
|
to either (1) setup a Google OAuth App in your company workspace or (2)
|
||||||
create a Service Account.
|
create a Service Account.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Download the credentials JSON if choosing option (1) or the Service
|
Download the credentials JSON if choosing option (1) or the Service
|
||||||
Account key JSON if chooosing option (2), and upload it here.
|
Account key JSON if choosing option (2), and upload it here.
|
||||||
</p>
|
</p>
|
||||||
<DriveJsonUpload setPopup={setPopup} />
|
<DriveJsonUpload setPopup={setPopup} onSuccess={handleSuccess} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -299,6 +350,34 @@ interface DriveCredentialSectionProps {
|
|||||||
user: User | null;
|
user: User | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRevokeAccess(
|
||||||
|
connectorExists: boolean,
|
||||||
|
setPopup: (popupSpec: PopupSpec | null) => void,
|
||||||
|
existingCredential:
|
||||||
|
| Credential<GmailCredentialJson>
|
||||||
|
| Credential<GmailServiceAccountCredentialJson>,
|
||||||
|
refreshCredentials: () => void
|
||||||
|
) {
|
||||||
|
if (connectorExists) {
|
||||||
|
const message =
|
||||||
|
"Cannot revoke the Gmail credential while any connector is still associated with the credential. " +
|
||||||
|
"Please delete all associated connectors, then try again.";
|
||||||
|
setPopup({
|
||||||
|
message: message,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await adminDeleteCredential(existingCredential.id);
|
||||||
|
setPopup({
|
||||||
|
message: "Successfully revoked the Gmail credential!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
export const GmailAuthSection = ({
|
export const GmailAuthSection = ({
|
||||||
gmailPublicCredential,
|
gmailPublicCredential,
|
||||||
gmailServiceAccountCredential,
|
gmailServiceAccountCredential,
|
||||||
@ -310,31 +389,49 @@ export const GmailAuthSection = ({
|
|||||||
user,
|
user,
|
||||||
}: DriveCredentialSectionProps) => {
|
}: DriveCredentialSectionProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
||||||
|
const [localServiceAccountData, setLocalServiceAccountData] = useState(
|
||||||
|
serviceAccountKeyData
|
||||||
|
);
|
||||||
|
const [localAppCredentialData, setLocalAppCredentialData] =
|
||||||
|
useState(appCredentialData);
|
||||||
|
const [localGmailPublicCredential, setLocalGmailPublicCredential] = useState(
|
||||||
|
gmailPublicCredential
|
||||||
|
);
|
||||||
|
const [
|
||||||
|
localGmailServiceAccountCredential,
|
||||||
|
setLocalGmailServiceAccountCredential,
|
||||||
|
] = useState(gmailServiceAccountCredential);
|
||||||
|
|
||||||
|
// Update local state when props change
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalServiceAccountData(serviceAccountKeyData);
|
||||||
|
setLocalAppCredentialData(appCredentialData);
|
||||||
|
setLocalGmailPublicCredential(gmailPublicCredential);
|
||||||
|
setLocalGmailServiceAccountCredential(gmailServiceAccountCredential);
|
||||||
|
}, [
|
||||||
|
serviceAccountKeyData,
|
||||||
|
appCredentialData,
|
||||||
|
gmailPublicCredential,
|
||||||
|
gmailServiceAccountCredential,
|
||||||
|
]);
|
||||||
|
|
||||||
const existingCredential =
|
const existingCredential =
|
||||||
gmailPublicCredential || gmailServiceAccountCredential;
|
localGmailPublicCredential || localGmailServiceAccountCredential;
|
||||||
if (existingCredential) {
|
if (existingCredential) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="mb-2 text-sm">
|
<p className="mb-2 text-sm">
|
||||||
<i>Existing credential already set up!</i>
|
<i>Uploaded and authenticated credential already exists!</i>
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (connectorExists) {
|
handleRevokeAccess(
|
||||||
setPopup({
|
connectorExists,
|
||||||
message:
|
setPopup,
|
||||||
"Cannot revoke access to Gmail while any connector is still set up. Please delete all connectors, then try again.",
|
existingCredential,
|
||||||
type: "error",
|
refreshCredentials
|
||||||
});
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
await adminDeleteCredential(existingCredential.id);
|
|
||||||
setPopup({
|
|
||||||
message: "Successfully revoked access to Gmail!",
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
refreshCredentials();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Revoke Access
|
Revoke Access
|
||||||
@ -343,20 +440,21 @@ export const GmailAuthSection = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceAccountKeyData?.service_account_email) {
|
if (localServiceAccountData?.service_account_email) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CardSection>
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
google_primary_admin: user?.email || "",
|
google_primary_admin: user?.email || "",
|
||||||
}}
|
}}
|
||||||
validationSchema={Yup.object().shape({
|
validationSchema={Yup.object().shape({
|
||||||
google_primary_admin: Yup.string().required(),
|
google_primary_admin: Yup.string()
|
||||||
|
.email("Must be a valid email")
|
||||||
|
.required("Required"),
|
||||||
})}
|
})}
|
||||||
onSubmit={async (values, formikHelpers) => {
|
onSubmit={async (values, formikHelpers) => {
|
||||||
formikHelpers.setSubmitting(true);
|
formikHelpers.setSubmitting(true);
|
||||||
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"/api/manage/admin/connector/gmail/service-account-credential",
|
"/api/manage/admin/connector/gmail/service-account-credential",
|
||||||
{
|
{
|
||||||
@ -375,6 +473,7 @@ export const GmailAuthSection = ({
|
|||||||
message: "Successfully created service account credential",
|
message: "Successfully created service account credential",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
refreshCredentials();
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = await response.text();
|
const errorMsg = await response.text();
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -382,7 +481,14 @@ export const GmailAuthSection = ({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
refreshCredentials();
|
} catch (error) {
|
||||||
|
setPopup({
|
||||||
|
message: `Failed to create service account credential - ${error}`,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
formikHelpers.setSubmitting(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{({ isSubmitting }) => (
|
||||||
@ -390,57 +496,58 @@ export const GmailAuthSection = ({
|
|||||||
<TextFormField
|
<TextFormField
|
||||||
name="google_primary_admin"
|
name="google_primary_admin"
|
||||||
label="Primary Admin Email:"
|
label="Primary Admin Email:"
|
||||||
subtext="You must provide an admin/owner account to retrieve all org emails."
|
subtext="Enter the email of an admin/owner of the Google Organization that owns the Gmail account(s) you want to index."
|
||||||
/>
|
/>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<Button type="submit" disabled={isSubmitting}>
|
||||||
type="submit"
|
Create Credential
|
||||||
disabled={isSubmitting}
|
</Button>
|
||||||
className={
|
|
||||||
"bg-slate-500 hover:bg-slate-700 text-white " +
|
|
||||||
"font-bold py-2 px-4 rounded focus:outline-none " +
|
|
||||||
"focus:shadow-outline w-full max-w-sm mx-auto"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</CardSection>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCredentialData?.client_id) {
|
if (localAppCredentialData?.client_id) {
|
||||||
return (
|
return (
|
||||||
<div className="text-sm mb-4">
|
<div className="text-sm mb-4">
|
||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
Next, you must provide credentials via OAuth. This gives us read
|
Next, you must provide credentials via OAuth. This gives us read
|
||||||
access to the docs you have access to in your gmail account.
|
access to the emails you have access to in your Gmail account.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const [authUrl, errorMsg] = await setupGmailOAuth({
|
setIsAuthenticating(true);
|
||||||
isAdmin: true,
|
try {
|
||||||
});
|
|
||||||
if (authUrl) {
|
|
||||||
// cookie used by callback to determine where to finally redirect to
|
|
||||||
Cookies.set(GMAIL_AUTH_IS_ADMIN_COOKIE_NAME, "true", {
|
Cookies.set(GMAIL_AUTH_IS_ADMIN_COOKIE_NAME, "true", {
|
||||||
path: "/",
|
path: "/",
|
||||||
});
|
});
|
||||||
router.push(authUrl);
|
const [authUrl, errorMsg] = await setupGmailOAuth({
|
||||||
return;
|
isAdmin: true,
|
||||||
}
|
});
|
||||||
|
|
||||||
|
if (authUrl) {
|
||||||
|
router.push(authUrl);
|
||||||
|
} else {
|
||||||
setPopup({
|
setPopup({
|
||||||
message: errorMsg,
|
message: errorMsg,
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
|
setIsAuthenticating(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setPopup({
|
||||||
|
message: `Failed to authenticate with Gmail - ${error}`,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
setIsAuthenticating(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
disabled={isAuthenticating}
|
||||||
>
|
>
|
||||||
Authenticate with Gmail
|
{isAuthenticating ? "Authenticating..." : "Authenticate with Gmail"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -449,8 +556,8 @@ export const GmailAuthSection = ({
|
|||||||
// case where no keys have been uploaded in step 1
|
// case where no keys have been uploaded in step 1
|
||||||
return (
|
return (
|
||||||
<p className="text-sm">
|
<p className="text-sm">
|
||||||
Please upload an OAuth or Service Account Credential JSON in Step 1 before
|
Please upload either a OAuth Client Credential JSON or a Gmail Service
|
||||||
moving onto Step 2.
|
Account Key JSON in Step 1 before moving onto Step 2.
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import useSWR from "swr";
|
import React from "react";
|
||||||
import { errorHandlingFetcher } from "@/lib/fetcher";
|
import { FetchError } from "@/lib/fetcher";
|
||||||
|
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||||
import { LoadingAnimation } from "@/components/Loading";
|
import { LoadingAnimation } from "@/components/Loading";
|
||||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
|
||||||
import { CCPairBasicInfo } from "@/lib/types";
|
import { CCPairBasicInfo, ValidSources } from "@/lib/types";
|
||||||
import {
|
import {
|
||||||
Credential,
|
Credential,
|
||||||
GmailCredentialJson,
|
GmailCredentialJson,
|
||||||
@ -14,26 +15,33 @@ import { GmailAuthSection, GmailJsonUploadSection } from "./Credential";
|
|||||||
import { usePublicCredentials, useBasicConnectorStatus } from "@/lib/hooks";
|
import { usePublicCredentials, useBasicConnectorStatus } from "@/lib/hooks";
|
||||||
import Title from "@/components/ui/title";
|
import Title from "@/components/ui/title";
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
|
import {
|
||||||
|
useGoogleAppCredential,
|
||||||
|
useGoogleServiceAccountKey,
|
||||||
|
useGoogleCredentials,
|
||||||
|
useConnectorsByCredentialId,
|
||||||
|
checkCredentialsFetched,
|
||||||
|
filterUploadedCredentials,
|
||||||
|
checkConnectorsExist,
|
||||||
|
refreshAllGoogleData,
|
||||||
|
} from "@/lib/googleConnector";
|
||||||
|
|
||||||
export const GmailMain = () => {
|
export const GmailMain = () => {
|
||||||
const { isAdmin, user } = useUser();
|
const { isAdmin, user } = useUser();
|
||||||
|
const { popup, setPopup } = usePopup();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: appCredentialData,
|
data: appCredentialData,
|
||||||
isLoading: isAppCredentialLoading,
|
isLoading: isAppCredentialLoading,
|
||||||
error: isAppCredentialError,
|
error: isAppCredentialError,
|
||||||
} = useSWR<{ client_id: string }>(
|
} = useGoogleAppCredential("gmail");
|
||||||
"/api/manage/admin/connector/gmail/app-credential",
|
|
||||||
errorHandlingFetcher
|
|
||||||
);
|
|
||||||
const {
|
const {
|
||||||
data: serviceAccountKeyData,
|
data: serviceAccountKeyData,
|
||||||
isLoading: isServiceAccountKeyLoading,
|
isLoading: isServiceAccountKeyLoading,
|
||||||
error: isServiceAccountKeyError,
|
error: isServiceAccountKeyError,
|
||||||
} = useSWR<{ service_account_email: string }>(
|
} = useGoogleServiceAccountKey("gmail");
|
||||||
"/api/manage/admin/connector/gmail/service-account-key",
|
|
||||||
errorHandlingFetcher
|
|
||||||
);
|
|
||||||
const {
|
const {
|
||||||
data: connectorIndexingStatuses,
|
data: connectorIndexingStatuses,
|
||||||
isLoading: isConnectorIndexingStatusesLoading,
|
isLoading: isConnectorIndexingStatusesLoading,
|
||||||
@ -47,20 +55,45 @@ export const GmailMain = () => {
|
|||||||
refreshCredentials,
|
refreshCredentials,
|
||||||
} = usePublicCredentials();
|
} = usePublicCredentials();
|
||||||
|
|
||||||
const { popup, setPopup } = usePopup();
|
const {
|
||||||
|
data: gmailCredentials,
|
||||||
|
isLoading: isGmailCredentialsLoading,
|
||||||
|
error: gmailCredentialsError,
|
||||||
|
} = useGoogleCredentials(ValidSources.Gmail);
|
||||||
|
|
||||||
const appCredentialSuccessfullyFetched =
|
const { credential_id, uploadedCredentials } =
|
||||||
appCredentialData ||
|
filterUploadedCredentials(gmailCredentials);
|
||||||
(isAppCredentialError && isAppCredentialError.status === 404);
|
|
||||||
const serviceAccountKeySuccessfullyFetched =
|
const {
|
||||||
serviceAccountKeyData ||
|
data: gmailConnectors,
|
||||||
(isServiceAccountKeyError && isServiceAccountKeyError.status === 404);
|
isLoading: isGmailConnectorsLoading,
|
||||||
|
error: gmailConnectorsError,
|
||||||
|
refreshConnectorsByCredentialId,
|
||||||
|
} = useConnectorsByCredentialId(credential_id);
|
||||||
|
|
||||||
|
const {
|
||||||
|
appCredentialSuccessfullyFetched,
|
||||||
|
serviceAccountKeySuccessfullyFetched,
|
||||||
|
} = checkCredentialsFetched(
|
||||||
|
appCredentialData,
|
||||||
|
isAppCredentialError,
|
||||||
|
serviceAccountKeyData,
|
||||||
|
isServiceAccountKeyError
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
|
refreshCredentials();
|
||||||
|
refreshConnectorsByCredentialId();
|
||||||
|
refreshAllGoogleData(ValidSources.Gmail);
|
||||||
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!appCredentialSuccessfullyFetched && isAppCredentialLoading) ||
|
(!appCredentialSuccessfullyFetched && isAppCredentialLoading) ||
|
||||||
(!serviceAccountKeySuccessfullyFetched && isServiceAccountKeyLoading) ||
|
(!serviceAccountKeySuccessfullyFetched && isServiceAccountKeyLoading) ||
|
||||||
(!connectorIndexingStatuses && isConnectorIndexingStatusesLoading) ||
|
(!connectorIndexingStatuses && isConnectorIndexingStatusesLoading) ||
|
||||||
(!credentialsData && isCredentialsLoading)
|
(!credentialsData && isCredentialsLoading) ||
|
||||||
|
(!gmailCredentials && isGmailCredentialsLoading) ||
|
||||||
|
(!gmailConnectors && isGmailConnectorsLoading)
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">
|
||||||
@ -70,19 +103,15 @@ export const GmailMain = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (credentialsError || !credentialsData) {
|
if (credentialsError || !credentialsData) {
|
||||||
return (
|
return <ErrorCallout errorTitle="Failed to load credentials." />;
|
||||||
<div className="mx-auto">
|
}
|
||||||
<div className="text-red-500">Failed to load credentials.</div>
|
|
||||||
</div>
|
if (gmailCredentialsError || !gmailCredentials) {
|
||||||
);
|
return <ErrorCallout errorTitle="Failed to load Gmail credentials." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectorIndexingStatusesError || !connectorIndexingStatuses) {
|
if (connectorIndexingStatusesError || !connectorIndexingStatuses) {
|
||||||
return (
|
return <ErrorCallout errorTitle="Failed to load connectors." />;
|
||||||
<div className="mx-auto">
|
|
||||||
<div className="text-red-500">Failed to load connectors.</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -90,21 +119,28 @@ export const GmailMain = () => {
|
|||||||
!serviceAccountKeySuccessfullyFetched
|
!serviceAccountKeySuccessfullyFetched
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto">
|
<ErrorCallout errorTitle="Error loading Gmail app credentials. Contact an administrator." />
|
||||||
<div className="text-red-500">
|
|
||||||
Error loading Gmail app credentials. Contact an administrator.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gmailPublicCredential: Credential<GmailCredentialJson> | undefined =
|
if (gmailConnectorsError) {
|
||||||
credentialsData.find(
|
return (
|
||||||
(credential) =>
|
<ErrorCallout errorTitle="Failed to load Gmail associated connectors." />
|
||||||
(credential.credential_json?.google_service_account_key ||
|
|
||||||
credential.credential_json?.google_tokens) &&
|
|
||||||
credential.admin_public
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectorExistsFromCredential = checkConnectorsExist(gmailConnectors);
|
||||||
|
|
||||||
|
const gmailPublicUploadedCredential:
|
||||||
|
| Credential<GmailCredentialJson>
|
||||||
|
| undefined = credentialsData.find(
|
||||||
|
(credential) =>
|
||||||
|
credential.credential_json?.google_tokens &&
|
||||||
|
credential.admin_public &&
|
||||||
|
credential.source === "gmail" &&
|
||||||
|
credential.credential_json.authentication_method !== "oauth_interactive"
|
||||||
|
);
|
||||||
|
|
||||||
const gmailServiceAccountCredential:
|
const gmailServiceAccountCredential:
|
||||||
| Credential<GmailServiceAccountCredentialJson>
|
| Credential<GmailServiceAccountCredentialJson>
|
||||||
| undefined = credentialsData.find(
|
| undefined = credentialsData.find(
|
||||||
@ -118,6 +154,13 @@ export const GmailMain = () => {
|
|||||||
(connectorIndexingStatus) => connectorIndexingStatus.source === "gmail"
|
(connectorIndexingStatus) => connectorIndexingStatus.source === "gmail"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const connectorExists =
|
||||||
|
connectorExistsFromCredential || gmailConnectorIndexingStatuses.length > 0;
|
||||||
|
|
||||||
|
const hasUploadedCredentials =
|
||||||
|
Boolean(appCredentialData?.client_id) ||
|
||||||
|
Boolean(serviceAccountKeyData?.service_account_email);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{popup}
|
{popup}
|
||||||
@ -129,21 +172,22 @@ export const GmailMain = () => {
|
|||||||
appCredentialData={appCredentialData}
|
appCredentialData={appCredentialData}
|
||||||
serviceAccountCredentialData={serviceAccountKeyData}
|
serviceAccountCredentialData={serviceAccountKeyData}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
|
onSuccess={handleRefresh}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && hasUploadedCredentials && (
|
||||||
<>
|
<>
|
||||||
<Title className="mb-2 mt-6 ml-auto mr-auto">
|
<Title className="mb-2 mt-6 ml-auto mr-auto">
|
||||||
Step 2: Authenticate with Onyx
|
Step 2: Authenticate with Onyx
|
||||||
</Title>
|
</Title>
|
||||||
<GmailAuthSection
|
<GmailAuthSection
|
||||||
setPopup={setPopup}
|
setPopup={setPopup}
|
||||||
refreshCredentials={refreshCredentials}
|
refreshCredentials={handleRefresh}
|
||||||
gmailPublicCredential={gmailPublicCredential}
|
gmailPublicCredential={gmailPublicUploadedCredential}
|
||||||
gmailServiceAccountCredential={gmailServiceAccountCredential}
|
gmailServiceAccountCredential={gmailServiceAccountCredential}
|
||||||
appCredentialData={appCredentialData}
|
appCredentialData={appCredentialData}
|
||||||
serviceAccountKeyData={serviceAccountKeyData}
|
serviceAccountKeyData={serviceAccountKeyData}
|
||||||
connectorExists={gmailConnectorIndexingStatuses.length > 0}
|
connectorExists={connectorExists}
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -3,7 +3,6 @@ interface Props {
|
|||||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||||
type?: "button" | "submit" | "reset";
|
type?: "button" | "submit" | "reset";
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
fullWidth?: boolean;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,14 +11,12 @@ export const Button = ({
|
|||||||
onClick,
|
onClick,
|
||||||
type = "submit",
|
type = "submit",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
fullWidth = false,
|
|
||||||
className = "",
|
className = "",
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
"group relative " +
|
"group relative " +
|
||||||
(fullWidth ? "w-full " : "") +
|
|
||||||
"py-1 px-2 border border-transparent text-sm " +
|
"py-1 px-2 border border-transparent text-sm " +
|
||||||
"font-medium rounded-md text-white " +
|
"font-medium rounded-md text-white " +
|
||||||
"focus:outline-none focus:ring-2 " +
|
"focus:outline-none focus:ring-2 " +
|
||||||
|
120
web/src/lib/googleConnector.ts
Normal file
120
web/src/lib/googleConnector.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import useSWR, { mutate } from "swr";
|
||||||
|
import { FetchError, errorHandlingFetcher } from "@/lib/fetcher";
|
||||||
|
import { Credential } from "@/lib/connectors/credentials";
|
||||||
|
import { ConnectorSnapshot } from "@/lib/connectors/connectors";
|
||||||
|
import { ValidSources } from "@/lib/types";
|
||||||
|
import { buildSimilarCredentialInfoURL } from "@/app/admin/connector/[ccPairId]/lib";
|
||||||
|
|
||||||
|
// Constants for service names to avoid typos
|
||||||
|
export const GOOGLE_SERVICES = {
|
||||||
|
GMAIL: "gmail",
|
||||||
|
GOOGLE_DRIVE: "google-drive",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const useGoogleAppCredential = (service: "gmail" | "google_drive") => {
|
||||||
|
const endpoint = `/api/manage/admin/connector/${
|
||||||
|
service === "gmail" ? GOOGLE_SERVICES.GMAIL : GOOGLE_SERVICES.GOOGLE_DRIVE
|
||||||
|
}/app-credential`;
|
||||||
|
|
||||||
|
return useSWR<{ client_id: string }, FetchError>(
|
||||||
|
endpoint,
|
||||||
|
errorHandlingFetcher
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGoogleServiceAccountKey = (
|
||||||
|
service: "gmail" | "google_drive"
|
||||||
|
) => {
|
||||||
|
const endpoint = `/api/manage/admin/connector/${
|
||||||
|
service === "gmail" ? GOOGLE_SERVICES.GMAIL : GOOGLE_SERVICES.GOOGLE_DRIVE
|
||||||
|
}/service-account-key`;
|
||||||
|
|
||||||
|
return useSWR<{ service_account_email: string }, FetchError>(
|
||||||
|
endpoint,
|
||||||
|
errorHandlingFetcher
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGoogleCredentials = (
|
||||||
|
source: ValidSources.Gmail | ValidSources.GoogleDrive
|
||||||
|
) => {
|
||||||
|
return useSWR<Credential<any>[]>(
|
||||||
|
buildSimilarCredentialInfoURL(source),
|
||||||
|
errorHandlingFetcher,
|
||||||
|
{ refreshInterval: 5000 }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useConnectorsByCredentialId = (credential_id: number | null) => {
|
||||||
|
let url: string | null = null;
|
||||||
|
if (credential_id !== null) {
|
||||||
|
url = `/api/manage/admin/connector?credential=${credential_id}`;
|
||||||
|
}
|
||||||
|
const swrResponse = useSWR<ConnectorSnapshot[]>(url, errorHandlingFetcher);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...swrResponse,
|
||||||
|
refreshConnectorsByCredentialId: () => mutate(url),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkCredentialsFetched = (
|
||||||
|
appCredentialData: any,
|
||||||
|
appCredentialError: FetchError | undefined,
|
||||||
|
serviceAccountKeyData: any,
|
||||||
|
serviceAccountKeyError: FetchError | undefined
|
||||||
|
) => {
|
||||||
|
const appCredentialSuccessfullyFetched =
|
||||||
|
appCredentialData ||
|
||||||
|
(appCredentialError && appCredentialError.status === 404);
|
||||||
|
|
||||||
|
const serviceAccountKeySuccessfullyFetched =
|
||||||
|
serviceAccountKeyData ||
|
||||||
|
(serviceAccountKeyError && serviceAccountKeyError.status === 404);
|
||||||
|
|
||||||
|
return {
|
||||||
|
appCredentialSuccessfullyFetched,
|
||||||
|
serviceAccountKeySuccessfullyFetched,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterUploadedCredentials = <
|
||||||
|
T extends { authentication_method?: string },
|
||||||
|
>(
|
||||||
|
credentials: Credential<T>[] | undefined
|
||||||
|
): { credential_id: number | null; uploadedCredentials: Credential<T>[] } => {
|
||||||
|
let credential_id = null;
|
||||||
|
let uploadedCredentials: Credential<T>[] = [];
|
||||||
|
|
||||||
|
if (credentials) {
|
||||||
|
uploadedCredentials = credentials.filter(
|
||||||
|
(credential) =>
|
||||||
|
credential.credential_json.authentication_method !== "oauth_interactive"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (uploadedCredentials.length > 0) {
|
||||||
|
credential_id = uploadedCredentials[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { credential_id, uploadedCredentials };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkConnectorsExist = (
|
||||||
|
connectors: ConnectorSnapshot[] | undefined
|
||||||
|
): boolean => {
|
||||||
|
return !!connectors && connectors.length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const refreshAllGoogleData = (
|
||||||
|
source: ValidSources.Gmail | ValidSources.GoogleDrive
|
||||||
|
) => {
|
||||||
|
mutate(buildSimilarCredentialInfoURL(source));
|
||||||
|
|
||||||
|
const service =
|
||||||
|
source === ValidSources.Gmail
|
||||||
|
? GOOGLE_SERVICES.GMAIL
|
||||||
|
: GOOGLE_SERVICES.GOOGLE_DRIVE;
|
||||||
|
mutate(`/api/manage/admin/connector/${service}/app-credential`);
|
||||||
|
mutate(`/api/manage/admin/connector/${service}/service-account-key`);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user