Associate a user with web/file connectors

This commit is contained in:
Weves 2023-11-29 18:10:56 -08:00 committed by Chris Weaver
parent fda89ac810
commit d1846823ba
5 changed files with 73 additions and 16 deletions

View File

@ -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) {

View File

@ -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() {
<ConnectorForm<WebConfig>
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={

View File

@ -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<T extends Yup.AnyObject> {
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<T>;
@ -68,6 +69,9 @@ interface BaseProps<T extends Yup.AnyObject> {
responseJson: Connector<T> | 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<T extends Yup.AnyObject> = RequireAtLeastOne<
@ -87,6 +91,7 @@ export function ConnectorForm<T extends Yup.AnyObject>({
initialValues,
refreshFreq,
onSubmit,
shouldCreateEmptyCredentialForConnector,
}: ConnectorFormProps<T>): JSX.Element {
const { mutate } = useSWRConfig();
const { popup, setPopup } = usePopup();
@ -147,13 +152,35 @@ export function ConnectorForm<T extends Yup.AnyObject>({
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) {

View File

@ -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<T>(
connector: CredentialBase<T>
credential: CredentialBase<T>
): 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 };

View File

@ -1,3 +1,15 @@
import { CredentialBase } from "./types";
export async function createCredential(credential: CredentialBase<any>) {
return await fetch(`/api/manage/credential`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credential),
});
}
export async function adminDeleteCredential<T>(credentialId: number) {
return await fetch(`/api/manage/admin/credential/${credentialId}`, {
method: "DELETE",