diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml index e7711d2224..46252ad1c9 100644 --- a/.github/workflows/acceptance-tests.yml +++ b/.github/workflows/acceptance-tests.yml @@ -31,6 +31,7 @@ jobs: renku-graph: ${{ steps.deploy-comment.outputs.renku-graph}} renku-notebooks: ${{ steps.deploy-comment.outputs.renku-notebooks}} renku-data-services: ${{ steps.deploy-comment.outputs.renku-data-services}} + amalthea: ${{ steps.deploy-comment.outputs.amalthea}} test-enabled: ${{ steps.deploy-comment.outputs.test-enabled}} extra-values: ${{ steps.deploy-comment.outputs.extra-values}} steps: @@ -88,6 +89,7 @@ jobs: renku_graph: "${{ needs.check-deploy.outputs.renku-graph }}" renku_notebooks: "${{ needs.check-deploy.outputs.renku-notebooks }}" renku_data_services: "${{ needs.check-deploy.outputs.renku-data-services }}" + amalthea: "${{ needs.check-deploy.outputs.amalthea }}" extra_values: "${{ needs.check-deploy.outputs.extra-values }}" selenium-acceptance-tests: diff --git a/client/src/components/errors/RtkErrorAlert.tsx b/client/src/components/errors/RtkErrorAlert.tsx index 6503c44196..5d28f976dd 100644 --- a/client/src/components/errors/RtkErrorAlert.tsx +++ b/client/src/components/errors/RtkErrorAlert.tsx @@ -19,11 +19,12 @@ import { SerializedError } from "@reduxjs/toolkit"; import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query"; -import { ErrorAlert } from "../Alert"; +import { ErrorAlert, RenkuAlert } from "../Alert"; import { CoreErrorAlert } from "./CoreErrorAlert"; import { CoreErrorResponse } from "../../utils/types/coreService.types"; import { extractTextFromObject } from "../../utils/helpers/TextUtils"; import { UpdateProjectResponse } from "../../features/project/Project"; +import { NotebooksErrorResponse } from "../../features/session/sessions.types"; export function extractRkErrorMessage( error: FetchBaseQueryError | SerializedError, @@ -93,7 +94,10 @@ export function RtkErrorAlert({ ); } -export function RtkOrCoreError({ error }: RtkErrorAlertProps) { +export function RtkOrCoreError({ + error, + dismissible = true, +}: RtkErrorAlertProps) { if (!error) return null; return "status" in error && typeof error.status === "number" && @@ -104,6 +108,32 @@ export function RtkOrCoreError({ error }: RtkErrorAlertProps) { (error.data as CoreErrorResponse).error ? ( ) : ( - + ); } + +export function RtkOrNotebooksError({ + error, + dismissible = true, +}: RtkErrorAlertProps) { + if (!error) return null; + if ( + "status" in error && + typeof error.status === "number" && + (error.status as number) >= 400 && + typeof error.data === "object" && + error.data && + "error" in error.data && + (error.data as NotebooksErrorResponse).error + ) { + return ( + +

Error {(error.data as NotebooksErrorResponse).error.code}

+

+ {(error.data as NotebooksErrorResponse).error.message} +

