Skip to content

Commit

Permalink
feat!: support for data connectors on user and group namespaces (#3315)
Browse files Browse the repository at this point in the history
  • Loading branch information
ciyer authored Sep 30, 2024
1 parent 765e524 commit f325fdd
Show file tree
Hide file tree
Showing 47 changed files with 5,739 additions and 119 deletions.
3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"storybook-wait-server": "wait-on http://127.0.0.1:6006",
"storybook-test": "test-storybook",
"storybook-compile-and-test": "concurrently -k -s first -n 'BUILD,TEST' -c 'magenta,blue' 'npm run storybook-build && npm run storybook-start-server' 'npm run storybook-wait-server && npm run storybook-test'",
"generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:platform && npm run generate-api:searchV2 && npm run generate-api:storages",
"generate-api": "npm run generate-api:data-connectors && npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:platform && npm run generate-api:searchV2 && npm run generate-api:storages",
"generate-api:data-connectors": "rtk-query-codegen-openapi src/features/projectsV2/api/data-connectors.api-config.ts",
"generate-api:dataServicesUser": "rtk-query-codegen-openapi src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts",
"generate-api:namespaceV2": "rtk-query-codegen-openapi src/features/projectsV2/api/namespace.api-config.ts",
"generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts",
Expand Down
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 { CloudStorageGetRead } from "../../../projectsV2/api/storagesV2.api";
import type { SessionStartCloudStorageConfiguration } from "../../../sessionsV2/startSessionOptionsV2.types";
import CloudStorageSecretsModal from "../../../sessionsV2/DataConnectorSecretsModal";
import DataStorageSecretsModal from "../../../sessionsV2/DataStorageSecretsModal";

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

return (
<CloudStorageSecretsModal
<DataStorageSecretsModal
cloudStorageConfigs={cloudStorageConfigs}
context="storage"
isOpen={isOpen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
} from "reactstrap";

import { Loader } from "../../../../components/Loader";
import DataConnectorModal from "../../../dataConnectorsV2/components/DataConnectorModal";
import DataSourceModal from "./DataSourceModal";
import {
type CloudStorageGetRead,
useDeleteStoragesV2ByStorageIdMutation,
Expand Down Expand Up @@ -174,7 +174,7 @@ export function DataSourceActions({
isOpen={isDeleteOpen}
toggleModal={toggleDelete}
/>
<DataConnectorModal
<DataSourceModal
currentStorage={storage}
isOpen={isEditOpen}
toggle={toggleEdit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,53 +23,53 @@ import { useCallback, useEffect, useMemo, useState } from "react";
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 { 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";
} from "../../../projectsV2/api/storagesV2.api";

import AddStorageBreadcrumbNavbar from "../../project/components/cloudStorage/AddStorageBreadcrumbNavbar";
import AddStorageBreadcrumbNavbar from "../../../project/components/cloudStorage/AddStorageBreadcrumbNavbar";
import {
useGetCloudStorageSchemaQuery,
useTestCloudStorageConnectionMutation,
} from "../../project/components/cloudStorage/projectCloudStorage.api";
} from "../../../project/components/cloudStorage/projectCloudStorage.api";
import {
CLOUD_STORAGE_SENSITIVE_FIELD_TOKEN,
CLOUD_STORAGE_TOTAL_STEPS,
EMPTY_CLOUD_STORAGE_DETAILS,
EMPTY_CLOUD_STORAGE_STATE,
} from "../../project/components/cloudStorage/projectCloudStorage.constants";
} from "../../../project/components/cloudStorage/projectCloudStorage.constants";
import {
AddCloudStorageForProjectParams,
AddCloudStorageState,
CloudStorageDetails,
CloudStorageDetailsOptions,
CredentialSaveStatus,
TestCloudStorageConnectionParams,
} from "../../project/components/cloudStorage/projectCloudStorage.types";
} from "../../../project/components/cloudStorage/projectCloudStorage.types";

import {
AddCloudStorageContinueButton,
AddCloudStorageBackButton,
AddCloudStorageConnectionTestResult,
AddCloudStorageHeaderContent,
} from "../../project/components/cloudStorage/cloudStorageModalComponents";
} from "../../../project/components/cloudStorage/cloudStorageModalComponents";
import {
findSensitive,
getCurrentStorageDetails,
getSchemaProviders,
hasProviderShortlist,
} from "../../project/utils/projectCloudStorage.utils";
} from "../../../project/utils/projectCloudStorage.utils";

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

import DataConnectorModalBody from "./DataConnectorModalBody";
import DataSourceModalBody from "./DataSourceModalBody";

interface DataConnectorModalProps {
currentStorage?: CloudStorageGetV2Read | null;
Expand Down Expand Up @@ -484,7 +484,7 @@ export default function DataConnectorModal({
</ModalHeader>

<ModalBody data-cy="cloud-storage-edit-body">
<DataConnectorModalBody
<DataSourceModalBody
addResultStorageName={addResultStorageName}
credentialSaveStatus={credentialSaveStatus}
isV2={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@

import cx from "classnames";

import { Loader } from "../../../components/Loader";
import { RtkOrNotebooksError } from "../../../components/errors/RtkErrorAlert";
import { Loader } from "../../../../components/Loader";
import { RtkOrNotebooksError } from "../../../../components/errors/RtkErrorAlert";

import { CLOUD_STORAGE_TOTAL_STEPS } from "../../project/components/cloudStorage/projectCloudStorage.constants";
import { CLOUD_STORAGE_TOTAL_STEPS } from "../../../project/components/cloudStorage/projectCloudStorage.constants";
import type {
AddCloudStorageState,
CloudStorageDetails,
CloudStorageSchema,
} from "../../project/components/cloudStorage/projectCloudStorage.types";
} from "../../../project/components/cloudStorage/projectCloudStorage.types";
import {
AddCloudStorageSuccessAlert,
type AddCloudStorageBodyContentProps,
} from "../../project/components/cloudStorage/cloudStorageModalComponents";
} from "../../../project/components/cloudStorage/cloudStorageModalComponents";
import {
AddStorageAdvanced,
AddStorageAdvancedToggle,
AddStorageMount,
AddStorageOptions,
AddStorageType,
} from "../../project/components/cloudStorage/AddOrEditCloudStorage";
import type { CloudStorageSecretGet } from "../../projectsV2/api/storagesV2.api";
} from "../../../project/components/cloudStorage/AddOrEditCloudStorage";
import type { CloudStorageSecretGet } from "../../../projectsV2/api/storagesV2.api";

interface AddOrEditCloudStorageProps {
schema: CloudStorageSchema[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import cx from "classnames";
import { useCallback, useState } from "react";
import { Database, PlusLg } from "react-bootstrap-icons";
import { Loader } from "../../../../components/Loader";
import DataConnectorModal from "../../../dataConnectorsV2/components/DataConnectorModal";
import { Project } from "../../../projectsV2/api/projectV2.api";
import { useGetStoragesV2Query } from "../../../projectsV2/api/storagesV2.api";
import AccessGuard from "../../utils/AccessGuard";
import useProjectAccess from "../../utils/useProjectAccess.hook";
import { DataSourceDisplay } from "./DataSourceDisplay";
import DataSourceModal from "./DataSourceModal";
import {
Badge,
Button,
Expand Down Expand Up @@ -117,7 +117,7 @@ export function DataSourcesDisplay({ project }: { project: Project }) {
</ListGroup>
)}
</CardBody>
<DataConnectorModal
<DataSourceModal
currentStorage={null}
isOpen={isOpen}
toggle={toggle}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*!
* Copyright 2024 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import cx from "classnames";
import { useCallback, useEffect, useState } from "react";
import { Lock, Pencil, Trash, XLg } from "react-bootstrap-icons";
import {
Button,
Col,
DropdownItem,
Input,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
Row,
} from "reactstrap";

import { Loader } from "../../../components/Loader";
import DataConnectorModal from "./DataConnectorModal";
import type { DataConnectorRead } from "../../projectsV2/api/data-connectors.api";
import { useDeleteDataConnectorsByDataConnectorIdMutation } from "../../projectsV2/api/data-connectors.enhanced-api";
import DataConnectorCredentialsModal from "./DataConnectorCredentialsModal";
import { ButtonWithMenuV2 } from "../../../components/buttons/Button";

interface DataConnectorDeleteModalProps {
dataConnector: DataConnectorRead;
isOpen: boolean;
onDelete: () => void;
toggleModal: () => void;
}
function DataConnectorDeleteModal({
dataConnector,
onDelete,
toggleModal,
isOpen,
}: DataConnectorDeleteModalProps) {
const [deleteDataConnector, { isLoading, isSuccess }] =
useDeleteDataConnectorsByDataConnectorIdMutation();

const [typedName, setTypedName] = useState("");
const onChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setTypedName(e.target.value.trim());
},
[setTypedName]
);

useEffect(() => {
if (isSuccess) {
onDelete();
}
}, [isSuccess, onDelete]);
const onDeleteDataCollector = () => {
deleteDataConnector({
dataConnectorId: dataConnector.id,
});
};

return (
<Modal size="lg" isOpen={isOpen} toggle={toggleModal} centered>
<ModalHeader className="text-danger" toggle={toggleModal}>
Delete data connector
</ModalHeader>
<ModalBody>
<Row>
<Col>
<p>
Are you sure you want to delete this data connector? It will
affect all projects that use it. Please type{" "}
<strong>{dataConnector.slug}</strong>, the slug of the data
connector, to confirm.
</p>
<Input
data-cy="delete-confirmation-input"
value={typedName}
onChange={onChange}
/>
</Col>
</Row>
</ModalBody>
<ModalFooter>
<div className="d-flex justify-content-end">
<Button color="outline-danger" onClick={toggleModal}>
<XLg className={cx("bi", "me-1")} />
Cancel
</Button>
<Button
color="danger"
className={cx("float-right", "ms-2")}
disabled={typedName !== dataConnector.slug.trim()}
data-cy="delete-data-connector-modal-button"
type="submit"
onClick={onDeleteDataCollector}
>
{isLoading ? (
<>
<Loader className="me-1" inline size={16} />
Deleting data connector
</>
) : (
<>
<Trash className={cx("bi", "me-1")} />
Remove data connector
</>
)}
</Button>
</div>
</ModalFooter>
</Modal>
);
}
export default function DataConnectorActions({
dataConnector,
toggleView,
}: {
dataConnector: DataConnectorRead;
toggleView: () => void;
}) {
const [isCredentialsOpen, setCredentialsOpen] = useState(false);
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const [isEditOpen, setIsEditOpen] = useState(false);
const onDelete = useCallback(() => {
setIsDeleteOpen(false);
toggleView();
}, [toggleView]);
const toggleCredentials = useCallback(() => {
setCredentialsOpen((open) => !open);
}, []);
const toggleDelete = useCallback(() => {
setIsDeleteOpen((open) => !open);
}, []);
const toggleEdit = useCallback(() => {
setIsEditOpen((open) => !open);
}, []);

const defaultAction = (
<Button
className="text-nowrap"
color="outline-primary"
data-cy="data-connector-edit"
onClick={toggleEdit}
size="sm"
>
<Pencil className={cx("bi", "me-1")} />
Edit
</Button>
);

return (
<>
<ButtonWithMenuV2
color="outline-primary"
default={defaultAction}
preventPropagation
size="sm"
>
<DropdownItem
data-cy="data-connector-credentials"
onClick={toggleCredentials}
>
<Lock className={cx("bi", "me-1")} />
Credentials
</DropdownItem>
<DropdownItem data-cy="data-connector-delete" onClick={toggleDelete}>
<Trash className={cx("bi", "me-1")} />
Remove
</DropdownItem>
</ButtonWithMenuV2>
<DataConnectorCredentialsModal
dataConnector={dataConnector}
setOpen={setCredentialsOpen}
isOpen={isCredentialsOpen}
/>
<DataConnectorDeleteModal
dataConnector={dataConnector}
isOpen={isDeleteOpen}
onDelete={onDelete}
toggleModal={toggleDelete}
/>
<DataConnectorModal
dataConnector={dataConnector}
isOpen={isEditOpen}
namespace={dataConnector.namespace}
toggle={toggleEdit}
/>
</>
);
}
Loading

0 comments on commit f325fdd

Please sign in to comment.