Skip to content

Commit 2daa452

Browse files
feat!: add a convenient way to add an openBIS cloud storage (#3238)
BREAKING CHANGE: requires renku-data-services >= v0.60.0 --------- Co-authored-by: Lorenzo Cavazzi <[email protected]>
1 parent 6545867 commit 2daa452

File tree

15 files changed

+364
-68
lines changed

15 files changed

+364
-68
lines changed

client/.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212
"onloadend",
213213
"onopen",
214214
"openapi",
215+
"openbis",
215216
"papermill",
216217
"pathname",
217218
"pdfjs",

client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalBody.tsx

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import {
3232
import KeywordBadge from "~/components/keywords/KeywordBadge";
3333
import KeywordContainer from "~/components/keywords/KeywordContainer";
3434
import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon";
35-
import { WarnAlert } from "../../../../components/Alert";
3635
import { Loader } from "../../../../components/Loader";
36+
import { InfoAlert, WarnAlert } from "../../../../components/Alert";
3737
import useAppDispatch from "../../../../utils/customHooks/useAppDispatch.hook";
3838
import useAppSelector from "../../../../utils/customHooks/useAppSelector.hook";
3939
import { slugFromTitle } from "../../../../utils/helpers/HelperFunctions";
@@ -49,7 +49,10 @@ import type {
4949
AddCloudStorageState,
5050
CloudStorageDetails,
5151
} from "../../../project/components/cloudStorage/projectCloudStorage.types";
52-
import { getSchemaOptions } from "../../../project/utils/projectCloudStorage.utils";
52+
import {
53+
getSchema,
54+
getSchemaOptions,
55+
} from "../../../project/utils/projectCloudStorage.utils";
5356
import type { Project } from "../../../projectsV2/api/projectV2.api";
5457
import { ProjectNamespaceControl } from "../../../projectsV2/fields/ProjectNamespaceFormField";
5558
import SlugPreviewFormField from "../../../projectsV2/fields/SlugPreviewFormField";
@@ -294,16 +297,19 @@ export function DataConnectorMount({
294297
const { validationResult } = useAppSelector(
295298
(state) => state.dataConnectorFormSlice
296299
);
297-
const options = getSchemaOptions(
300+
const schema = getSchema(schemata, flatDataConnector.schema);
301+
const schemaOptions = getSchemaOptions(
298302
schemata,
299303
true,
300304
flatDataConnector.schema,
301305
flatDataConnector.provider
302306
);
303307
const secretFields =
304-
options == null
308+
schemaOptions == null
305309
? []
306-
: Object.values(options).filter((o) => o && o.convertedType === "secret");
310+
: Object.values(schemaOptions).filter(
311+
(o) => o && o.convertedType === "secret"
312+
);
307313
const hasPasswordFieldWithInput = secretFields.some(
308314
(o) => flatDataConnector.options && flatDataConnector.options[o.name]
309315
);
@@ -514,6 +520,7 @@ export function DataConnectorMount({
514520
field.onChange(e);
515521
onFieldValueChange("readOnly", !!e.target.value);
516522
}}
523+
disabled={schema?.forceReadOnly ?? false}
517524
/>
518525
<Label
519526
for="data-connector-readonly-true"
@@ -535,6 +542,7 @@ export function DataConnectorMount({
535542
field.onChange(e);
536543
onFieldValueChange("readOnly", false);
537544
}}
545+
disabled={schema?.forceReadOnly ?? false}
538546
/>
539547
<Label
540548
for="data-connector-readonly-false"
@@ -548,16 +556,26 @@ export function DataConnectorMount({
548556
)}
549557
rules={{ required: true }}
550558
/>
551-
{!flatDataConnector.readOnly && (
559+
{schema?.forceReadOnly ? (
552560
<div className="mt-1">
553-
<WarnAlert dismissible={false}>
561+
<InfoAlert dismissible={false} timeout={0}>
554562
<p className="mb-0">
555-
You are mounting this storage in read-write mode. If you have
556-
read-only access, please select &quot;Read Only&quot; to
557-
prevent errors with some storage types.
563+
This cloud storage only supports read-only access.
558564
</p>
559-
</WarnAlert>
565+
</InfoAlert>
560566
</div>
567+
) : (
568+
!flatDataConnector.readOnly && (
569+
<div className="mt-1">
570+
<WarnAlert dismissible={false}>
571+
<p className="mb-0">
572+
You are mounting this storage in read-write mode. If you
573+
have read-only access, please select &quot;Read Only&quot;
574+
to prevent errors with some storage types.
575+
</p>
576+
</WarnAlert>
577+
</div>
578+
)
561579
)}
562580
<div className={cx("form-text", "text-muted")}>
563581
Select &quot;Read Only&quot; to mount the storage without write

client/src/features/dataConnectorsV2/components/DataConnectorModal/DataConnectorModalFooter.tsx

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434

3535
import {
3636
findSensitive,
37+
getSchemaOptions,
3738
hasProviderShortlist,
3839
} from "../../../project/utils/projectCloudStorage.utils";
3940

@@ -307,10 +308,32 @@ function DataConnectorCreateFooter({
307308
]);
308309

309310
// Visual elements
310-
const disableContinueButton =
311-
cloudStorageState.step === 1 &&
312-
(!flatDataConnector.schema ||
313-
(schemaRequiresProvider && !flatDataConnector.provider));
311+
const disableContinueButton = useMemo(() => {
312+
return (
313+
(cloudStorageState.step === 1 &&
314+
(!flatDataConnector.schema ||
315+
(schemaRequiresProvider && !flatDataConnector.provider))) ||
316+
(cloudStorageState.step === 2 &&
317+
!getSchemaOptions(
318+
schemata,
319+
true,
320+
flatDataConnector.schema,
321+
flatDataConnector.provider
322+
)?.every((o) => {
323+
return (
324+
!o.required ||
325+
(flatDataConnector.options && flatDataConnector.options[o.name])
326+
);
327+
}))
328+
);
329+
}, [
330+
cloudStorageState.step,
331+
flatDataConnector.schema,
332+
schemaRequiresProvider,
333+
flatDataConnector.provider,
334+
schemata,
335+
flatDataConnector.options,
336+
]);
314337

315338
const isAddResultLoading = createResult.isLoading;
316339
const actionError = createResult.error;
@@ -490,10 +513,32 @@ function DataConnectorEditFooter({
490513
}, [dispatch, updateResult]);
491514

492515
// Visual elements
493-
const disableContinueButton =
494-
cloudStorageState.step === 1 &&
495-
(!flatDataConnector.schema ||
496-
(schemaRequiresProvider && !flatDataConnector.provider));
516+
const disableContinueButton = useMemo(() => {
517+
return (
518+
(cloudStorageState.step === 1 &&
519+
(!flatDataConnector.schema ||
520+
(schemaRequiresProvider && !flatDataConnector.provider))) ||
521+
(cloudStorageState.step === 2 &&
522+
!getSchemaOptions(
523+
schemata,
524+
true,
525+
flatDataConnector.schema,
526+
flatDataConnector.provider
527+
)?.every((o) => {
528+
return (
529+
!o.required ||
530+
(flatDataConnector.options && flatDataConnector.options[o.name])
531+
);
532+
}))
533+
);
534+
}, [
535+
cloudStorageState.step,
536+
flatDataConnector.schema,
537+
schemaRequiresProvider,
538+
flatDataConnector.provider,
539+
schemata,
540+
flatDataConnector.options,
541+
]);
497542

498543
const isModifyResultLoading = updateResult.isLoading;
499544
const actionError = updateResult.error;

client/src/features/dataConnectorsV2/components/DataConnectorModal/dataConnectorModalButtons.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,15 @@ export function DataConnectorModalContinueButton({
176176
continueId="add-data-connector-continue"
177177
step={cloudStorageState.step}
178178
testId="test-data-connector"
179+
disableContinueButton={disableContinueButton}
179180
editDataConnector={addOrEditStorage}
180181
/>
181182
{disableContinueButton && (
182183
<UncontrolledTooltip
183184
placement="top"
184185
target={`${continueButtonId}-div`}
185186
>
186-
{!flatDataConnector.schema
187-
? "Please select a storage type"
188-
: selectedSchemaHasAccessMode
189-
? "Please select a mode or change storage type"
190-
: "Please select a provider or change storage type"}
187+
Please fill out all fields labeled as required
191188
</UncontrolledTooltip>
192189
)}
193190
</div>
@@ -211,10 +208,19 @@ export function DataConnectorModalContinueButton({
211208
id="add-data-connector-continue-button"
212209
data-cy="add-data-connector-continue-button"
213210
className={cx("btn-primary")}
211+
disabled={disableContinueButton}
214212
onClick={addOrEditStorage}
215213
>
216214
<PencilSquare className={cx("bi", "me-1")} /> Update connector
217215
</Button>
216+
{disableContinueButton && (
217+
<UncontrolledTooltip
218+
placement="top"
219+
target="add-data-connector-continue-div"
220+
>
221+
Please fill out all fields labeled as required
222+
</UncontrolledTooltip>
223+
)}
218224
</div>
219225
</div>
220226
);
@@ -294,12 +300,14 @@ interface TestConnectionAndContinueButtonsProps
294300
continueId: string;
295301
step: number;
296302
testId: string;
303+
disableContinueButton: boolean;
297304
editDataConnector?: () => void;
298305
}
299306
function TestConnectionAndContinueButtons({
300307
continueId,
301308
step,
302309
testId,
310+
disableContinueButton,
303311
editDataConnector,
304312
}: TestConnectionAndContinueButtonsProps) {
305313
const dispatch = useAppDispatch();
@@ -395,7 +403,7 @@ function TestConnectionAndContinueButtons({
395403
color={testConnectionColor}
396404
id={buttonTestId}
397405
data-cy={buttonTestId}
398-
disabled={validationResult.isLoading}
406+
disabled={disableContinueButton || validationResult.isLoading}
399407
onClick={() => validateConnection()}
400408
>
401409
{testConnectionContent}

0 commit comments

Comments
 (0)