diff --git a/client/.eslintrc.json b/client/.eslintrc.json
index a2a9b3488b..63589f5eea 100644
--- a/client/.eslintrc.json
+++ b/client/.eslintrc.json
@@ -212,6 +212,7 @@
"onloadend",
"onopen",
"openapi",
+ "openbis",
"papermill",
"pathname",
"pdfjs",
diff --git a/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalBody.tsx b/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalBody.tsx
index c2d877e42b..758f2bfacd 100644
--- a/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalBody.tsx
+++ b/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalBody.tsx
@@ -32,8 +32,8 @@ import {
import KeywordBadge from "~/components/keywords/KeywordBadge";
import KeywordContainer from "~/components/keywords/KeywordContainer";
import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon";
-import { WarnAlert } from "../../../../components/Alert";
import { Loader } from "../../../../components/Loader";
+import { InfoAlert, WarnAlert } from "../../../../components/Alert";
import useAppDispatch from "../../../../utils/customHooks/useAppDispatch.hook";
import useAppSelector from "../../../../utils/customHooks/useAppSelector.hook";
import { slugFromTitle } from "../../../../utils/helpers/HelperFunctions";
@@ -49,7 +49,10 @@ import type {
AddCloudStorageState,
CloudStorageDetails,
} from "../../../project/components/cloudStorage/projectCloudStorage.types";
-import { getSchemaOptions } from "../../../project/utils/projectCloudStorage.utils";
+import {
+ getSchema,
+ getSchemaOptions,
+} from "../../../project/utils/projectCloudStorage.utils";
import type { Project } from "../../../projectsV2/api/projectV2.api";
import { ProjectNamespaceControl } from "../../../projectsV2/fields/ProjectNamespaceFormField";
import SlugPreviewFormField from "../../../projectsV2/fields/SlugPreviewFormField";
@@ -294,16 +297,19 @@ export function DataConnectorMount({
const { validationResult } = useAppSelector(
(state) => state.dataConnectorFormSlice
);
- const options = getSchemaOptions(
+ const schema = getSchema(schemata, flatDataConnector.schema);
+ const schemaOptions = getSchemaOptions(
schemata,
true,
flatDataConnector.schema,
flatDataConnector.provider
);
const secretFields =
- options == null
+ schemaOptions == null
? []
- : Object.values(options).filter((o) => o && o.convertedType === "secret");
+ : Object.values(schemaOptions).filter(
+ (o) => o && o.convertedType === "secret"
+ );
const hasPasswordFieldWithInput = secretFields.some(
(o) => flatDataConnector.options && flatDataConnector.options[o.name]
);
@@ -514,6 +520,7 @@ export function DataConnectorMount({
field.onChange(e);
onFieldValueChange("readOnly", !!e.target.value);
}}
+ disabled={schema?.forceReadOnly ?? false}
/>
- {!flatDataConnector.readOnly && (
+ {schema?.forceReadOnly ? (
-
+
- You are mounting this storage in read-write mode. If you have
- read-only access, please select "Read Only" to
- prevent errors with some storage types.
+ This cloud storage only supports read-only access.
-
+
+ ) : (
+ !flatDataConnector.readOnly && (
+
+
+
+ You are mounting this storage in read-write mode. If you
+ have read-only access, please select "Read Only"
+ to prevent errors with some storage types.
+
+
+
+ )
)}
Select "Read Only" to mount the storage without write
diff --git a/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalFooter.tsx b/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalFooter.tsx
index 0ad57d3d75..ba789e3419 100644
--- a/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalFooter.tsx
+++ b/client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalFooter.tsx
@@ -34,6 +34,7 @@ import {
import {
findSensitive,
+ getSchemaOptions,
hasProviderShortlist,
} from "../../../project/utils/projectCloudStorage.utils";
@@ -307,10 +308,32 @@ function DataConnectorCreateFooter({
]);
// Visual elements
- const disableContinueButton =
- cloudStorageState.step === 1 &&
- (!flatDataConnector.schema ||
- (schemaRequiresProvider && !flatDataConnector.provider));
+ const disableContinueButton = useMemo(() => {
+ return (
+ (cloudStorageState.step === 1 &&
+ (!flatDataConnector.schema ||
+ (schemaRequiresProvider && !flatDataConnector.provider))) ||
+ (cloudStorageState.step === 2 &&
+ !getSchemaOptions(
+ schemata,
+ true,
+ flatDataConnector.schema,
+ flatDataConnector.provider
+ )?.every((o) => {
+ return (
+ !o.required ||
+ (flatDataConnector.options && flatDataConnector.options[o.name])
+ );
+ }))
+ );
+ }, [
+ cloudStorageState.step,
+ flatDataConnector.schema,
+ schemaRequiresProvider,
+ flatDataConnector.provider,
+ schemata,
+ flatDataConnector.options,
+ ]);
const isAddResultLoading = createResult.isLoading;
const actionError = createResult.error;
@@ -490,10 +513,32 @@ function DataConnectorEditFooter({
}, [dispatch, updateResult]);
// Visual elements
- const disableContinueButton =
- cloudStorageState.step === 1 &&
- (!flatDataConnector.schema ||
- (schemaRequiresProvider && !flatDataConnector.provider));
+ const disableContinueButton = useMemo(() => {
+ return (
+ (cloudStorageState.step === 1 &&
+ (!flatDataConnector.schema ||
+ (schemaRequiresProvider && !flatDataConnector.provider))) ||
+ (cloudStorageState.step === 2 &&
+ !getSchemaOptions(
+ schemata,
+ true,
+ flatDataConnector.schema,
+ flatDataConnector.provider
+ )?.every((o) => {
+ return (
+ !o.required ||
+ (flatDataConnector.options && flatDataConnector.options[o.name])
+ );
+ }))
+ );
+ }, [
+ cloudStorageState.step,
+ flatDataConnector.schema,
+ schemaRequiresProvider,
+ flatDataConnector.provider,
+ schemata,
+ flatDataConnector.options,
+ ]);
const isModifyResultLoading = updateResult.isLoading;
const actionError = updateResult.error;
diff --git a/client/src/features/dataConnectorsV2/components/DataConnectorModal/dataConnectorModalButtons.tsx b/client/src/features/dataConnectorsV2/components/DataConnectorModal/dataConnectorModalButtons.tsx
index 6e26050e6e..8f0eca59ce 100644
--- a/client/src/features/dataConnectorsV2/components/DataConnectorModal/dataConnectorModalButtons.tsx
+++ b/client/src/features/dataConnectorsV2/components/DataConnectorModal/dataConnectorModalButtons.tsx
@@ -176,6 +176,7 @@ export function DataConnectorModalContinueButton({
continueId="add-data-connector-continue"
step={cloudStorageState.step}
testId="test-data-connector"
+ disableContinueButton={disableContinueButton}
editDataConnector={addOrEditStorage}
/>
{disableContinueButton && (
@@ -183,11 +184,7 @@ export function DataConnectorModalContinueButton({
placement="top"
target={`${continueButtonId}-div`}
>
- {!flatDataConnector.schema
- ? "Please select a storage type"
- : selectedSchemaHasAccessMode
- ? "Please select a mode or change storage type"
- : "Please select a provider or change storage type"}
+ Please fill out all fields labeled as required
)}
@@ -211,10 +208,19 @@ export function DataConnectorModalContinueButton({
id="add-data-connector-continue-button"
data-cy="add-data-connector-continue-button"
className={cx("btn-primary")}
+ disabled={disableContinueButton}
onClick={addOrEditStorage}
>
Update connector
+ {disableContinueButton && (
+
+ Please fill out all fields labeled as required
+
+ )}
);
@@ -294,12 +300,14 @@ interface TestConnectionAndContinueButtonsProps
continueId: string;
step: number;
testId: string;
+ disableContinueButton: boolean;
editDataConnector?: () => void;
}
function TestConnectionAndContinueButtons({
continueId,
step,
testId,
+ disableContinueButton,
editDataConnector,
}: TestConnectionAndContinueButtonsProps) {
const dispatch = useAppDispatch();
@@ -395,7 +403,7 @@ function TestConnectionAndContinueButtons({
color={testConnectionColor}
id={buttonTestId}
data-cy={buttonTestId}
- disabled={validationResult.isLoading}
+ disabled={disableContinueButton || validationResult.isLoading}
onClick={() => validateConnection()}
>
{testConnectionContent}
diff --git a/client/src/features/project/components/cloudStorage/AddOrEditCloudStorage.tsx b/client/src/features/project/components/cloudStorage/AddOrEditCloudStorage.tsx
index 822221ab8f..1af2a51e14 100644
--- a/client/src/features/project/components/cloudStorage/AddOrEditCloudStorage.tsx
+++ b/client/src/features/project/components/cloudStorage/AddOrEditCloudStorage.tsx
@@ -34,6 +34,7 @@ import {
} from "react-bootstrap-icons";
import { Control, Controller, FieldValues, useForm } from "react-hook-form";
import {
+ Badge,
Button,
Input,
InputGroup,
@@ -43,13 +44,14 @@ import {
UncontrolledTooltip,
} from "reactstrap";
-import { WarnAlert } from "../../../../components/Alert";
+import { InfoAlert, WarnAlert } from "../../../../components/Alert";
import { ExternalLink } from "../../../../components/ExternalLinks";
import useAppSelector from "../../../../utils/customHooks/useAppSelector.hook";
import type { DataConnectorSecret } from "../../../dataConnectorsV2/api/data-connectors.api";
import { hasSchemaAccessMode } from "../../../dataConnectorsV2/components/dataConnector.utils";
import {
convertFromAdvancedConfig,
+ getSchema,
getSchemaOptions,
getSchemaProviders,
getSchemaStorage,
@@ -192,8 +194,8 @@ interface AddStorageAdvancedForm {
configuration: string;
}
export function AddStorageAdvanced({
- storage,
setStorage,
+ storage,
}: AddStorageStepProps) {
const {
control,
@@ -231,6 +233,9 @@ export function AddStorageAdvanced({
return (