Skip to content

Commit

Permalink
feat: improve feedback when starting sessions on outdated projects (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ciyer authored and lorenzo-cavazzi committed Jan 22, 2024
1 parent 19344d8 commit 6a3efc5
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 9 deletions.
13 changes: 12 additions & 1 deletion client/src/features/session/components/StartNewSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { DEFAULT_APP_PARAMS } from "../../../utils/context/appParams.constants";
import { isFetchBaseQueryError } from "../../../utils/helpers/ApiErrors";
import { Url } from "../../../utils/helpers/url";
import { getProvidedSensitiveFields } from "../../project/utils/projectCloudStorage.utils";
import { useCoreSupport } from "../../project/useProjectCoreSupport";
import { useStartSessionMutation } from "../sessions.api";
import startSessionSlice, {
setError,
Expand Down Expand Up @@ -658,6 +659,14 @@ function StartSessionButton() {
storage,
} = useStartSessionOptionsSelector();

const projectRepositoryUrl = useSelector<RootStateOrAny, string>(
(state) => state.stateModel.project.metadata.externalUrl
);
const { coreSupport } = useCoreSupport({
gitUrl: projectRepositoryUrl ?? undefined,
branch: branch,
});

const missingCredentialsStorage = useMemo(
() =>
cloudStorage
Expand All @@ -674,7 +683,9 @@ function StartSessionButton() {
);

const enabled =
dockerImageStatus === "available" && missingCredentialsStorage.length == 0;
dockerImageStatus === "available" &&
missingCredentialsStorage.length == 0 &&
coreSupport.backendAvailable;

const dispatch = useDispatch();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import cx from "classnames";
import { useCallback } from "react";
import { RootStateOrAny, useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import {
Badge,
Button,
Expand All @@ -33,7 +34,12 @@ import {
import { ErrorAlert } from "../../../../components/Alert";
import { Loader } from "../../../../components/Loader";
import { ProjectConfig } from "../../../project/Project";
import { Url } from "../../../../utils/helpers/url";
import { useCoreSupport } from "../../../project/useProjectCoreSupport";
import {
canUpdateProjectAutomatically,
getRenkuLevel,
} from "../../../project/utils/migrations";
import useDefaultAutoFetchLfsOption from "../../hooks/options/useDefaultAutoFetchLfsOption.hook";
import useDefaultUrlOption from "../../hooks/options/useDefaultUrlOption.hook";
import usePatchedProjectConfig from "../../hooks/usePatchedProjectConfig.hook";
Expand All @@ -48,6 +54,88 @@ import { SessionClassOption } from "./SessionClassOption";
import { SessionStorageOption } from "./SessionStorageOption";
import styles from "./StartNotebookServerOptions.module.scss";

type CoreSupport = ReturnType<typeof useCoreSupport>["coreSupport"];
type GetMigrationStatusQuery = ReturnType<
typeof useCoreSupport
>["getMigrationStatusQuery"];

type BackendNotAvailableProps = {
coreSupport: CoreSupport;
getMigrationStatusQuery: GetMigrationStatusQuery;
projectNamespace: string;
projectName: string;
};
function BackendNotAvailableMessage({
coreSupport,
getMigrationStatusQuery,
projectNamespace,
projectName,
}: BackendNotAvailableProps) {
if (coreSupport.backendAvailable) return null;
const {
backendAvailable,
backendErrorMessage,
computed: coreSupportComputed,
} = coreSupport;
const isSupported = coreSupportComputed && backendAvailable;
const checkingSupport = !coreSupportComputed;
const { data: migrationStatus } = getMigrationStatusQuery;

if (checkingSupport || isSupported) return null;

const renkuMigrationLevel = getRenkuLevel(migrationStatus, isSupported);
const automatedUpdatePossible = canUpdateProjectAutomatically(
renkuMigrationLevel,
null
);

const settingsPageUrl = Url.get(Url.pages.project.settings, {
namespace: projectNamespace,
path: projectName,
});
if (backendErrorMessage)
return (
<>
<h3 className={cx("fs-6", "fw-bold")}>
A session cannot be started on this project.
</h3>
<div>{backendErrorMessage}</div>
</>
);

if (automatedUpdatePossible)
return (
<>
<h3 className={cx("fs-6", "fw-bold")}>
Update the project to start a session.
</h3>
<div>
Follow the instructions on the{" "}
<Link className="btn btn-danger btn-sm" to={settingsPageUrl}>
Settings
</Link>{" "}
page to update the project. Once that is done, you can start a
session.
</div>
</>
);

return (
<>
<h3 className={cx("fs-6", "fw-bold")}>
Changes are necessary to start a session.
</h3>
<div>
Details are provided on the{" "}
<Link className="btn btn-danger btn-sm" to={settingsPageUrl}>
Settings
</Link>{" "}
page.
</div>
</>
);
}

export const StartNotebookServerOptions = () => {
// Wait for options to load

Expand All @@ -64,7 +152,14 @@ export const StartNotebookServerOptions = () => {
const gitLabProjectId = useSelector<RootStateOrAny, number | null>(
(state) => state.stateModel.project.metadata.id ?? null
);
const { coreSupport } = useCoreSupport({

const projectNamespace = useSelector<RootStateOrAny, string>(
(state) => state.stateModel.project.metadata.namespace
);
const projectName = useSelector<RootStateOrAny, string>(
(state) => state.stateModel.project.metadata.path
);
const { coreSupport, getMigrationStatusQuery } = useCoreSupport({
gitUrl: projectRepositoryUrl ?? undefined,
branch: defaultBranch ?? undefined,
});
Expand Down Expand Up @@ -112,13 +207,18 @@ export const StartNotebookServerOptions = () => {
if (!backendAvailable || errorProjectConfig) {
return (
<ErrorAlert dismissible={false}>
<h3 className={cx("fs-6", "fw-bold")}>
{!backendAvailable ? (
<>Error: This project is not supported</>
) : (
<>Error while loading project configuration</>
)}
</h3>
{!backendAvailable ? (
<BackendNotAvailableMessage
coreSupport={coreSupport}
getMigrationStatusQuery={getMigrationStatusQuery}
projectName={projectName}
projectNamespace={projectNamespace}
/>
) : (
<h3 className={cx("fs-6", "fw-bold")}>
Error while loading project configuration
</h3>
)}
</ErrorAlert>
);
}
Expand Down
78 changes: 78 additions & 0 deletions tests/cypress/e2e/newSession.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,81 @@ describe("launch sessions", () => {
}
});
});

describe("launch sessions, outdated projects", () => {
beforeEach(() => {
fixtures.config().versions().projects().landingUserProjects();
fixtures.projectTest();
fixtures
.sessionAutosave()
.sessionServersEmpty()
.sessionsVersion()
.renkuIni()
.sessionServerOptions()
.resourcePoolsTest()
.projectConfigShow()
.projectLockStatus()
.cloudStorage();
cy.visit("/projects/e2e/local-test-project");
});

it("new session page - logged - project automatically upgradable", () => {
fixtures.interceptMigrationCheck({
fixture: "project/migrationStatus/level5-old-updatable.json",
});
fixtures.userTest();
fixtures.newSessionImages();
cy.visit("/projects/e2e/local-test-project/sessions/new");
cy.wait("@getSessionImage", { timeout: 10000 });
cy.get("form")
.contains("Start Session")
.should("be.visible")
.should("be.disabled");
cy.get("form")
.contains("Update the project to start a session.")
.should("be.visible");
cy.get("a.btn")
.should("have.attr", "href", "/projects/e2e/local-test-project/settings")
.contains("Settings")
.should("be.visible");
});

it("new session page - logged - project manually upgradable", () => {
fixtures.interceptMigrationCheck({
fixture: "project/migrationStatus/level5-old-version.json",
});
fixtures.userTest();
fixtures.newSessionImages();
cy.visit("/projects/e2e/local-test-project/sessions/new");
cy.wait("@getSessionImage", { timeout: 10000 });
cy.get("form")
.contains("Start Session")
.should("be.visible")
.should("be.disabled");
cy.get("form")
.contains("Changes are necessary to start a session.")
.should("be.visible");
cy.get("a.btn")
.should("have.attr", "href", "/projects/e2e/local-test-project/settings")
.contains("Settings")
.should("be.visible");
});

it("new session page - logged - project support error", () => {
fixtures.projectMigrationError();
fixtures.userTest();
fixtures.newSessionImages();
cy.visit("/projects/e2e/local-test-project/sessions/new");
cy.wait("@getSessionImage", { timeout: 10000 });
cy.get("form")
.contains("Start Session")
.should("be.visible")
.should("be.disabled");
cy.get("form")
.contains("A session cannot be started on this project.")
.should("be.visible");
cy.get("form")
.contains("There was an unexpected error while handling project data.")
.should("be.visible");
});
});

0 comments on commit 6a3efc5

Please sign in to comment.