From 88d44e71573ca5215b4d3a261718528d48088816 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 14 Jan 2025 09:32:41 +0100 Subject: [PATCH] feat: configure disk storage for Renku 2.0 sessions (#3463) Closes #3451. Add an option to configure disk storage in 3 locations: * Add launcher * Modify launcher's resource class * Custom launch **Breaking change**: requires update to `renku-data-services`. --- .../session/components/SessionsList.tsx | 4 +- .../sessionsV2/SessionView/SessionView.tsx | 76 ++++--- .../SessionForm/LauncherDetailsFields.tsx | 169 ++++++++++---- .../SessionModals/ModifyResourcesLauncher.tsx | 206 +++++++++++++++--- .../SessionModals/NewSessionLauncherModal.tsx | 11 +- .../SessionModals/SelectResourceClass.tsx | 174 ++++++++++++--- .../src/features/sessionsV2/sessionsV2.api.ts | 2 +- .../features/sessionsV2/sessionsV2.types.ts | 6 +- .../useSessionResourceClass.hook.ts | 10 +- tests/cypress/e2e/projectV2Session.spec.ts | 24 +- tests/cypress/e2e/projectV2setup.spec.ts | 2 +- .../support/renkulab-fixtures/newSession.ts | 16 +- .../support/renkulab-fixtures/projectV2.ts | 2 +- .../support/renkulab-fixtures/sessions.ts | 12 +- 14 files changed, 538 insertions(+), 176 deletions(-) diff --git a/client/src/features/session/components/SessionsList.tsx b/client/src/features/session/components/SessionsList.tsx index b040873521..245426a67f 100644 --- a/client/src/features/session/components/SessionsList.tsx +++ b/client/src/features/session/components/SessionsList.tsx @@ -340,7 +340,9 @@ export function SessionRowResourceRequests({ {entries.map(([key, value], index) => ( - {value} + + {value} {(key === "memory" || key === "storage") && "GB "} + {key !== "name" && key} {entries.length - 1 === index ? " " : " | "} diff --git a/client/src/features/sessionsV2/SessionView/SessionView.tsx b/client/src/features/sessionsV2/SessionView/SessionView.tsx index 67406a475c..bcc85bb8d1 100644 --- a/client/src/features/sessionsV2/SessionView/SessionView.tsx +++ b/client/src/features/sessionsV2/SessionView/SessionView.tsx @@ -265,8 +265,9 @@ export function SessionView({ resourceRequests={{ name: launcherResourceClass.name, cpu: launcherResourceClass.cpu, - memory: `${launcherResourceClass.memory}G`, - storage: `${launcherResourceClass.default_storage}G`, + memory: launcherResourceClass.memory, + storage: + launcher?.disk_storage ?? launcherResourceClass.default_storage, gpu: launcherResourceClass.gpu, }} /> @@ -374,27 +375,29 @@ export function SessionView({

Default Resource Class

- - - - Set resource class - - - } - requestedPermission="write" - userPermissions={permissions} - /> + {launcher && ( + + + + Set resource class + + + } + requestedPermission="write" + userPermissions={permissions} + /> + )}
{resourceDetails} {launcherResourceClass && !userLauncherResourceClass && ( @@ -403,12 +406,27 @@ export function SessionView({ You do not have access to this resource class.

)} - + {launcher && + launcherResourceClass && + launcher.disk_storage && + launcher.disk_storage > launcherResourceClass.max_storage && ( +

+ + The selected disk storage exceeds the maximum value allowed ( + {launcherResourceClass.max_storage} GB). +

+ )} + {launcher && ( + + )}
diff --git a/client/src/features/sessionsV2/components/SessionForm/LauncherDetailsFields.tsx b/client/src/features/sessionsV2/components/SessionForm/LauncherDetailsFields.tsx index 626b63ab18..6d0876f140 100644 --- a/client/src/features/sessionsV2/components/SessionForm/LauncherDetailsFields.tsx +++ b/client/src/features/sessionsV2/components/SessionForm/LauncherDetailsFields.tsx @@ -15,44 +15,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import cx from "classnames"; import { useMemo } from "react"; +import { Control, Controller, useWatch } from "react-hook-form"; import { - Control, - Controller, - FieldErrors, - UseFormSetValue, -} from "react-hook-form"; -import { SingleValue } from "react-select"; -import { Input, Label } from "reactstrap"; + FormText, + Input, + InputGroup, + InputGroupText, + Label, + UncontrolledTooltip, +} from "reactstrap"; import { WarnAlert } from "../../../../components/Alert"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; import { Loader } from "../../../../components/Loader"; import { useGetResourcePoolsQuery } from "../../../dataServices/computeResources.api"; -import { ResourceClass } from "../../../dataServices/dataServices.types"; import { SessionClassSelectorV2 } from "../../../session/components/options/SessionClassOption"; import { SessionLauncherForm } from "../../sessionsV2.types"; +import { + MIN_SESSION_STORAGE_GB, + STEP_SESSION_STORAGE_GB, +} from "../../../session/startSessionOptions.constants"; interface LauncherDetailsFieldsProps { - control: Control; - errors: FieldErrors; - setValue: UseFormSetValue; + control: Control; } -export function LauncherDetailsFields({ - setValue, - control, - errors, -}: LauncherDetailsFieldsProps) { +export function LauncherDetailsFields({ control }: LauncherDetailsFieldsProps) { const { data: resourcePools, isLoading: isLoadingResourcesPools, error: resourcePoolsError, } = useGetResourcePoolsQuery({}); - const onChangeResourceClass = (resourceClass: SingleValue) => { - if (resourceClass) setValue("resourceClass", resourceClass); - }; - const defaultSessionClass = useMemo( () => resourcePools @@ -64,6 +59,13 @@ export function LauncherDetailsFields({ [resourcePools] ); + const watchCurrentSessionClass = useWatch({ + control, + name: "resourceClass", + defaultValue: defaultSessionClass, + }); + const watchCurrentDiskStorage = useWatch({ control, name: "diskStorage" }); + return (
@@ -76,9 +78,9 @@ export function LauncherDetailsFields({ ( + render={({ field, fieldState: { error } }) => ( 0 ? ( - ( - <> - - {errors?.resourceClass && ( -
- Please provide a resource class -
- )} - - )} - rules={{ required: true }} - /> + <> + ( + <> + + {error && ( +
+ Please provide a resource class +
+ )} + + )} + rules={{ required: true }} + /> + ) : ( There are no one resource pool available to create a session )} + + {watchCurrentSessionClass && ( +
+
+ Disk Storage:{" "} + + {watchCurrentDiskStorage && + watchCurrentDiskStorage != + watchCurrentSessionClass.default_storage ? ( + <>{watchCurrentDiskStorage} GB + ) : ( + <>{watchCurrentSessionClass?.default_storage} GB (default) + )} + +
+ ( + <> + + { + if (isNaN(event.target.valueAsNumber)) { + field.onChange(event.target.value); + } else { + field.onChange(event.target.valueAsNumber); + } + }} + /> + + GB + + + Gigabytes + + + + Default: {watchCurrentSessionClass.default_storage} GB, max:{" "} + {watchCurrentSessionClass.max_storage} GB + +
+ {error?.message || + "Please provide a valid value for disk storage."} +
+ + )} + rules={{ + min: { + value: MIN_SESSION_STORAGE_GB, + message: `Please select a value greater than or equal to ${MIN_SESSION_STORAGE_GB}.`, + }, + max: { + value: watchCurrentSessionClass.max_storage, + message: `Selected disk storage exceeds maximum allowed value (${watchCurrentSessionClass.max_storage} GB).`, + }, + validate: { + integer: (value: unknown) => + value == null || + value === "" || + (!isNaN(parseInt(`${value}`, 10)) && + parseInt(`${value}`, 10) == parseFloat(`${value}`)), + }, + deps: ["resourceClass"], + }} + /> +
+ )}
); diff --git a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx index 9938cc98b4..d897d56035 100644 --- a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx @@ -1,13 +1,29 @@ import cx from "classnames"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { CheckLg, XLg } from "react-bootstrap-icons"; -import { SingleValue } from "react-select"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { Controller, useForm } from "react-hook-form"; +import { + Button, + FormText, + Input, + InputGroup, + InputGroupText, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + UncontrolledTooltip, +} from "reactstrap"; + import { SuccessAlert } from "../../../../components/Alert"; import { Loader } from "../../../../components/Loader"; import { useGetResourcePoolsQuery } from "../../../dataServices/computeResources.api"; import { ResourceClass } from "../../../dataServices/dataServices.types"; import { SessionClassSelectorV2 } from "../../../session/components/options/SessionClassOption"; +import { + MIN_SESSION_STORAGE_GB, + STEP_SESSION_STORAGE_GB, +} from "../../../session/startSessionOptions.constants"; import { useUpdateSessionLauncherMutation } from "../../sessionsV2.api"; import { ErrorOrNotAvailableResourcePools, @@ -18,7 +34,8 @@ interface ModifyResourcesLauncherModalProps { isOpen: boolean; toggleModal: () => void; resourceClassId?: number; - sessionLauncherId?: string; + diskStorage?: number; + sessionLauncherId: string; } export function ModifyResourcesLauncherModal({ @@ -26,6 +43,7 @@ export function ModifyResourcesLauncherModal({ sessionLauncherId, toggleModal, resourceClassId, + diskStorage, }: ModifyResourcesLauncherModalProps) { const [updateSessionLauncher, result] = useUpdateSessionLauncherMutation(); const { @@ -34,43 +52,92 @@ export function ModifyResourcesLauncherModal({ isError: isErrorResources, } = useGetResourcePoolsQuery({}); - const [currentSessionClass, setCurrentSessionClass] = useState< - ResourceClass | undefined - >(undefined); - - const onChange = useCallback((newValue: SingleValue) => { - if (newValue) { - setCurrentSessionClass(newValue); - } - }, []); + const { + control, + formState: { isDirty }, + handleSubmit, + reset, + watch, + } = useForm({ + defaultValues: { + diskStorage, + }, + }); - const onModifyResources = useCallback(() => { - if (currentSessionClass) { - updateSessionLauncher({ - launcherId: sessionLauncherId, - resource_class_id: currentSessionClass?.id, - }); - } - }, [sessionLauncherId, updateSessionLauncher, currentSessionClass]); + const onSubmitInner = useCallback( + (data: ModifyResourcesLauncherForm) => { + if (data.resourceClass) { + const diskStorage = + data.diskStorage && + data.diskStorage != data.resourceClass.default_storage + ? data.diskStorage + : null; + updateSessionLauncher({ + launcherId: sessionLauncherId, + resource_class_id: data.resourceClass.id, + disk_storage: diskStorage, + }); + } + }, + [sessionLauncherId, updateSessionLauncher] + ); + const onSubmit = useMemo( + () => handleSubmit(onSubmitInner), + [handleSubmit, onSubmitInner] + ); useEffect(() => { const currentSessionClass = resourcePools ?.flatMap((pool) => pool.classes) .find((c) => c.id === resourceClassId); - setCurrentSessionClass(currentSessionClass); - }, [resourceClassId, resourcePools]); + reset({ + resourceClass: currentSessionClass, + diskStorage, + }); + }, [diskStorage, reset, resourceClassId, resourcePools]); + + useEffect(() => { + if (!isOpen) { + const currentSessionClass = resourcePools + ?.flatMap((pool) => pool.classes) + .find((c) => c.id === resourceClassId); + reset({ + resourceClass: currentSessionClass, + diskStorage, + }); + } + }, [diskStorage, isOpen, reset, resourceClassId, resourcePools]); + + const watchCurrentSessionClass = watch("resourceClass"); + const watchCurrentDiskStorage = watch("diskStorage"); const selector = isLoadingResources ? ( ) : !resourcePools || resourcePools.length == 0 || isErrorResources ? ( ) : ( - ( + <> + + {error && ( +
+ {error.message || "Please provide a valid resource class."} +
+ )} + + )} + rules={{ required: "Please provide a resource class." }} /> ); + return (
{selector}
+ {watchCurrentSessionClass && ( +
+
+ Disk Storage:{" "} + + {watchCurrentDiskStorage && + watchCurrentDiskStorage != + watchCurrentSessionClass.default_storage ? ( + <>{watchCurrentDiskStorage} GB + ) : ( + <>{watchCurrentSessionClass?.default_storage} GB (default) + )} + +
+ ( + <> + + { + if (isNaN(event.target.valueAsNumber)) { + field.onChange(event.target.value); + } else { + field.onChange(event.target.valueAsNumber); + } + }} + /> + + GB + + + Gigabytes + + + + Default: {watchCurrentSessionClass.default_storage} GB, max:{" "} + {watchCurrentSessionClass.max_storage} GB + +
+ {error?.message || + "Please provide a valid value for disk storage."} +
+ + )} + rules={{ + min: { + value: MIN_SESSION_STORAGE_GB, + message: `Please select a value greater than or equal to ${MIN_SESSION_STORAGE_GB}.`, + }, + max: { + value: watchCurrentSessionClass.max_storage, + message: `Selected disk storage exceeds maximum allowed value (${watchCurrentSessionClass.max_storage} GB).`, + }, + validate: { + integer: (value: unknown) => + value == null || + value === "" || + (!isNaN(parseInt(`${value}`, 10)) && + parseInt(`${value}`, 10) == parseFloat(`${value}`)), + }, + deps: ["resourceClass"], + }} + /> +
+ )}
diff --git a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx index 6a92d7cc7e..6abed19b13 100644 --- a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx @@ -18,11 +18,23 @@ import { skipToken } from "@reduxjs/toolkit/query"; import cx from "classnames"; -import { useCallback, useState } from "react"; +import { useCallback, useMemo } from "react"; import { XLg } from "react-bootstrap-icons"; +import { Controller, useForm } from "react-hook-form"; import { Link } from "react-router-dom-v5-compat"; -import { SingleValue } from "react-select"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { + Button, + FormText, + Input, + InputGroup, + InputGroupText, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + UncontrolledTooltip, +} from "reactstrap"; + import { useGetResourceClassByIdQuery, useGetResourcePoolsQuery, @@ -30,6 +42,10 @@ import { import { ResourceClass } from "../../../dataServices/dataServices.types"; import { SessionRowResourceRequests } from "../../../session/components/SessionsList"; import { SessionClassSelectorV2 } from "../../../session/components/options/SessionClassOption"; +import { + MIN_SESSION_STORAGE_GB, + STEP_SESSION_STORAGE_GB, +} from "../../../session/startSessionOptions.constants"; import { ErrorOrNotAvailableResourcePools, FetchingResourcePools, @@ -37,7 +53,7 @@ import { interface SelectResourceClassModalProps { isOpen: boolean; - onContinue: (env: ResourceClass) => void; + onContinue: (env: ResourceClass, diskStorage: number | undefined) => void; projectUrl: string; resourceClassId?: number; isCustom: boolean; @@ -55,34 +71,61 @@ export function SelectResourceClassModal({ isError, } = useGetResourcePoolsQuery({}); - const [currentSessionClass, setCurrentSessionClass] = useState< - ResourceClass | undefined - >(undefined); - const { data: launcherClass, isLoading: isLoadingLauncherClass } = useGetResourceClassByIdQuery(resourceClassId ?? skipToken); - const onChange = useCallback((newValue: SingleValue) => { - if (newValue) { - setCurrentSessionClass(newValue); - } - }, []); + const { + control, + formState: { isDirty }, + handleSubmit, + watch, + } = useForm(); + + const onSubmitInner = useCallback( + (data: SelectResourceClassForm) => { + if (data.resourceClass) { + const diskStorage = + data.diskStorage != null && + data.diskStorage != data.resourceClass.default_storage + ? data.diskStorage + : undefined; + onContinue(data.resourceClass, diskStorage); + } + }, + [onContinue] + ); + const onSubmit = useMemo( + () => handleSubmit(onSubmitInner), + [handleSubmit, onSubmitInner] + ); - const onClick = useCallback(() => { - if (currentSessionClass) { - onContinue(currentSessionClass); - } - }, [currentSessionClass, onContinue]); + const watchCurrentSessionClass = watch("resourceClass"); + const watchCurrentDiskStorage = watch("diskStorage"); const selector = isLoading ? ( ) : !resourcePools || resourcePools.length == 0 || isError ? ( ) : ( - ( + <> + + {error && ( +
+ {error.message || "Please provide a valid resource class."} +
+ )} + + )} + rules={{ required: "Please provide a resource class." }} /> ); @@ -116,7 +159,7 @@ export function SelectResourceClassModal({

You do not have access to the default resource class of this session launcher. Please select one of your available resource classes to - continue.” + continue.

)} {launcherClass && ( @@ -128,6 +171,80 @@ export function SelectResourceClassModal({

)}
{selector}
+ {watchCurrentSessionClass && ( +
+
+ Disk Storage:{" "} + + {watchCurrentDiskStorage && + watchCurrentDiskStorage != + watchCurrentSessionClass.default_storage ? ( + <>{watchCurrentDiskStorage} GB + ) : ( + <>{watchCurrentSessionClass?.default_storage} GB (default) + )} + +
+ ( + <> + + { + if (isNaN(event.target.valueAsNumber)) { + field.onChange(event.target.value); + } else { + field.onChange(event.target.valueAsNumber); + } + }} + /> + + GB + + + Gigabytes + + + + Default: {watchCurrentSessionClass.default_storage} GB, max:{" "} + {watchCurrentSessionClass.max_storage} GB + +
+ {error?.message || + "Please provide a valid value for disk storage."} +
+ + )} + rules={{ + min: { + value: MIN_SESSION_STORAGE_GB, + message: `Please select a value greater than or equal to ${MIN_SESSION_STORAGE_GB}.`, + }, + max: { + value: watchCurrentSessionClass.max_storage, + message: `Selected disk storage exceeds maximum allowed value (${watchCurrentSessionClass.max_storage} GB).`, + }, + validate: { + integer: (value: unknown) => + value == null || + value === "" || + (!isNaN(parseInt(`${value}`, 10)) && + parseInt(`${value}`, 10) == parseFloat(`${value}`)), + }, + deps: ["resourceClass"], + }} + /> +
+ )} Cancel launch - ); } + +interface SelectResourceClassForm { + resourceClass: ResourceClass | undefined; + diskStorage: number | undefined; +} diff --git a/client/src/features/sessionsV2/sessionsV2.api.ts b/client/src/features/sessionsV2/sessionsV2.api.ts index 7854de9e72..003793113c 100644 --- a/client/src/features/sessionsV2/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/sessionsV2.api.ts @@ -40,7 +40,7 @@ import { const sessionsV2Api = createApi({ reducerPath: "sessionsV2Api", baseQuery: fetchBaseQuery({ - baseUrl: "/ui-server/api/data", + baseUrl: "/api/data", }), tagTypes: ["Environment", "Launcher", "SessionsV2"], endpoints: (builder) => ({ diff --git a/client/src/features/sessionsV2/sessionsV2.types.ts b/client/src/features/sessionsV2/sessionsV2.types.ts index 087dddbefc..d111671ae0 100644 --- a/client/src/features/sessionsV2/sessionsV2.types.ts +++ b/client/src/features/sessionsV2/sessionsV2.types.ts @@ -45,6 +45,7 @@ export type SessionLauncher = { creation_date: string; description?: string; resource_class_id?: number; + disk_storage?: number; environment: SessionLauncherEnvironment; }; @@ -98,14 +99,16 @@ export type AddSessionLauncherParams = { name: string; project_id: string; resource_class_id?: number; + disk_storage?: number; environment: SessionLauncherEnvironmentParams; }; export interface UpdateSessionLauncherParams { - launcherId?: string; + launcherId: string; description?: string; name?: string; resource_class_id?: number; + disk_storage?: number | null; environment?: SessionLauncherEnvironmentParams; } @@ -121,6 +124,7 @@ export interface SessionLauncherForm { environment_kind: EnvironmentKind; environment_id: string; resourceClass: ResourceClass; + diskStorage: number | undefined; port: number; working_directory: string; uid: number; diff --git a/client/src/features/sessionsV2/useSessionResourceClass.hook.ts b/client/src/features/sessionsV2/useSessionResourceClass.hook.ts index 4499624f8f..7b1b0bf2e2 100644 --- a/client/src/features/sessionsV2/useSessionResourceClass.hook.ts +++ b/client/src/features/sessionsV2/useSessionResourceClass.hook.ts @@ -47,14 +47,17 @@ export default function useSessionResourceClass({ const [isPendingResourceClass, setIsPendingResourceClass] = useState(false); const setResourceClass = useCallback( - (envClass: ResourceClass) => { + (envClass: ResourceClass, diskStorage: number | undefined) => { if (envClass) { dispatch( startSessionOptionsV2Slice.actions.setSessionClass(envClass.id) ); + const cappedStorage = diskStorage + ? Math.min(diskStorage, envClass.max_storage) + : diskStorage; dispatch( startSessionOptionsV2Slice.actions.setStorage( - envClass.default_storage + cappedStorage ?? envClass.default_storage ) ); setIsPendingResourceClass(false); @@ -86,10 +89,11 @@ export default function useSessionResourceClass({ } if (initialSessionClass && !isCustomLaunch) - setResourceClass(initialSessionClass); + setResourceClass(initialSessionClass, launcher.disk_storage); }, [ isCustomLaunch, isLoadingLauncherClass, + launcher.disk_storage, launcherClass, resourcePools, setResourceClass, diff --git a/tests/cypress/e2e/projectV2Session.spec.ts b/tests/cypress/e2e/projectV2Session.spec.ts index 7bd7f67039..39fdd9a241 100644 --- a/tests/cypress/e2e/projectV2Session.spec.ts +++ b/tests/cypress/e2e/projectV2Session.spec.ts @@ -98,7 +98,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); req.reply({ body: session, delay: 2000 }); @@ -157,7 +157,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); const storage = csConfig[0]; @@ -238,7 +238,7 @@ describe("launch sessions with data connectors", () => { cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); const storage = csConfig[0]; @@ -331,7 +331,7 @@ describe("launch sessions with data connectors", () => { cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); const storage = csConfig[0]; @@ -394,7 +394,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); const storage = csConfig[0]; @@ -433,7 +433,7 @@ describe("launch sessions with data connectors", () => { cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(1); const storage = csConfig[0]; @@ -523,7 +523,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(3); const s3Storage = csConfig[0]; @@ -607,7 +607,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(3); const s3Storage = csConfig[0]; @@ -702,7 +702,7 @@ describe("launch sessions with data connectors", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { const csConfig = req.body.cloudstorage; expect(csConfig.length).equal(3); const s3Storage = csConfig[0]; @@ -824,7 +824,7 @@ describe("launch sessions with secrets", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { req.reply({ body: session, delay: 2000 }); }).as("createSession"); }); @@ -861,7 +861,7 @@ describe("launch sessions with secrets", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { req.reply({ body: session, delay: 2000 }); }).as("createSession"); }); @@ -909,7 +909,7 @@ describe("launch sessions with secrets", () => { // start session cy.fixture("sessions/sessionV2.json").then((session) => { // eslint-disable-next-line max-nested-callbacks - cy.intercept("POST", "/ui-server/api/data/sessions", (req) => { + cy.intercept("POST", "/api/data/sessions", (req) => { req.reply({ body: session, delay: 2000 }); }).as("createSession"); }); diff --git a/tests/cypress/e2e/projectV2setup.spec.ts b/tests/cypress/e2e/projectV2setup.spec.ts index 3f5b71a5d7..3fe6647f7c 100644 --- a/tests/cypress/e2e/projectV2setup.spec.ts +++ b/tests/cypress/e2e/projectV2setup.spec.ts @@ -76,7 +76,7 @@ describe("Set up project components", () => { }); it("set up sessions", () => { - cy.intercept("/ui-server/api/data/sessions*", { + cy.intercept("/api/data/sessions*", { body: [], }).as("getSessionsV2"); fixtures diff --git a/tests/cypress/support/renkulab-fixtures/newSession.ts b/tests/cypress/support/renkulab-fixtures/newSession.ts index 283b159406..072a24a125 100644 --- a/tests/cypress/support/renkulab-fixtures/newSession.ts +++ b/tests/cypress/support/renkulab-fixtures/newSession.ts @@ -111,22 +111,14 @@ export function NewSession(Parent: T) { newLauncher(args?: SimpleFixture) { const { fixture = "", name = "newLauncher" } = args ?? {}; const response = { fixture, statusCode: 201 }; - cy.intercept( - "POST", - "/ui-server/api/data/session_launchers", - response - ).as(name); + cy.intercept("POST", "/api/data/session_launchers", response).as(name); return this; } editLauncher(args?: SimpleFixture) { const { fixture = "", name = "editLauncher" } = args ?? {}; const response = { fixture, statusCode: 201 }; - cy.intercept( - "PATCH", - "/ui-server/api/data/session_launchers/*", - response - ).as(name); + cy.intercept("PATCH", "/api/data/session_launchers/*", response).as(name); return this; } @@ -136,9 +128,7 @@ export function NewSession(Parent: T) { name = "getEnvironments", } = args ?? {}; const response = { fixture }; - cy.intercept("GET", `/ui-server/api/data/environments`, response).as( - name - ); + cy.intercept("GET", `/api/data/environments`, response).as(name); return this; } }; diff --git a/tests/cypress/support/renkulab-fixtures/projectV2.ts b/tests/cypress/support/renkulab-fixtures/projectV2.ts index 2a8842c35e..b20c917ca5 100644 --- a/tests/cypress/support/renkulab-fixtures/projectV2.ts +++ b/tests/cypress/support/renkulab-fixtures/projectV2.ts @@ -420,7 +420,7 @@ export function ProjectV2(Parent: T) { const response = { fixture }; cy.intercept( "GET", - `/ui-server/api/data/projects/*/session_launchers`, + `/api/data/projects/*/session_launchers`, response ).as(name); return this; diff --git a/tests/cypress/support/renkulab-fixtures/sessions.ts b/tests/cypress/support/renkulab-fixtures/sessions.ts index f2053d3a56..69bb313ca9 100644 --- a/tests/cypress/support/renkulab-fixtures/sessions.ts +++ b/tests/cypress/support/renkulab-fixtures/sessions.ts @@ -39,7 +39,7 @@ export function Sessions(Parent: T) { const { fixture = "sessions/sessions.json", name = "getSessionsV2" } = args ?? {}; const response = { fixture }; - cy.intercept("GET", "/ui-server/api/data/sessions*", response).as(name); + cy.intercept("GET", "/api/data/sessions*", response).as(name); return this; } @@ -231,11 +231,9 @@ export function Sessions(Parent: T) { sessionImage(args?: NameOnlyFixture) { const { name = "getSessionImage" } = args ?? {}; const response = { status: 200 }; - cy.intercept( - "GET", - "/ui-server/api/data/sessions/images?image_url=*", - response - ).as(name); + cy.intercept("GET", "/api/data/sessions/images?image_url=*", response).as( + name + ); return this; } @@ -251,7 +249,7 @@ export function Sessions(Parent: T) { sessionServersEmptyV2(args?: NameOnlyFixture) { const { name = "sessionServersEmptyV2" } = args ?? {}; const response = { body: [] }; - cy.intercept("GET", "/ui-server/api/data/sessions", response).as(name); + cy.intercept("GET", "/api/data/sessions", response).as(name); return this; }