+
+ ); + } + return ; +} diff --git a/client/src/features/project/components/AddCloudStorageButton.module.scss b/client/src/features/project/components/AddCloudStorageButton.module.scss deleted file mode 100644 index ec983852a8..0000000000 --- a/client/src/features/project/components/AddCloudStorageButton.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.modal :global(.modal-content) { - height: unset !important; -} diff --git a/client/src/features/project/components/AddCloudStorageButton.tsx b/client/src/features/project/components/AddCloudStorageButton.tsx deleted file mode 100644 index 665aa93201..0000000000 --- a/client/src/features/project/components/AddCloudStorageButton.tsx +++ /dev/null @@ -1,887 +0,0 @@ -/*! - * Copyright 2023 - 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, useMemo, useState } from "react"; -import { CheckLg, CloudFill, PlusLg, XLg } from "react-bootstrap-icons"; -import { Controller, useFieldArray, useForm } from "react-hook-form"; -import { - Button, - Card, - CardBody, - Col, - Container, - Form, - FormText, - Input, - Label, - Modal, - ModalBody, - ModalFooter, - ModalHeader, - Row, -} from "reactstrap"; - -import { ExternalLink } from "../../../components/ExternalLinks"; -import { Loader } from "../../../components/Loader"; -import { RtkErrorAlert } from "../../../components/errors/RtkErrorAlert"; -import LazyRenkuMarkdown from "../../../components/markdown/LazyRenkuMarkdown"; -import useLegacySelector from "../../../utils/customHooks/useLegacySelector.hook"; -import { StateModelProject } from "../Project"; -import { - useAddCloudStorageForProjectMutation, - useUpdateCloudStorageMutation, -} from "../projectCloudStorage.api"; -import { - CLOUD_STORAGE_CONFIGURATION_PLACEHOLDER, - CLOUD_STORAGE_READWRITE_ENABLED, - CLOUD_STORAGE_SENSITIVE_FIELD_TOKEN, -} from "../projectCloudStorage.constants"; -import { - CloudStorage, - CloudStorageCredential, -} from "../projectCloudStorage.types"; -import { - getCredentialFieldDefinitions, - parseCloudStorageConfiguration, -} from "../utils/projectCloudStorage.utils"; - -import { useGetNotebooksVersionQuery } from "../../versions/versions.api"; -import styles from "./AddCloudStorageButton.module.scss"; - -export default function AddCloudStorageButton() { - const [isOpen, setIsOpen] = useState(false); - const toggle = useCallback(() => { - setIsOpen((open) => !open); - }, []); - - return ( - <> - - - - ); -} - -interface AddCloudStorageModalProps { - isOpen: boolean; - toggle: () => void; -} - -function AddCloudStorageModal({ isOpen, toggle }: AddCloudStorageModalProps) { - const [state, setState] = useState({ - step: "configuration", - mode: "simple", - }); - const toggleAdvanced = useCallback(() => { - setState((prevState) => { - if (prevState.step === "credentials") { - return prevState; - } - return { - ...prevState, - mode: prevState.mode === "advanced" ? "simple" : "advanced", - }; - }); - }, []); - const goToCredentialsStep = useCallback((storageDefinition: CloudStorage) => { - setState({ step: "credentials", storageDefinition }); - }, []); - - // Reset state when closed - useEffect(() => { - if (!isOpen) { - setState({ step: "configuration", mode: "simple" }); - } - }, [isOpen]); - - return ( - - - - Add Cloud Storage - - {state.step === "configuration" && ( - -
-
- - -
-
-
- )} - {state.step === "credentials" ? ( - - ) : state.mode === "advanced" ? ( - - ) : ( - - )} -
- ); -} - -type AddCloudStorageModalState = - | { - step: "configuration"; - mode: "simple" | "advanced"; - } - | { - step: "credentials"; - storageDefinition: CloudStorage; - }; - -interface AddCloudStorageProps { - goToCredentialsStep: (storageDefinition: CloudStorage) => void; - toggle: () => void; -} - -function AdvancedAddCloudStorage({ - goToCredentialsStep, - toggle, -}: AddCloudStorageProps) { - const projectId = useLegacySelector( - (state) => state.stateModel.project.metadata.id - ); - - const [addCloudStorageForProject, result] = - useAddCloudStorageForProjectMutation(); - - const { - control, - formState: { errors }, - handleSubmit, - } = useForm({ - defaultValues: { - configuration: "", - name: "", - private: true, - readonly: true, - source_path: "", - }, - }); - const onSubmit = useCallback( - (data: AdvancedAddCloudStorageForm) => { - const configuration = parseCloudStorageConfiguration(data.configuration); - addCloudStorageForProject({ - configuration, - name: data.name, - private: data.private, - readonly: data.readonly, - project_id: `${projectId}`, - source_path: data.source_path, - target_path: data.name, - }); - }, - [addCloudStorageForProject, projectId] - ); - - // Handle picking required credentials if necessary - useEffect(() => { - if (!result.isSuccess) { - return; - } - if ( - !result.data.storage.private || - result.data.sensitive_fields == null || - result.data.sensitive_fields.length == 0 - ) { - toggle(); - return; - } - goToCredentialsStep(result.data); - }, [goToCredentialsStep, result.data, result.isSuccess, toggle]); - - return ( - <> - -
- {result.error && } - -
-

- Advanced mode uses rclone configurations to set up - cloud storage. -

-

- Learn more at the{" "} - - . -

- -
- - - The name also determines the mount location, though it is - possible to change it later. - - ( - - )} - rules={{ required: true }} - /> -
Please provide a name
-
- -
- ( - - )} - /> - - - Check this box if this cloud storage requires credentials to be - used. - -
- - {!CLOUD_STORAGE_READWRITE_ENABLED ? null : ( -
-
Mode
- ( - <> -
- field.onChange(true)} - /> - -
-
- field.onChange(false)} - /> - -
- - )} - /> -
- )} - -
- - ( - - )} - rules={{ required: true }} - /> -
- Please provide a valid source path -
-
- -
- - - You can paste here the output of{" "} - - rclone config show <name> - - . - - ( -