From d1846823ba57262c53ff9945f34703ad3c893b34 Mon Sep 17 00:00:00 2001 From: Weves Date: Wed, 29 Nov 2023 18:10:56 -0800 Subject: [PATCH] Associate a user with web/file connectors --- web/src/app/admin/connectors/file/page.tsx | 23 ++++++++++-- web/src/app/admin/connectors/web/page.tsx | 7 +++- .../admin/connectors/ConnectorForm.tsx | 35 ++++++++++++++++--- .../admin/connectors/CredentialForm.tsx | 12 ++----- web/src/lib/credential.ts | 12 +++++++ 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/web/src/app/admin/connectors/file/page.tsx b/web/src/app/admin/connectors/file/page.tsx index a4674ce0d..8f8206f7b 100644 --- a/web/src/app/admin/connectors/file/page.tsx +++ b/web/src/app/admin/connectors/file/page.tsx @@ -7,7 +7,7 @@ import { FileIcon } from "@/components/icons/icons"; import { fetcher } from "@/lib/fetcher"; import { HealthCheckBanner } from "@/components/health/healthcheck"; import { ConnectorIndexingStatus, FileConfig } from "@/lib/types"; -import { linkCredential } from "@/lib/credential"; +import { createCredential, linkCredential } from "@/lib/credential"; import { useState } from "react"; import { usePopup } from "@/components/admin/connectors/Popup"; import { createConnector, runConnector } from "@/lib/connector"; @@ -125,9 +125,28 @@ const Main = () => { return; } + // Since there is no "real" credential associated with a file connector + // we create a dummy one here so that we can associate the CC Pair with a + // user. This is needed since the user for a CC Pair is found via the credential + // associated with it. + const createCredentialResponse = await createCredential({ + credential_json: {}, + admin_public: true, + }); + if (!createCredentialResponse.ok) { + const errorMsg = await createCredentialResponse.text(); + setPopup({ + message: `Error creating credential for CC Pair - ${errorMsg}`, + type: "error", + }); + formikHelpers.setSubmitting(false); + return; + } + const credentialId = (await createCredentialResponse.json()).id; + const credentialResponse = await linkCredential( connector.id, - 0, + credentialId, values.name ); if (!credentialResponse.ok) { diff --git a/web/src/app/admin/connectors/web/page.tsx b/web/src/app/admin/connectors/web/page.tsx index 1b8b50b5d..75afda7e7 100644 --- a/web/src/app/admin/connectors/web/page.tsx +++ b/web/src/app/admin/connectors/web/page.tsx @@ -18,6 +18,7 @@ import { HealthCheckBanner } from "@/components/health/healthcheck"; import { ConnectorIndexingStatus, WebConfig } from "@/lib/types"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; +import { createCredential, linkCredential } from "@/lib/credential"; const SCRAPE_TYPE_TO_PRETTY_NAME = { recursive: "Recursive", @@ -62,7 +63,11 @@ export default function Web() { nameBuilder={(values) => `WebConnector-${values.base_url}`} ccPairNameBuilder={(values) => values.base_url} - credentialId={0} // 0 is the ID of the default public credential + // Since there is no "real" credential associated with a web connector + // we create a dummy one here so that we can associate the CC Pair with a + // user. This is needed since the user for a CC Pair is found via the credential + // associated with it. + shouldCreateEmptyCredentialForConnector={true} source="web" inputType="load_state" formBody={ diff --git a/web/src/components/admin/connectors/ConnectorForm.tsx b/web/src/components/admin/connectors/ConnectorForm.tsx index f2706d45c..478ac5140 100644 --- a/web/src/components/admin/connectors/ConnectorForm.tsx +++ b/web/src/components/admin/connectors/ConnectorForm.tsx @@ -11,7 +11,7 @@ import { import { deleteConnectorIfExistsAndIsUnlinked } from "@/lib/connector"; import { FormBodyBuilder, RequireAtLeastOne } from "./types"; import { TextFormField } from "./Field"; -import { linkCredential } from "@/lib/credential"; +import { createCredential, linkCredential } from "@/lib/credential"; import { useSWRConfig } from "swr"; const BASE_CONNECTOR_URL = "/api/manage/admin/connector"; @@ -57,7 +57,8 @@ interface BaseProps { ccPairNameBuilder?: (values: T) => string | null; source: ValidSources; inputType: ValidInputTypes; - credentialId?: number; // if specified, will automatically try and link the credential + // if specified, will automatically try and link the credential + credentialId?: number; // If both are specified, will render formBody and then formBodyBuilder formBody?: JSX.Element | null; formBodyBuilder?: FormBodyBuilder; @@ -68,6 +69,9 @@ interface BaseProps { responseJson: Connector | undefined ) => void; refreshFreq?: number; + // If specified, then we will create an empty credential and associate + // the connector with it. If credentialId is specified, then this will be ignored + shouldCreateEmptyCredentialForConnector?: boolean; } type ConnectorFormProps = RequireAtLeastOne< @@ -87,6 +91,7 @@ export function ConnectorForm({ initialValues, refreshFreq, onSubmit, + shouldCreateEmptyCredentialForConnector, }: ConnectorFormProps): JSX.Element { const { mutate } = useSWRConfig(); const { popup, setPopup } = usePopup(); @@ -147,13 +152,35 @@ export function ConnectorForm({ return; } - if (credentialId !== undefined) { + let credentialIdToLinkTo = credentialId; + // create empty credential if specified + if ( + shouldCreateEmptyCredentialForConnector && + credentialIdToLinkTo === undefined + ) { + const createCredentialResponse = await createCredential({ + credential_json: {}, + admin_public: true, + }); + if (!createCredentialResponse.ok) { + const errorMsg = await createCredentialResponse.text(); + setPopup({ + message: `Error creating credential for CC Pair - ${errorMsg}`, + type: "error", + }); + formikHelpers.setSubmitting(false); + return; + } + credentialIdToLinkTo = (await createCredentialResponse.json()).id; + } + + if (credentialIdToLinkTo !== undefined) { const ccPairName = ccPairNameBuilder ? ccPairNameBuilder(values) : values.cc_pair_name; const linkCredentialResponse = await linkCredential( response.id, - credentialId, + credentialIdToLinkTo, ccPairName ); if (!linkCredentialResponse.ok) { diff --git a/web/src/components/admin/connectors/CredentialForm.tsx b/web/src/components/admin/connectors/CredentialForm.tsx index 92d04a29d..d809e8536 100644 --- a/web/src/components/admin/connectors/CredentialForm.tsx +++ b/web/src/components/admin/connectors/CredentialForm.tsx @@ -3,20 +3,14 @@ import { Formik, Form } from "formik"; import * as Yup from "yup"; import { Popup } from "./Popup"; import { CredentialBase } from "@/lib/types"; +import { createCredential } from "@/lib/credential"; export async function submitCredential( - connector: CredentialBase + credential: CredentialBase ): Promise<{ message: string; isSuccess: boolean }> { let isSuccess = false; try { - const response = await fetch(`/api/manage/credential`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(connector), - }); - + const response = await createCredential(credential); if (response.ok) { isSuccess = true; return { message: "Success!", isSuccess: true }; diff --git a/web/src/lib/credential.ts b/web/src/lib/credential.ts index 9213194ef..fdac483e4 100644 --- a/web/src/lib/credential.ts +++ b/web/src/lib/credential.ts @@ -1,3 +1,15 @@ +import { CredentialBase } from "./types"; + +export async function createCredential(credential: CredentialBase) { + return await fetch(`/api/manage/credential`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(credential), + }); +} + export async function adminDeleteCredential(credentialId: number) { return await fetch(`/api/manage/admin/credential/${credentialId}`, { method: "DELETE",