Skip to content

Commit

Permalink
work
Browse files Browse the repository at this point in the history
  • Loading branch information
ciyer committed Sep 19, 2024
1 parent 575908f commit 7af33c1
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
} from "../../projectsV2/api/projectV2.enhanced-api";
import type { DataConnectorRead } from "../../projectsV2/api/data-connectors.api";
import type { SessionStartCloudStorageConfiguration } from "../../sessionsV2/startSessionOptionsV2.types";
import CloudStorageSecretsModal from "../../sessionsV2/DataConnectorSecretsModal";
import DataConnectorSecretsModal from "../../sessionsV2/DataConnectorSecretsModal";

import useDataConnectorConfiguration from "./useDataConnectorConfiguration.hook";
import { Loader } from "../../../components/Loader";
Expand Down Expand Up @@ -188,7 +188,7 @@ export default function DataSourceCredentialsModal({
}

return (
<CloudStorageSecretsModal
<DataConnectorSecretsModal
dataConnectorConfigs={dataConnectorConfigs}
context="storage"
isOpen={isOpen}
Expand Down
179 changes: 107 additions & 72 deletions client/src/features/dataConnectorsV2/components/DataConnectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,6 @@ import { ArrowCounterclockwise } from "react-bootstrap-icons";
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap";

import { RtkOrNotebooksError } from "../../../components/errors/RtkErrorAlert";
import { usePostStoragesV2ByStorageIdSecretsMutation } from "../../projectsV2/api/projectV2.enhanced-api";
import {
CloudStorageGetV2Read,
CloudStoragePatch,
PostStoragesV2ApiArg,
RCloneConfig,
usePatchStoragesV2ByStorageIdMutation,
usePostStoragesV2Mutation,
} from "../../projectsV2/api/storagesV2.api";

import AddStorageBreadcrumbNavbar from "../../project/components/cloudStorage/AddStorageBreadcrumbNavbar";
import {
Expand Down Expand Up @@ -62,28 +53,63 @@ import {
} from "../../project/components/cloudStorage/cloudStorageModalComponents";
import {
findSensitive,
getCurrentStorageDetails,
getSchemaProviders,
hasProviderShortlist,
} from "../../project/utils/projectCloudStorage.utils";

import {
usePatchDataConnectorsByDataConnectorIdMutation,
usePostDataConnectorsMutation,
usePostStoragesV2ByStorageIdSecretsMutation,
} from "../../projectsV2/api/projectV2.enhanced-api";
import { CloudStoragePatch } from "../../projectsV2/api/storagesV2.api";
import type {
DataConnectorPatch,
DataConnectorPost,
DataConnectorRead,
} from "../../projectsV2/api/data-connectors.api";

import styles from "../../project/components/cloudStorage/CloudStorage.module.scss";

import DataConnectorModalBody from "./DataConnectorModalBody";

function dataConnectorStorageDetails(
dataConnector: DataConnectorRead | null
): CloudStorageDetails {
if (!dataConnector) {
return EMPTY_CLOUD_STORAGE_DETAILS;
}
const configurationOptions = dataConnector.storage.configuration
? dataConnector.storage.configuration
: {};
const { type, provider, ...options } = configurationOptions; // eslint-disable-line @typescript-eslint/no-unused-vars
const storageDetails: CloudStorageDetails = {
storageId: dataConnector.id,
schema: dataConnector.storage.configuration.type as string,
name: dataConnector.name,
mountPoint: dataConnector.storage.target_path,
sourcePath: dataConnector.storage.source_path,
readOnly: dataConnector.storage.readonly,
provider: dataConnector.storage.configuration.provider
? (dataConnector.storage.configuration.provider as string)
: undefined,
options,
};

return storageDetails;
}

interface DataConnectorModalProps {
currentStorage?: CloudStorageGetV2Read | null;
dataConnector?: DataConnectorRead | null;
isOpen: boolean;
toggle: () => void;
projectId: string;
}
export default function DataConnectorModal({
currentStorage = null,
dataConnector = null,
isOpen,
toggle: originalToggle,
projectId,
}: DataConnectorModalProps) {
const storageId = currentStorage?.storage.storage_id ?? null;
const dataConnectorId = dataConnector?.id ?? null;
// Fetch available schema when users open the modal
const {
data: schema,
Expand All @@ -94,11 +120,9 @@ export default function DataConnectorModal({
// Reset state on props change
useEffect(() => {
const cloudStorageDetails: CloudStorageDetails =
currentStorage != null
? getCurrentStorageDetails(currentStorage)
: EMPTY_CLOUD_STORAGE_DETAILS;
dataConnectorStorageDetails(dataConnector);
const cloudStorageState: AddCloudStorageState =
currentStorage != null
dataConnector != null
? {
...EMPTY_CLOUD_STORAGE_STATE,
step: 2,
Expand All @@ -107,7 +131,7 @@ export default function DataConnectorModal({
: EMPTY_CLOUD_STORAGE_STATE;
setStorageDetails(cloudStorageDetails);
setState(cloudStorageState);
}, [currentStorage]);
}, [dataConnector]);

const [success, setSuccess] = useState(false);
const [credentialSaveStatus, setCredentialSaveStatus] =
Expand Down Expand Up @@ -167,19 +191,18 @@ export default function DataConnectorModal({
}, [redraw]);

// Mutations
const [addCloudStorageForProjectV2, addResultV2] =
usePostStoragesV2Mutation();
const [modifyCloudStorageV2ForProject, modifyResultV2] =
usePatchStoragesV2ByStorageIdMutation();
const [createDataConnector, createResult] = usePostDataConnectorsMutation();
const [updateDataConnector, updateResult] =
usePatchDataConnectorsByDataConnectorIdMutation();
const [saveCredentials, saveCredentialsResult] =
usePostStoragesV2ByStorageIdSecretsMutation();
const [validateCloudStorageConnection, validationResult] =
useTestCloudStorageConnectionMutation();

const reset = useCallback(() => {
const resetStatus = getCurrentStorageDetails(currentStorage);
const resetStatus = dataConnectorStorageDetails(dataConnector);
setState((prevState) =>
currentStorage != null
dataConnector != null
? {
...EMPTY_CLOUD_STORAGE_STATE,
step: prevState.step,
Expand All @@ -189,14 +212,14 @@ export default function DataConnectorModal({
...EMPTY_CLOUD_STORAGE_STATE,
}
);
addResultV2.reset();
createResult.reset();
validationResult.reset();
setStorageDetails(resetStatus);
setSuccess(false);
setCredentialSaveStatus("none");
setValidationSucceeded(false);
setRedraw(true); // This forces re-loading the useForm fields
}, [addResultV2, currentStorage, validationResult]);
}, [createResult, dataConnector, validationResult]);

const setStorageDetailsSafe = useCallback(
(newStorageDetails: Partial<CloudStorageDetails>) => {
Expand Down Expand Up @@ -248,12 +271,13 @@ export default function DataConnectorModal({
}, [storageDetails, validateCloudStorageConnection]);

const addOrEditStorage = useCallback(() => {
// TODO Convert this to a data-connector-native structure
const storageParameters:
| AddCloudStorageForProjectParams
| CloudStoragePatch = {
name: storageDetails.name as string,
project_id: "",
readonly: storageDetails.readOnly ?? true,
project_id: `${projectId}`,
source_path: storageDetails.sourcePath ?? "/",
target_path: storageDetails.mountPoint as string,
configuration: { type: storageDetails.schema },
Expand All @@ -274,8 +298,8 @@ export default function DataConnectorModal({
const allOptions = storageDetails.options as CloudStorageDetailsOptions;
const sensitiveFields = schema
? findSensitive(schema.find((s) => s.prefix === storageDetails.schema))
: currentStorage?.sensitive_fields
? currentStorage.sensitive_fields.map((field) => field.name)
: dataConnector?.storage?.sensitive_fields
? dataConnector.storage.sensitive_fields.map((field) => field.name)
: [];
const validOptions = Object.keys(
storageDetails.options
Expand All @@ -296,41 +320,52 @@ export default function DataConnectorModal({
}

// We manually set success only when we get an ID back. That's just to show a success message
if (storageId) {
const cloudStoragePatch: CloudStoragePatch = {
project_id: projectId,
if (dataConnector && dataConnectorId) {
const dataConnectorPatch: DataConnectorPatch = {
name: storageParameters.name,
configuration: storageParameters.configuration as RCloneConfig,
source_path: storageParameters.source_path,
target_path: storageParameters.target_path,
readonly: storageParameters.readonly,
// TODO ad namespace, slug, visibility, description, keywords
storage: {
...storageParameters.configuration,
source_path: storageParameters.source_path,
target_path: storageParameters.target_path,
readonly: storageParameters.readonly,
},
};
modifyCloudStorageV2ForProject({
storageId: storageId,
cloudStoragePatch,
updateDataConnector({
dataConnectorId,
dataConnectorPatch,
"If-Match": dataConnector.etag,
}).then((result) => {
if ("data" in result && result.data.storage.storage_id) {
if ("data" in result && result.data.id) {
setSuccess(true);
}
});
} else {
const parameterV2 = {
body: storageParameters,
} as PostStoragesV2ApiArg;
addCloudStorageForProjectV2(parameterV2).then((result) => {
if ("data" in result && result.data.storage.storage_id) {
const dataConnectorPost = {
name: storageParameters.name,
// TODO ad namespace, slug, visibility, description, keywords
storage: {
...storageParameters.configuration,
source_path: storageParameters.source_path,
target_path: storageParameters.target_path,
readonly: storageParameters.readonly,
},
} as DataConnectorPost;
createDataConnector({
dataConnectorPost,
}).then((result) => {
if ("data" in result && result.data.id) {
setSuccess(true);
}
});
}
}, [
addCloudStorageForProjectV2,
currentStorage,
modifyCloudStorageV2ForProject,
projectId,
createDataConnector,
dataConnector,
dataConnectorId,
updateDataConnector,
schema,
storageDetails,
storageId,
]);

const toggle = useCallback(() => {
Expand All @@ -341,10 +376,10 @@ export default function DataConnectorModal({
setSuccess(false);
reset();
} else {
addResultV2.reset();
createResult.reset();
validationResult.reset();
}
}, [addResultV2, originalToggle, reset, success, validationResult]);
}, [createResult, originalToggle, reset, success, validationResult]);

// Handle unmount
useEffect(() => {
Expand All @@ -361,8 +396,8 @@ export default function DataConnectorModal({
);

useEffect(() => {
const storageId = addResultV2.data?.storage?.storage_id;
if (storageId == null) return;
const dataConnectorId = createResult.data?.id;
if (dataConnectorId == null) return;
const shouldSaveCredentials = shouldSaveDataConnectorCredentials(
storageDetails.options,
state.saveCredentials,
Expand All @@ -387,11 +422,11 @@ export default function DataConnectorModal({
value: "" + secret.value,
}));
saveCredentials({
storageId,
storageId: dataConnectorId,
cloudStorageSecretPostList,
});
}, [
addResultV2.data?.storage?.storage_id,
createResult.data?.id,
saveCredentials,
state.saveCredentials,
schema,
Expand All @@ -406,7 +441,7 @@ export default function DataConnectorModal({
return;
}
if (
addResultV2.data?.storage?.storage_id == null ||
createResult.data?.id == null ||
saveCredentialsResult.isUninitialized
) {
setCredentialSaveStatus("none");
Expand All @@ -425,19 +460,19 @@ export default function DataConnectorModal({
return;
}
setCredentialSaveStatus("none");
}, [addResultV2, saveCredentialsResult, validationSucceeded]);
}, [createResult, saveCredentialsResult, validationSucceeded]);

// Visual elements
const disableContinueButton =
state.step === 1 &&
(!storageDetails.schema ||
(schemaRequiresProvider && !storageDetails.provider));

const isAddResultLoading = addResultV2.isLoading;
const isModifyResultLoading = modifyResultV2.isLoading;
const addResultError = addResultV2.error;
const modifyResultError = modifyResultV2.error;
const addResultStorageName = addResultV2?.data?.storage?.name;
const isAddResultLoading = createResult.isLoading;
const isModifyResultLoading = updateResult.isLoading;
const addResultError = createResult.error;
const modifyResultError = updateResult.error;
const addResultStorageName = createResult?.data?.name;

const disableAddButton =
isAddResultLoading ||
Expand All @@ -448,7 +483,7 @@ export default function DataConnectorModal({
(hasProviderShortlist(storageDetails.schema) && !storageDetails.provider);
const addButtonDisableReason = isAddResultLoading
? "Please wait, the storage is being added"
: modifyResultV2.isLoading
: updateResult.isLoading
? "Please wait, the storage is being modified"
: !storageDetails.name
? "Please provide a name"
Expand All @@ -460,8 +495,8 @@ export default function DataConnectorModal({
const isResultLoading = isAddResultLoading || isModifyResultLoading;

const storageSecrets =
currentStorage != null && "secrets" in currentStorage
? currentStorage.secrets ?? []
dataConnector != null && "secrets" in dataConnector
? dataConnector.secrets ?? []
: [];
const hasStoredCredentialsInConfig = storageSecrets.length > 0;

Expand All @@ -472,15 +507,15 @@ export default function DataConnectorModal({
className={styles.modal}
data-cy="cloud-storage-edit-modal"
fullscreen="lg"
id={currentStorage?.storage.storage_id ?? "new-cloud-storage"}
id={dataConnector?.id ?? "new-cloud-storage"}
isOpen={isOpen}
scrollable
size="lg"
unmountOnClose={false}
toggle={toggle}
>
<ModalHeader toggle={toggle} data-cy="cloud-storage-edit-header">
<AddCloudStorageHeaderContent isV2={true} storageId={storageId} />
<AddCloudStorageHeaderContent isV2={true} storageId={dataConnectorId} />
</ModalHeader>

<ModalBody data-cy="cloud-storage-edit-body">
Expand All @@ -497,7 +532,7 @@ export default function DataConnectorModal({
state={state}
storageDetails={storageDetails}
storageSecrets={storageSecrets}
storageId={storageId}
storageId={dataConnectorId}
success={success}
validationSucceeded={validationSucceeded}
/>
Expand Down Expand Up @@ -549,7 +584,7 @@ export default function DataConnectorModal({
setValidationSucceeded={setValidationSucceeded}
state={state}
storageDetails={storageDetails}
storageId={storageId}
storageId={dataConnectorId}
validateConnection={validateConnection}
validationResult={validationResult}
/>
Expand Down

0 comments on commit 7af33c1

Please sign in to comment.