From 9899f56fc99e466c8d4864bd80faa58bac000b43 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 08:50:26 +0100 Subject: [PATCH 01/11] refactor: use API code generators for sessions v2 --- .../sessionsV2/api/sessionsV2.api-config.ts | 32 +++++++++++++++++++ .../features/sessionsV2/api/sessionsV2.api.ts | 0 .../sessionsV2/api/sessionsV2.empty-api.ts | 26 +++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 client/src/features/sessionsV2/api/sessionsV2.api-config.ts create mode 100644 client/src/features/sessionsV2/api/sessionsV2.api.ts create mode 100644 client/src/features/sessionsV2/api/sessionsV2.empty-api.ts diff --git a/client/src/features/sessionsV2/api/sessionsV2.api-config.ts b/client/src/features/sessionsV2/api/sessionsV2.api-config.ts new file mode 100644 index 0000000000..eda5fee45b --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionsV2.api-config.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2025 - 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. + */ + +// Run `npm run generate-api:sessionsV2` to generate the API +import type { ConfigFile } from "@rtk-query/codegen-openapi"; +import path from "path"; + +const config: ConfigFile = { + apiFile: "./sessionsV2.empty-api.ts", + apiImport: "sessionsV2EmptyApi", + outputFile: "./sessionsV2.generated-api.ts", + exportName: "sessionsV2GeneratedApi", + hooks: true, + schemaFile: path.join(__dirname, "sessionsV2.openapi.json"), +}; + +export default config; diff --git a/client/src/features/sessionsV2/api/sessionsV2.api.ts b/client/src/features/sessionsV2/api/sessionsV2.api.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/client/src/features/sessionsV2/api/sessionsV2.empty-api.ts b/client/src/features/sessionsV2/api/sessionsV2.empty-api.ts new file mode 100644 index 0000000000..37f9908e06 --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionsV2.empty-api.ts @@ -0,0 +1,26 @@ +/*! + * Copyright 2025 - 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 { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; + +// initialize an empty api service that we'll inject endpoints into later as needed +export const sessionsV2EmptyApi = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: "/api/data" }), + endpoints: () => ({}), + reducerPath: "sessionsV2Api", +}); From 92346b75ac9affb86b8ed7739b196f44494f92ff Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 11:25:44 +0100 Subject: [PATCH 02/11] a step :) --- client/package.json | 3 +- .../src/features/dashboardV2/DashboardV2.tsx | 3 +- .../session/components/SessionsList.tsx | 8 +- .../session/useWaitForSessionStatus.hook.ts | 3 +- .../SessionList/SessionItemDisplay.tsx | 3 +- .../SessionShowPage/ShowSessionPage.tsx | 29 +- client/src/features/sessionsV2/SessionsV2.tsx | 3 +- .../features/sessionsV2/api/sessionsV2.api.ts | 48 + .../api/sessionsV2.generated-api.ts | 461 ++++++ .../sessionsV2/api/sessionsV2.openapi.json | 1420 +++++++++++++++++ .../SessionButton/ActiveSessionButton.tsx | 10 +- .../SessionStatus/SessionStatus.tsx | 4 +- .../src/features/sessionsV2/sessionsV2.api.ts | 2 +- .../features/sessionsV2/sessionsV2.types.ts | 18 +- 14 files changed, 1977 insertions(+), 38 deletions(-) create mode 100644 client/src/features/sessionsV2/api/sessionsV2.generated-api.ts create mode 100644 client/src/features/sessionsV2/api/sessionsV2.openapi.json diff --git a/client/package.json b/client/package.json index 8d3c3f3ae7..36cc7a736a 100644 --- a/client/package.json +++ b/client/package.json @@ -29,7 +29,8 @@ "generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts", "generate-api:platform": "rtk-query-codegen-openapi src/features/platform/api/platform.api-config.ts", "generate-api:searchV2": "rtk-query-codegen-openapi src/features/searchV2/api/searchV2.api-config.ts", - "generate-api:users": "rtk-query-codegen-openapi src/features/usersV2/api/users.api-config.ts" + "generate-api:users": "rtk-query-codegen-openapi src/features/usersV2/api/users.api-config.ts", + "generate-api:sessionsV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionsV2.api-config.ts" }, "type": "module", "dependencies": { diff --git a/client/src/features/dashboardV2/DashboardV2.tsx b/client/src/features/dashboardV2/DashboardV2.tsx index 6ce7b0a54e..72e60cec78 100644 --- a/client/src/features/dashboardV2/DashboardV2.tsx +++ b/client/src/features/dashboardV2/DashboardV2.tsx @@ -61,7 +61,8 @@ import CreateProjectV2Button from "../projectsV2/new/CreateProjectV2Button"; import GroupShortHandDisplay from "../projectsV2/show/GroupShortHandDisplay"; import ProjectShortHandDisplay from "../projectsV2/show/ProjectShortHandDisplay"; import SearchV2Bar from "../searchV2/components/SearchV2Bar"; -import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; +// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; +import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/api/sessionsV2.api"; import { useGetUserQuery } from "../usersV2/api/users.api"; import UserAvatar from "../usersV2/show/UserAvatar"; import DashboardV2Sessions from "./DashboardV2Sessions"; diff --git a/client/src/features/session/components/SessionsList.tsx b/client/src/features/session/components/SessionsList.tsx index 245426a67f..d9d0f8d113 100644 --- a/client/src/features/session/components/SessionsList.tsx +++ b/client/src/features/session/components/SessionsList.tsx @@ -315,10 +315,10 @@ function SessionRowProject({ annotations }: SessionRowProjectProps) { export interface SessionLauncherResources { name?: string; - cpu: number; - memory: number; - gpu: number; - storage: number; + cpu?: number; + memory?: number; + gpu?: number; + storage?: number; } interface SessionRowResourceRequestsProps { resourceRequests: Session["resources"]["requests"] | SessionLauncherResources; diff --git a/client/src/features/session/useWaitForSessionStatus.hook.ts b/client/src/features/session/useWaitForSessionStatus.hook.ts index 8470e57134..c88a267a44 100644 --- a/client/src/features/session/useWaitForSessionStatus.hook.ts +++ b/client/src/features/session/useWaitForSessionStatus.hook.ts @@ -18,7 +18,8 @@ import { useEffect, useMemo, useState } from "react"; import { useGetSessionsQuery } from "./sessions.api"; -import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; +// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; +import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/api/sessionsV2.api"; import { SessionStatusState } from "./sessions.types"; const DEFAULT_POLLING_INTERVAL_MS = 5_000; diff --git a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx index 8a519c1743..31a8a4282d 100644 --- a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx +++ b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx @@ -20,7 +20,8 @@ import { useCallback, useMemo } from "react"; import useLocationHash from "../../../utils/customHooks/useLocationHash.hook"; import { Project } from "../../projectsV2/api/projectV2.api"; -import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2.api"; +// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2.api"; +import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../api/sessionsV2.api"; import { SessionView } from "../SessionView/SessionView"; import { SessionLauncher } from "../sessionsV2.types"; import SessionItem from "./SessionItem"; diff --git a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx index 4d267c7ada..29b9d2a822 100644 --- a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx +++ b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx @@ -63,8 +63,9 @@ import PauseOrDeleteSessionModal from "../PauseOrDeleteSessionModal"; import { getSessionFavicon } from "../session.utils"; import { useGetProjectSessionLaunchersQuery, - useGetSessionsQuery, + // useGetSessionsQuery, } from "../sessionsV2.api"; +import { useGetSessionsQuery } from "../api/sessionsV2.api"; import { SessionV2 } from "../sessionsV2.types"; import SessionIframe from "./SessionIframe"; import SessionPaused from "./SessionPaused"; @@ -432,18 +433,20 @@ function SessionDetails({

-
-

- - - - -

-
+ {session.started && ( +
+

+ + + + +

+
+ )}
({ + getNotebooksImages: build.query< + GetNotebooksImagesApiResponse, + GetNotebooksImagesApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/images`, + params: { image_url: queryArg.imageUrl }, + }), + }), + getNotebooksLogsByServerName: build.query< + GetNotebooksLogsByServerNameApiResponse, + GetNotebooksLogsByServerNameApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/logs/${queryArg.serverName}`, + params: { max_lines: queryArg.maxLines }, + }), + }), + getNotebooksServerOptions: build.query< + GetNotebooksServerOptionsApiResponse, + GetNotebooksServerOptionsApiArg + >({ + query: () => ({ url: `/notebooks/server_options` }), + }), + postNotebooksServers: build.mutation< + PostNotebooksServersApiResponse, + PostNotebooksServersApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/servers`, + method: "POST", + body: queryArg.launchNotebookRequestOld, + }), + }), + getNotebooksServers: build.query< + GetNotebooksServersApiResponse, + GetNotebooksServersApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/servers`, + params: { + project: queryArg.project, + commit_sha: queryArg.commitSha, + namespace: queryArg["namespace"], + branch: queryArg.branch, + }, + }), + }), + deleteNotebooksServersByServerName: build.mutation< + DeleteNotebooksServersByServerNameApiResponse, + DeleteNotebooksServersByServerNameApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/servers/${queryArg.serverName}`, + method: "DELETE", + params: { forced: queryArg.forced }, + }), + }), + getNotebooksServersByServerName: build.query< + GetNotebooksServersByServerNameApiResponse, + GetNotebooksServersByServerNameApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/servers/${queryArg.serverName}`, + }), + }), + patchNotebooksServersByServerName: build.mutation< + PatchNotebooksServersByServerNameApiResponse, + PatchNotebooksServersByServerNameApiArg + >({ + query: (queryArg) => ({ + url: `/notebooks/servers/${queryArg.serverName}`, + method: "PATCH", + body: queryArg.patchServerRequest, + }), + }), + postSessions: build.mutation({ + query: (queryArg) => ({ + url: `/sessions`, + method: "POST", + body: queryArg.sessionPostRequest, + }), + }), + getSessions: build.query({ + query: () => ({ url: `/sessions` }), + }), + getSessionsBySessionId: build.query< + GetSessionsBySessionIdApiResponse, + GetSessionsBySessionIdApiArg + >({ + query: (queryArg) => ({ url: `/sessions/${queryArg.sessionId}` }), + }), + deleteSessionsBySessionId: build.mutation< + DeleteSessionsBySessionIdApiResponse, + DeleteSessionsBySessionIdApiArg + >({ + query: (queryArg) => ({ + url: `/sessions/${queryArg.sessionId}`, + method: "DELETE", + }), + }), + patchSessionsBySessionId: build.mutation< + PatchSessionsBySessionIdApiResponse, + PatchSessionsBySessionIdApiArg + >({ + query: (queryArg) => ({ + url: `/sessions/${queryArg.sessionId}`, + method: "PATCH", + body: queryArg.sessionPatchRequest, + }), + }), + getSessionsBySessionIdLogs: build.query< + GetSessionsBySessionIdLogsApiResponse, + GetSessionsBySessionIdLogsApiArg + >({ + query: (queryArg) => ({ + url: `/sessions/${queryArg.sessionId}/logs`, + params: { max_lines: queryArg.maxLines }, + }), + }), + getSessionsImages: build.query< + GetSessionsImagesApiResponse, + GetSessionsImagesApiArg + >({ + query: (queryArg) => ({ + url: `/sessions/images`, + params: { image_url: queryArg.imageUrl }, + }), + }), + }), + overrideExisting: false, +}); +export { injectedRtkApi as sessionsV2GeneratedApi }; +export type GetNotebooksImagesApiResponse = unknown; +export type GetNotebooksImagesApiArg = { + /** The Docker image URL (tag included) that should be fetched. */ + imageUrl: string; +}; +export type GetNotebooksLogsByServerNameApiResponse = + /** status 200 Server logs. An array of strings where each element is a line of the logs. */ ServerLogs; +export type GetNotebooksLogsByServerNameApiArg = { + /** The name of the server whose logs should be fetched. */ + serverName: ServerName; + /** The maximum number of (most recent) lines to return from the logs. */ + maxLines?: number; +}; +export type GetNotebooksServerOptionsApiResponse = + /** status 200 Server options such as CPU, memory, storage, etc. */ ServerOptionsEndpointResponse; +export type GetNotebooksServerOptionsApiArg = void; +export type PostNotebooksServersApiResponse = + /** status 201 The project was created */ NotebookResponse; +export type PostNotebooksServersApiArg = { + launchNotebookRequestOld: LaunchNotebookRequestOld; +}; +export type GetNotebooksServersApiResponse = + /** status 200 Map of all servers for a user. */ ServersGetResponse; +export type GetNotebooksServersApiArg = { + project?: string; + commitSha?: string; + namespace?: string; + branch?: string; +}; +export type DeleteNotebooksServersByServerNameApiResponse = + /** status 204 The server was stopped successfully. */ void; +export type DeleteNotebooksServersByServerNameApiArg = { + /** The name of the server that should be deleted. */ + serverName: ServerName; + /** If true, delete immediately disregarding the grace period + of the underlying JupyterServer resource. + */ + forced?: boolean; +}; +export type GetNotebooksServersByServerNameApiResponse = + /** status 200 Server properties. */ NotebookResponse; +export type GetNotebooksServersByServerNameApiArg = { + /** The name of the server for which additional information is required. */ + serverName: ServerName; +}; +export type PatchNotebooksServersByServerNameApiResponse = + /** status 200 The server was patched successfully. */ NotebookResponse; +export type PatchNotebooksServersByServerNameApiArg = { + /** The name of the server that should be patched. */ + serverName: ServerName; + patchServerRequest: PatchServerRequest; +}; +export type PostSessionsApiResponse = + /** status 200 The session already exists */ + SessionResponse | /** status 201 The session was created */ SessionResponse; +export type PostSessionsApiArg = { + sessionPostRequest: SessionPostRequest; +}; +export type GetSessionsApiResponse = + /** status 200 Information about the sessions */ SessionListResponse; +export type GetSessionsApiArg = void; +export type GetSessionsBySessionIdApiResponse = + /** status 200 Information about the session */ SessionResponse; +export type GetSessionsBySessionIdApiArg = { + /** The id of the session */ + sessionId: string; +}; +export type DeleteSessionsBySessionIdApiResponse = + /** status 204 The session was deleted or it never existed in the first place */ void; +export type DeleteSessionsBySessionIdApiArg = { + /** The id of the session that should be deleted */ + sessionId: string; +}; +export type PatchSessionsBySessionIdApiResponse = + /** status 200 The session was patched */ SessionResponse; +export type PatchSessionsBySessionIdApiArg = { + /** The id of the session */ + sessionId: string; + sessionPatchRequest: SessionPatchRequest; +}; +export type GetSessionsBySessionIdLogsApiResponse = + /** status 200 The session logs */ SessionLogsResponse; +export type GetSessionsBySessionIdLogsApiArg = { + /** The id of the session */ + sessionId: string; + /** The maximum number of most-recent lines to return for each container */ + maxLines?: number; +}; +export type GetSessionsImagesApiResponse = + /** status 200 The docker image can be found */ void; +export type GetSessionsImagesApiArg = { + /** The Docker image URL (tag included) that should be fetched. */ + imageUrl: string; +}; +export type ServerLogs = { + "jupyter-server"?: string; + [key: string]: any; +}; +export type ErrorResponse = { + error: { + code: number; + detail?: string; + message: string; + }; +}; +export type ServerName = string; +export type Generated = { + enabled: boolean; +}; +export type StringServerOptionsChoice = { + default: string; + displayName: string; + options?: string[]; + order: number; + type: "enum" | "boolean"; +}; +export type BoolServerOptionsChoice = { + default: boolean; + displayName: string; + order: number; + type: "enum" | "boolean"; +}; +export type ServerOptionsEndpointResponse = { + cloudstorage: Generated; + defaultUrl?: StringServerOptionsChoice; + lfs_auto_fetch?: BoolServerOptionsChoice; +}; +export type UserPodAnnotations = { + "jupyter.org/servername"?: string; + "jupyter.org/username"?: string; + "renku.io/branch": string; + "renku.io/commit-sha": string; + "renku.io/default_image_used": string; + "renku.io/git-host"?: string; + "renku.io/gitlabProjectId"?: string; + "renku.io/hibernatedSecondsThreshold"?: string; + "renku.io/hibernation"?: string; + "renku.io/hibernationBranch"?: string; + "renku.io/hibernationCommitSha"?: string; + "renku.io/hibernationDate"?: string; + "renku.io/hibernationDirty"?: string; + "renku.io/hibernationSynchronized"?: string; + "renku.io/idleSecondsThreshold"?: string; + "renku.io/lastActivityDate"?: string; + "renku.io/launcherId"?: string; + "renku.io/namespace": string; + "renku.io/projectId"?: string; + "renku.io/projectName": string; + "renku.io/renkuVersion"?: string; + "renku.io/repository": string; + "renku.io/resourceClassId"?: string; + "renku.io/servername"?: string; + "renku.io/username"?: string; + [key: string]: any; +}; +export type LaunchNotebookResponseCloudStorage = { + mount_folder?: any; + remote?: any; + type?: any; +}; +export type ResourceRequests = { + cpu: any; + gpu?: any; + memory: any; + storage?: any; +}; +export type ResourceUsage = { + cpu?: any; + memory?: any; + storage?: any; +}; +export type UserPodResources = { + requests?: ResourceRequests; + usage?: ResourceUsage; +}; +export type ServerStatusDetail = { + status: "ready" | "waiting" | "executing" | "failed"; + step: string; +}; +export type ServerStatusWarning = { + critical?: boolean; + message: string; +}; +export type ServerStatus = { + details: ServerStatusDetail[]; + message?: string; + readyNumContainers: number; + state: "running" | "starting" | "stopping" | "failed" | "hibernated"; + totalNumContainers: number; + warnings?: ServerStatusWarning[]; +}; +export type NotebookResponse = { + annotations?: UserPodAnnotations; + cloudstorage?: LaunchNotebookResponseCloudStorage[]; + image?: string; + name?: ServerName; + resources?: UserPodResources; + started?: string | null; + state?: object; + status?: ServerStatus; + url?: string; +}; +export type RCloneStorageRequest = { + configuration?: { + [key: string]: any; + } | null; + readonly?: boolean; + source_path?: string; + storage_id?: string | null; + target_path?: string; +}; +export type LaunchNotebookRequestServerOptions = { + cpu_request?: any; + defaultUrl?: string; + disk_request?: any; + gpu_request?: any; + lfs_auto_fetch?: boolean; + mem_request?: any; +}; +export type UserSecrets = { + mount_path: any; + user_secret_ids: any[]; +}; +export type LaunchNotebookRequestOld = { + branch?: string; + cloudstorage?: RCloneStorageRequest[]; + commit_sha: string; + default_url?: string; + environment_variables?: { + [key: string]: string; + }; + image?: string | null; + lfs_auto_fetch?: boolean; + namespace: string; + notebook?: string | null; + project: string; + resource_class_id?: number | null; + serverOptions?: LaunchNotebookRequestServerOptions; + storage?: number; + user_secrets?: UserSecrets | null; +}; +export type ServersGetResponse = { + servers?: { + [key: string]: NotebookResponse; + }; +}; +export type PatchServerRequest = { + resource_class_id?: number; + state?: "running" | "hibernated"; +}; +export type SessionResourcesRequests = { + /** Fractional CPUs */ + cpu?: number; + /** Number of GPUs used */ + gpu?: number; + /** Ammount of RAM for the session, in gigabytes */ + memory?: number; + /** The size of disk storage for the session, in gigabytes */ + storage?: number; +}; +export type SessionResources = { + requests?: SessionResourcesRequests; +}; +export type SessionStatus = { + message?: string; + state: "running" | "starting" | "stopping" | "failed" | "hibernated"; + will_hibernate_at?: string | null; + will_delete_at?: string | null; + ready_containers: number; + total_containers: number; +}; +export type Ulid = string; +export type SessionResponse = { + image: string; + name: ServerName; + resources: SessionResources; + started: string | null; + status: SessionStatus; + url: string; + project_id: Ulid; + launcher_id: Ulid; + resource_class_id: number; +}; +export type SessionCloudStoragePost = { + configuration?: { + [key: string]: any; + }; + readonly?: boolean; + source_path?: string; + target_path?: string; + storage_id: Ulid & any; +}; +export type SessionCloudStoragePostList = SessionCloudStoragePost[]; +export type SessionPostRequest = { + launcher_id: Ulid; + /** The size of disk storage for the session, in gigabytes */ + disk_storage?: number; + resource_class_id?: number | null; + cloudstorage?: SessionCloudStoragePostList; +}; +export type SessionListResponse = SessionResponse[]; +export type SessionPatchRequest = { + resource_class_id?: number; + state?: "running" | "hibernated"; +}; +export type SessionLogsResponse = { + [key: string]: string; +}; +export const { + useGetNotebooksImagesQuery, + useGetNotebooksLogsByServerNameQuery, + useGetNotebooksServerOptionsQuery, + usePostNotebooksServersMutation, + useGetNotebooksServersQuery, + useDeleteNotebooksServersByServerNameMutation, + useGetNotebooksServersByServerNameQuery, + usePatchNotebooksServersByServerNameMutation, + usePostSessionsMutation, + useGetSessionsQuery, + useGetSessionsBySessionIdQuery, + useDeleteSessionsBySessionIdMutation, + usePatchSessionsBySessionIdMutation, + useGetSessionsBySessionIdLogsQuery, + useGetSessionsImagesQuery, +} = injectedRtkApi; diff --git a/client/src/features/sessionsV2/api/sessionsV2.openapi.json b/client/src/features/sessionsV2/api/sessionsV2.openapi.json new file mode 100644 index 0000000000..c57a70f1cc --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionsV2.openapi.json @@ -0,0 +1,1420 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Renku Data Services API", + "description": "Service that allows creating, updating, deleting, and managing Renku user sessions.\nAll errors have the same format as the schema called ErrorResponse.\n", + "version": "v1" + }, + "servers": [ + { + "url": "/api/data" + } + ], + "paths": { + "/notebooks/images": { + "get": { + "description": "Docker image availability.", + "parameters": [ + { + "description": "The Docker image URL (tag included) that should be fetched.", + "in": "query", + "name": "image_url", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "responses": { + "200": { + "description": "The Docker image is available." + }, + "404": { + "description": "The Docker image is not available." + } + }, + "tags": ["notebooks"] + } + }, + "/notebooks/logs/{server_name}": { + "get": { + "description": "Server logs.", + "parameters": [ + { + "description": "The name of the server whose logs should be fetched.", + "in": "path", + "name": "server_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/ServerName" + } + }, + { + "description": "The maximum number of (most recent) lines to return from the logs.", + "in": "query", + "name": "max_lines", + "required": false, + "schema": { + "default": 250, + "minimum": 0, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerLogs" + } + } + }, + "description": "Server logs. An array of strings where each element is a line of the logs." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The specified server does not exist." + } + }, + "tags": ["notebooks"] + } + }, + "/notebooks/server_options": { + "get": { + "description": "Get the options available to customize when starting a server.", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerOptionsEndpointResponse" + } + } + }, + "description": "Server options such as CPU, memory, storage, etc." + } + }, + "tags": ["notebooks"] + } + }, + "/notebooks/servers": { + "post": { + "summary": "Launch a new session", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchNotebookRequestOld" + } + } + } + }, + "responses": { + "201": { + "description": "The project was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotebookResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["notebooks"] + }, + "get": { + "description": "Information about all active servers for a user.", + "parameters": [ + { + "in": "query", + "name": "project", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "commit_sha", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "namespace", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "branch", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServersGetResponse" + } + } + }, + "description": "Map of all servers for a user." + } + }, + "tags": ["notebooks"] + } + }, + "/notebooks/servers/{server_name}": { + "delete": { + "description": "Stop a running server by name.", + "parameters": [ + { + "description": "The name of the server that should be deleted.", + "in": "path", + "name": "server_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/ServerName" + } + }, + { + "description": "If true, delete immediately disregarding the grace period\nof the underlying JupyterServer resource.\n", + "in": "query", + "name": "forced", + "required": false, + "schema": { + "default": false, + "type": "boolean" + } + } + ], + "responses": { + "204": { + "description": "The server was stopped successfully." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The server cannot be found." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The server exists but could not be successfully deleted." + } + }, + "tags": ["notebooks"] + }, + "get": { + "description": "Information about an active server.", + "parameters": [ + { + "description": "The name of the server for which additional information is required.", + "in": "path", + "name": "server_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/ServerName" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotebookResponse" + } + } + }, + "description": "Server properties." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The specified server does not exist." + } + }, + "tags": ["notebooks"] + }, + "patch": { + "description": "Patch a running server by name.", + "parameters": [ + { + "description": "The name of the server that should be patched.", + "in": "path", + "name": "server_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/ServerName" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchServerRequest" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotebookResponse" + } + } + }, + "description": "The server was patched successfully." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Invalid json argument value." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The server cannot be found." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "The server exists but could not be successfully hibernated." + } + }, + "tags": ["notebooks"] + } + }, + "/sessions": { + "post": { + "summary": "Launch a new session", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionPostRequest" + } + } + } + }, + "responses": { + "200": { + "description": "The session already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + } + }, + "201": { + "description": "The session was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + }, + "get": { + "summary": "Get a list of all sessions for a user", + "responses": { + "200": { + "description": "Information about the sessions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionListResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + } + }, + "/sessions/{session_id}": { + "get": { + "summary": "Get information about a specific session", + "parameters": [ + { + "description": "The id of the session", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Information about the session", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + }, + "delete": { + "parameters": [ + { + "description": "The id of the session that should be deleted", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "summary": "Fully remove a session", + "responses": { + "204": { + "description": "The session was deleted or it never existed in the first place" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + }, + "patch": { + "summary": "Patch a session", + "parameters": [ + { + "description": "The id of the session", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "The session was patched", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + } + }, + "/sessions/{session_id}/logs": { + "get": { + "summary": "Get all logs from a specific session", + "parameters": [ + { + "description": "The id of the session", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The maximum number of most-recent lines to return for each container", + "in": "query", + "name": "max_lines", + "required": false, + "schema": { + "type": "integer", + "default": 250 + } + } + ], + "responses": { + "200": { + "description": "The session logs", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLogsResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + } + }, + "/sessions/images": { + "get": { + "summary": "Check if a session image exists", + "parameters": [ + { + "description": "The Docker image URL (tag included) that should be fetched.", + "in": "query", + "name": "image_url", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "responses": { + "200": { + "description": "The docker image can be found" + }, + "404": { + "description": "The docker image cannot be found or the user does not have permissions to access it", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["sessions"] + } + } + }, + "components": { + "schemas": { + "BoolServerOptionsChoice": { + "properties": { + "default": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "type": { + "enum": ["enum", "boolean"], + "type": "string" + } + }, + "required": ["default", "displayName", "order", "type"], + "type": "object" + }, + "CullingThreshold": { + "properties": { + "hibernation": { + "type": "integer" + }, + "idle": { + "type": "integer" + } + }, + "required": ["hibernation", "idle"], + "type": "object" + }, + "DefaultCullingThresholds": { + "properties": { + "anonymous": { + "$ref": "#/components/schemas/CullingThreshold" + }, + "registered": { + "$ref": "#/components/schemas/CullingThreshold" + } + }, + "required": ["anonymous", "registered"], + "type": "object" + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "example": 1404 + }, + "detail": { + "type": "string", + "example": "A more detailed optional message showing what the problem was" + }, + "message": { + "type": "string", + "example": "Something went wrong - please try again later" + } + }, + "required": ["code", "message"] + } + }, + "required": ["error"] + }, + "Generated": { + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"], + "type": "object" + }, + "LaunchNotebookRequest": { + "properties": { + "project_id": { + "type": "string" + }, + "launcher_id": { + "type": "string" + }, + "image": { + "type": "string" + }, + "repositories": { + "type": "array", + "default": [], + "items": { + "$ref": "#/components/schemas/LaunchNotebookRequestRepository" + } + }, + "cloudstorage": { + "default": [], + "items": { + "$ref": "#/components/schemas/RCloneStorageRequest" + }, + "type": "array" + }, + "storage": { + "default": 1, + "type": "integer" + }, + "resource_class_id": { + "default": null, + "nullable": true, + "type": "integer" + }, + "environment_variables": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "type": "object" + }, + "user_secrets": { + "allOf": [ + { + "$ref": "#/components/schemas/UserSecrets" + } + ], + "default": null, + "nullable": true + } + }, + "required": ["project_id", "launcher_id"], + "type": "object" + }, + "LaunchNotebookRequestRepository": { + "properties": { + "url": { + "type": "string" + }, + "dirname": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "commit_sha": { + "type": "string" + } + }, + "required": ["url"] + }, + "LaunchNotebookRequestOld": { + "properties": { + "branch": { + "default": "master", + "type": "string" + }, + "cloudstorage": { + "default": [], + "items": { + "$ref": "#/components/schemas/RCloneStorageRequest" + }, + "type": "array" + }, + "commit_sha": { + "type": "string" + }, + "default_url": { + "default": "/lab", + "type": "string" + }, + "environment_variables": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "type": "object" + }, + "image": { + "default": null, + "nullable": true, + "type": "string" + }, + "lfs_auto_fetch": { + "default": false, + "type": "boolean" + }, + "namespace": { + "type": "string" + }, + "notebook": { + "default": null, + "nullable": true, + "type": "string" + }, + "project": { + "type": "string" + }, + "resource_class_id": { + "default": null, + "nullable": true, + "type": "integer" + }, + "serverOptions": { + "$ref": "#/components/schemas/LaunchNotebookRequestServerOptions" + }, + "storage": { + "default": 1, + "type": "integer" + }, + "user_secrets": { + "allOf": [ + { + "$ref": "#/components/schemas/UserSecrets" + } + ], + "default": null, + "nullable": true + } + }, + "required": ["commit_sha", "namespace", "project"], + "type": "object" + }, + "LaunchNotebookRequestServerOptions": { + "properties": { + "cpu_request": { + "default": 0 + }, + "defaultUrl": { + "default": "/lab", + "type": "string" + }, + "disk_request": { + "default": "1G" + }, + "gpu_request": { + "default": 0 + }, + "lfs_auto_fetch": { + "default": false, + "type": "boolean" + }, + "mem_request": { + "default": "0G" + } + }, + "type": "object" + }, + "LaunchNotebookResponseCloudStorage": { + "properties": { + "mount_folder": {}, + "remote": {}, + "type": {} + }, + "type": "object" + }, + "NotebookResponse": { + "properties": { + "annotations": { + "$ref": "#/components/schemas/_UserPodAnnotations" + }, + "cloudstorage": { + "items": { + "$ref": "#/components/schemas/LaunchNotebookResponseCloudStorage" + }, + "type": "array" + }, + "image": { + "type": "string" + }, + "name": { + "$ref": "#/components/schemas/ServerName" + }, + "resources": { + "$ref": "#/components/schemas/UserPodResources" + }, + "started": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "state": { + "type": "object" + }, + "status": { + "$ref": "#/components/schemas/ServerStatus" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "NotebooksServiceInfo": { + "properties": { + "anonymousSessionsEnabled": { + "type": "boolean" + }, + "cloudstorageEnabled": { + "type": "boolean" + }, + "defaultCullingThresholds": { + "$ref": "#/components/schemas/DefaultCullingThresholds" + }, + "sshEnabled": { + "type": "boolean" + } + }, + "required": [ + "anonymousSessionsEnabled", + "cloudstorageEnabled", + "defaultCullingThresholds", + "sshEnabled" + ], + "type": "object" + }, + "NotebooksServiceVersions": { + "properties": { + "data": { + "$ref": "#/components/schemas/NotebooksServiceInfo" + }, + "version": { + "type": "string" + } + }, + "required": ["data", "version"], + "type": "object" + }, + "PatchServerRequest": { + "properties": { + "resource_class_id": { + "type": "integer" + }, + "state": { + "enum": ["running", "hibernated"], + "type": "string" + } + }, + "type": "object" + }, + "RCloneStorageRequest": { + "properties": { + "configuration": { + "additionalProperties": {}, + "default": null, + "nullable": true, + "type": "object" + }, + "readonly": { + "default": true, + "type": "boolean" + }, + "source_path": { + "type": "string" + }, + "storage_id": { + "default": null, + "nullable": true, + "type": "string" + }, + "target_path": { + "type": "string" + } + }, + "type": "object" + }, + "ResourceRequests": { + "properties": { + "cpu": {}, + "gpu": {}, + "memory": {}, + "storage": {} + }, + "required": ["cpu", "memory"], + "type": "object" + }, + "ResourceUsage": { + "properties": { + "cpu": {}, + "memory": {}, + "storage": {} + }, + "type": "object" + }, + "ServerLogs": { + "additionalProperties": true, + "properties": { + "jupyter-server": { + "type": "string" + } + }, + "type": "object" + }, + "ServerOptionsEndpointResponse": { + "properties": { + "cloudstorage": { + "$ref": "#/components/schemas/Generated" + }, + "defaultUrl": { + "$ref": "#/components/schemas/StringServerOptionsChoice" + }, + "lfs_auto_fetch": { + "$ref": "#/components/schemas/BoolServerOptionsChoice" + } + }, + "required": ["cloudstorage"], + "type": "object" + }, + "ServerStatus": { + "properties": { + "details": { + "items": { + "$ref": "#/components/schemas/ServerStatusDetail" + }, + "type": "array" + }, + "message": { + "type": "string" + }, + "readyNumContainers": { + "minimum": 0, + "type": "integer" + }, + "state": { + "enum": ["running", "starting", "stopping", "failed", "hibernated"], + "type": "string" + }, + "totalNumContainers": { + "minimum": 0, + "type": "integer" + }, + "warnings": { + "items": { + "$ref": "#/components/schemas/ServerStatusWarning" + }, + "type": "array" + } + }, + "required": [ + "details", + "readyNumContainers", + "state", + "totalNumContainers" + ], + "type": "object" + }, + "ServerStatusDetail": { + "properties": { + "status": { + "enum": ["ready", "waiting", "executing", "failed"], + "type": "string" + }, + "step": { + "type": "string" + } + }, + "required": ["status", "step"], + "type": "object" + }, + "ServerStatusWarning": { + "properties": { + "critical": { + "default": false, + "type": "boolean" + }, + "message": { + "type": "string" + } + }, + "required": ["message"], + "type": "object" + }, + "ServersGetResponse": { + "properties": { + "servers": { + "additionalProperties": { + "$ref": "#/components/schemas/NotebookResponse" + }, + "type": "object" + } + }, + "type": "object" + }, + "StringServerOptionsChoice": { + "properties": { + "default": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "options": { + "items": { + "type": "string" + }, + "type": "array" + }, + "order": { + "type": "integer" + }, + "type": { + "enum": ["enum", "boolean"], + "type": "string" + } + }, + "required": ["default", "displayName", "order", "type"], + "type": "object" + }, + "UserPodResources": { + "properties": { + "requests": { + "$ref": "#/components/schemas/ResourceRequests" + }, + "usage": { + "$ref": "#/components/schemas/ResourceUsage" + } + }, + "type": "object" + }, + "UserSecrets": { + "properties": { + "mount_path": {}, + "user_secret_ids": { + "items": {}, + "type": "array" + } + }, + "required": ["mount_path", "user_secret_ids"], + "type": "object" + }, + "_UserPodAnnotations": { + "additionalProperties": true, + "properties": { + "jupyter.org/servername": { + "type": "string" + }, + "jupyter.org/username": { + "type": "string" + }, + "renku.io/branch": { + "type": "string" + }, + "renku.io/commit-sha": { + "type": "string" + }, + "renku.io/default_image_used": { + "type": "string" + }, + "renku.io/git-host": { + "type": "string" + }, + "renku.io/gitlabProjectId": { + "type": "string" + }, + "renku.io/hibernatedSecondsThreshold": { + "type": "string" + }, + "renku.io/hibernation": { + "type": "string" + }, + "renku.io/hibernationBranch": { + "type": "string" + }, + "renku.io/hibernationCommitSha": { + "type": "string" + }, + "renku.io/hibernationDate": { + "type": "string" + }, + "renku.io/hibernationDirty": { + "type": "string" + }, + "renku.io/hibernationSynchronized": { + "type": "string" + }, + "renku.io/idleSecondsThreshold": { + "type": "string" + }, + "renku.io/lastActivityDate": { + "type": "string" + }, + "renku.io/launcherId": { + "type": "string" + }, + "renku.io/namespace": { + "type": "string" + }, + "renku.io/projectId": { + "type": "string" + }, + "renku.io/projectName": { + "type": "string" + }, + "renku.io/renkuVersion": { + "type": "string" + }, + "renku.io/repository": { + "type": "string" + }, + "renku.io/resourceClassId": { + "type": "string" + }, + "renku.io/servername": { + "type": "string" + }, + "renku.io/username": { + "type": "string" + } + }, + "required": [ + "renku.io/branch", + "renku.io/commit-sha", + "renku.io/default_image_used", + "renku.io/namespace", + "renku.io/projectName", + "renku.io/repository" + ], + "type": "object" + }, + "SessionPostRequest": { + "properties": { + "launcher_id": { + "$ref": "#/components/schemas/Ulid" + }, + "disk_storage": { + "default": 1, + "type": "integer", + "description": "The size of disk storage for the session, in gigabytes" + }, + "resource_class_id": { + "default": null, + "nullable": true, + "type": "integer" + }, + "cloudstorage": { + "$ref": "#/components/schemas/SessionCloudStoragePostList" + } + }, + "required": ["launcher_id"], + "type": "object" + }, + "SessionResponse": { + "properties": { + "image": { + "type": "string" + }, + "name": { + "$ref": "#/components/schemas/ServerName" + }, + "resources": { + "$ref": "#/components/schemas/SessionResources" + }, + "started": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/SessionStatus" + }, + "url": { + "type": "string" + }, + "project_id": { + "$ref": "#/components/schemas/Ulid" + }, + "launcher_id": { + "$ref": "#/components/schemas/Ulid" + }, + "resource_class_id": { + "type": "integer" + } + }, + "required": [ + "image", + "name", + "resources", + "started", + "status", + "url", + "project_id", + "launcher_id", + "resource_class_id" + ], + "type": "object" + }, + "SessionListResponse": { + "items": { + "$ref": "#/components/schemas/SessionResponse" + }, + "type": "array" + }, + "SessionPatchRequest": { + "properties": { + "resource_class_id": { + "type": "integer" + }, + "state": { + "enum": ["running", "hibernated"], + "type": "string" + } + } + }, + "SessionStatus": { + "properties": { + "message": { + "type": "string" + }, + "state": { + "enum": ["running", "starting", "stopping", "failed", "hibernated"], + "type": "string" + }, + "will_hibernate_at": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "will_delete_at": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "ready_containers": { + "type": "integer", + "minimum": 0 + }, + "total_containers": { + "type": "integer", + "minimum": 0 + } + }, + "required": ["state", "ready_containers", "total_containers"], + "type": "object" + }, + "SessionResources": { + "properties": { + "requests": { + "$ref": "#/components/schemas/SessionResourcesRequests" + } + }, + "type": "object" + }, + "SessionResourcesRequests": { + "properties": { + "cpu": { + "type": "number", + "description": "Fractional CPUs" + }, + "gpu": { + "type": "integer", + "description": "Number of GPUs used" + }, + "memory": { + "type": "integer", + "description": "Ammount of RAM for the session, in gigabytes" + }, + "storage": { + "type": "integer", + "description": "The size of disk storage for the session, in gigabytes" + } + }, + "example": { + "cpu": 1.5, + "memory": 1, + "storage": 40, + "gpu": 0 + }, + "type": "object" + }, + "SessionLogsResponse": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "container-A": "Log line 1\nLog line 2", + "container-B": "Log line 1\nLog line 2" + } + }, + "Ulid": { + "description": "ULID identifier", + "type": "string", + "minLength": 26, + "maxLength": 26, + "pattern": "^[0-7][0-9A-HJKMNP-TV-Z]{25}$" + }, + "SessionCloudStoragePostList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionCloudStoragePost" + } + }, + "SessionCloudStoragePost": { + "type": "object", + "properties": { + "configuration": { + "type": "object", + "additionalProperties": true + }, + "readonly": { + "type": "boolean" + }, + "source_path": { + "type": "string" + }, + "target_path": { + "type": "string" + }, + "storage_id": { + "allOf": [ + { + "$ref": "#/components/schemas/Ulid" + }, + { + "description": "If the storage_id is provided then this config must replace an existing storage config in the session" + } + ] + } + }, + "required": ["storage_id"] + }, + "ServerName": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "pattern": "^[a-z]([-a-z0-9]*[a-z0-9])?$", + "example": "d185e68d-d43-renku-2-b9ac279a4e8a85ac28d08" + } + }, + "responses": { + "Error": { + "description": "The schema for all 4xx and 5xx responses", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } +} diff --git a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx index a57bc65d8f..cf0b066f42 100644 --- a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx +++ b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx @@ -476,7 +476,7 @@ interface ModifySessionModalProps { resources: SessionResources; status: SessionStatus; toggleModal: () => void; - resource_class_id: string; + resource_class_id: number; } function ModifySessionModal({ @@ -512,7 +512,7 @@ interface ModifySessionModalContentProps { resources: SessionResources; status: SessionStatus; toggleModal: () => void; - resource_class_id: string; + resource_class_id: number; } function ModifySessionModalContent({ @@ -556,7 +556,7 @@ function ModifySessionModalContent({ useEffect(() => { const currentSessionClass = resourcePools ?.flatMap((pool) => pool.classes) - .find((c) => `${c.id}` == resource_class_id); + .find((c) => c.id == resource_class_id); setCurrentSessionClass(currentSessionClass); }, [resource_class_id, resourcePools]); @@ -614,7 +614,7 @@ function ModifySessionModalContent({ resourcePools.length == 0 || isError || currentSessionClass == null || - resource_class_id === `${currentSessionClass?.id}` + resource_class_id === currentSessionClass?.id } onClick={onClick({ resumeSession: true })} type="submit" @@ -632,7 +632,7 @@ function ModifySessionModalContent({ isError || currentSessionClass == null || (resource_class_id != null && - resource_class_id === `${currentSessionClass?.id}`) + resource_class_id === currentSessionClass?.id) } onClick={onClick({ resumeSession: false })} type="submit" diff --git a/client/src/features/sessionsV2/components/SessionStatus/SessionStatus.tsx b/client/src/features/sessionsV2/components/SessionStatus/SessionStatus.tsx index bc03c427fd..f3bf50b914 100644 --- a/client/src/features/sessionsV2/components/SessionStatus/SessionStatus.tsx +++ b/client/src/features/sessionsV2/components/SessionStatus/SessionStatus.tsx @@ -134,7 +134,9 @@ export function SessionStatusV2Description({ "align-items-center" )} > - + {started && ( + + )} {showInfoDetails && ( )} diff --git a/client/src/features/sessionsV2/sessionsV2.api.ts b/client/src/features/sessionsV2/sessionsV2.api.ts index 003793113c..1a64cb46e5 100644 --- a/client/src/features/sessionsV2/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/sessionsV2.api.ts @@ -216,7 +216,7 @@ const sessionsV2Api = createApi({ export default sessionsV2Api; export const { - useGetSessionsQuery, + // useGetSessionsQuery, useGetSessionEnvironmentsQuery, useGetSessionLaunchersQuery, useGetProjectSessionLauncherQuery, diff --git a/client/src/features/sessionsV2/sessionsV2.types.ts b/client/src/features/sessionsV2/sessionsV2.types.ts index d111671ae0..84f8990489 100644 --- a/client/src/features/sessionsV2/sessionsV2.types.ts +++ b/client/src/features/sessionsV2/sessionsV2.types.ts @@ -135,19 +135,19 @@ export interface SessionLauncherForm { } export interface SessionResources { - requests: { - cpu: number; - gpu: number; - memory: number; - storage: number; + requests?: { + cpu?: number; + gpu?: number; + memory?: number; + storage?: number; }; } export interface SessionStatus { message?: string; state: "running" | "starting" | "stopping" | "failed" | "hibernated"; - will_hibernate_at?: string; - will_delete_at?: string; + will_hibernate_at?: string | null; + will_delete_at?: string | null; ready_containers: number; total_containers: number; } @@ -157,12 +157,12 @@ export interface SessionV2 { image: string; name: string; resources: SessionResources; - started: string; + started: string | null; status: SessionStatus; url: string; project_id: string; launcher_id: string; - resource_class_id: string; + resource_class_id: number; } export interface SessionCloudStorageV2 { From 59f10413cda23db32dd151cc90744773aba71224 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 15:36:42 +0100 Subject: [PATCH 03/11] more API updates --- client/.eslintignore | 1 + .../sessionsV2/PauseOrDeleteSessionModal.tsx | 15 ++++---- .../SessionShowPage/SessionPaused.tsx | 8 +++-- .../features/sessionsV2/SessionStartPage.tsx | 7 ++-- .../features/sessionsV2/api/sessionsV2.api.ts | 36 +++++++++++++++++-- .../SessionButton/ActiveSessionButton.tsx | 22 +++++++----- .../src/features/sessionsV2/sessionsV2.api.ts | 6 ++-- 7 files changed, 72 insertions(+), 23 deletions(-) diff --git a/client/.eslintignore b/client/.eslintignore index e8891d7569..6eb2d9a87f 100644 --- a/client/.eslintignore +++ b/client/.eslintignore @@ -5,3 +5,4 @@ src/features/projectsV2/api/projectV2.api.ts src/features/projectsV2/api/storagesV2.api.ts src/features/dataConnectorsV2/api/data-connectors.api.ts src/features/usersV2/api/users.generated-api.ts +src/features/sessionsV2/api/sessionsV2.generated-api.ts diff --git a/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx b/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx index debd472ab6..99f6add6ea 100644 --- a/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx +++ b/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx @@ -35,9 +35,9 @@ import useLegacySelector from "../../utils/customHooks/useLegacySelector.hook"; import styles from "../session/components/SessionModals.module.scss"; import { useWaitForSessionStatusV2 } from "../session/useWaitForSessionStatus.hook"; import { - usePatchSessionMutation, - useStopSessionMutation, -} from "../sessionsV2/sessionsV2.api"; + usePatchSessionsBySessionIdMutation as usePatchSessionMutation, + useDeleteSessionsBySessionIdMutation as useStopSessionMutation, +} from "./api/sessionsV2.api"; import { SessionV2 } from "./sessionsV2.types"; import { Loader } from "../../components/Loader"; @@ -107,7 +107,7 @@ function AnonymousDeleteSessionModal({ const [isStopping, setIsStopping] = useState(false); const onStopSession = useCallback(async () => { - stopSession({ session_id: sessionName }); + stopSession({ sessionId: sessionName }); setIsStopping(true); }, [sessionName, stopSession]); @@ -226,7 +226,10 @@ function PauseSessionModalContent({ const [isStopping, setIsStopping] = useState(false); const onHibernateSession = useCallback(async () => { - patchSession({ session_id: sessionName, state: "hibernated" }); + patchSession({ + sessionId: sessionName, + sessionPatchRequest: { state: "hibernated" }, + }); setIsStopping(true); }, [patchSession, sessionName]); @@ -335,7 +338,7 @@ function DeleteSessionModalContent({ const [isStopping, setIsStopping] = useState(false); const onStopSession = useCallback(async () => { - stopSession({ session_id: sessionName }); + stopSession({ sessionId: sessionName }); setIsStopping(true); }, [sessionName, stopSession]); diff --git a/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx b/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx index b1ff102cb9..4b21c73872 100644 --- a/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx +++ b/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx @@ -29,7 +29,8 @@ import { NOTIFICATION_TOPICS } from "../../../notifications/Notifications.consta import type { NotificationsManager } from "../../../notifications/notifications.types"; import { ABSOLUTE_ROUTES } from "../../../routing/routes.constants"; import AppContext from "../../../utils/context/appContext"; -import { usePatchSessionMutation } from "../sessionsV2.api"; +// import { usePatchSessionMutation } from "../sessionsV2.api"; +import { usePatchSessionsBySessionIdMutation as usePatchSessionMutation } from "../api/sessionsV2.api"; import type { SessionV2 } from "../sessionsV2.types"; interface SessionPausedProps { @@ -46,7 +47,10 @@ export default function SessionPaused({ session }: SessionPausedProps) { const [isResuming, setIsResuming] = useState(false); const onResumeSession = useCallback(() => { - patchSession({ session_id: sessionName, state: "running" }); + patchSession({ + sessionId: sessionName, + sessionPatchRequest: { state: "running" }, + }); setIsResuming(true); }, [patchSession, sessionName]); diff --git a/client/src/features/sessionsV2/SessionStartPage.tsx b/client/src/features/sessionsV2/SessionStartPage.tsx index 3658fafc71..78cb602d4f 100644 --- a/client/src/features/sessionsV2/SessionStartPage.tsx +++ b/client/src/features/sessionsV2/SessionStartPage.tsx @@ -61,8 +61,9 @@ import { SelectResourceClassModal } from "./components/SessionModals/SelectResou import { useGetDockerImageQuery, useGetProjectSessionLaunchersQuery, - useLaunchSessionMutation, + // useLaunchSessionMutation, } from "./sessionsV2.api"; +import { usePostSessionsMutation as useLaunchSessionMutation } from "./api/sessionsV2.api"; import { SessionLauncher } from "./sessionsV2.types"; import startSessionOptionsV2Slice from "./startSessionOptionsV2.slice"; import { @@ -224,7 +225,9 @@ function SessionStarting({ launcher, project }: StartSessionFromLauncherProps) { // Request session useEffect(() => { if (isLoadingStartSession || session != null || isError) return; - startSessionV2(launcherToStart); + startSessionV2({ + sessionPostRequest: launcherToStart, + }); dispatch(setFavicon("waiting")); }, [ isLoadingStartSession, diff --git a/client/src/features/sessionsV2/api/sessionsV2.api.ts b/client/src/features/sessionsV2/api/sessionsV2.api.ts index 8d15731888..b3cb527ed2 100644 --- a/client/src/features/sessionsV2/api/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/api/sessionsV2.api.ts @@ -19,8 +19,37 @@ import { sessionsV2GeneratedApi } from "./sessionsV2.generated-api"; export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ - addTagTypes: [], - endpoints: {}, + addTagTypes: ["Session"], + endpoints: { + getSessions: { + providesTags: (result) => + result + ? [ + ...result.map(({ name }) => ({ + id: name, + type: "Session" as const, + })), + "Session", + ] + : ["Session"], + }, + getSessionsBySessionId: { + providesTags: (result) => + result + ? [{ id: result.name, type: "Session" }, "Session"] + : ["Session"], + }, + postSessions: { + invalidatesTags: ["Session"], + }, + patchSessionsBySessionId: { + invalidatesTags: (result) => + result ? [{ id: result.name, type: "Session" }] : ["Session"], + }, + deleteSessionsBySessionId: { + invalidatesTags: ["Session"], + }, + }, }); // useGetNotebooksImagesQuery, @@ -43,6 +72,9 @@ export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ export const { // "sessions" hooks useGetSessionsQuery, + usePostSessionsMutation, + usePatchSessionsBySessionIdMutation, + useDeleteSessionsBySessionIdMutation, } = sessionsV2Api; export type * from "./sessionsV2.generated-api"; diff --git a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx index cf0b066f42..cf16483595 100644 --- a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx +++ b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx @@ -63,9 +63,9 @@ import { SessionClassSelectorV2 } from "../../../session/components/options/Sess import { SessionStatusState } from "../../../session/sessions.types"; import { useWaitForSessionStatusV2 } from "../../../session/useWaitForSessionStatus.hook"; import { - usePatchSessionMutation, - useStopSessionMutation, -} from "../../sessionsV2.api"; + usePatchSessionsBySessionIdMutation as usePatchSessionMutation, + useDeleteSessionsBySessionIdMutation as useStopSessionMutation, +} from "../../api/sessionsV2.api"; import { SessionResources, SessionStatus, @@ -111,7 +111,10 @@ export default function ActiveSessionButton({ { isSuccess: isSuccessResumeSession, error: errorResumeSession }, ] = usePatchSessionMutation(); const onResumeSession = useCallback(() => { - resumeSession({ session_id: session.name, state: "running" }); + resumeSession({ + sessionId: session.name, + sessionPatchRequest: { state: "running" }, + }); setIsResuming(true); }, [resumeSession, session.name]); const { isWaiting: isWaitingForResumedSession } = useWaitForSessionStatusV2({ @@ -147,7 +150,10 @@ export default function ActiveSessionButton({ { isSuccess: isSuccessHibernateSession, error: errorHibernateSession }, ] = usePatchSessionMutation(); const onHibernateSession = useCallback(() => { - hibernateSession({ session_id: session.name, state: "hibernated" }); + hibernateSession({ + sessionId: session.name, + sessionPatchRequest: { state: "hibernated" }, + }); setIsHibernating(true); }, [hibernateSession, session.name]); const { isWaiting: isWaitingForHibernatedSession } = @@ -177,7 +183,7 @@ export default function ActiveSessionButton({ // Optimistically show a session as "stopping" when triggered from the UI const [isStopping, setIsStopping] = useState(false); const onStopSession = useCallback(() => { - stopSession({ session_id: session.name }); + stopSession({ sessionId: session.name }); setIsStopping(true); }, [session.name, stopSession]); useEffect(() => { @@ -204,8 +210,8 @@ export default function ActiveSessionButton({ (sessionClass: number, resumeSession: boolean) => { const status = session.status.state; const request = modifySession({ - session_id: session.name, - resource_class_id: sessionClass, + sessionId: session.name, + sessionPatchRequest: { resource_class_id: sessionClass }, }); if (resumeSession && status === "hibernated") { request.then(() => { diff --git a/client/src/features/sessionsV2/sessionsV2.api.ts b/client/src/features/sessionsV2/sessionsV2.api.ts index 1a64cb46e5..38dd53f676 100644 --- a/client/src/features/sessionsV2/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/sessionsV2.api.ts @@ -224,9 +224,9 @@ export const { useAddSessionLauncherMutation, useUpdateSessionLauncherMutation, useDeleteSessionLauncherMutation, - useLaunchSessionMutation, - usePatchSessionMutation, - useStopSessionMutation, + // useLaunchSessionMutation, + // usePatchSessionMutation, + // useStopSessionMutation, useGetLogsQuery, useGetDockerImageQuery, } = sessionsV2Api; From 99423c88771404a0d2cb4da20497392b3c0cdb0b Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 15:49:49 +0100 Subject: [PATCH 04/11] more API updates --- client/src/features/sessionsV2/SessionStartPage.tsx | 9 ++++++--- client/src/features/sessionsV2/api/sessionsV2.api.ts | 10 ++++++++++ client/src/features/sessionsV2/sessionsV2.api.ts | 4 ++-- client/src/utils/customHooks/UseGetSessionLogs.ts | 8 ++++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/client/src/features/sessionsV2/SessionStartPage.tsx b/client/src/features/sessionsV2/SessionStartPage.tsx index 78cb602d4f..f148691076 100644 --- a/client/src/features/sessionsV2/SessionStartPage.tsx +++ b/client/src/features/sessionsV2/SessionStartPage.tsx @@ -59,11 +59,14 @@ import DataConnectorSecretsModal from "./DataConnectorSecretsModal"; import SessionSecretsModal from "./SessionSecretsModal"; import { SelectResourceClassModal } from "./components/SessionModals/SelectResourceClass"; import { - useGetDockerImageQuery, + // useGetDockerImageQuery, useGetProjectSessionLaunchersQuery, // useLaunchSessionMutation, } from "./sessionsV2.api"; -import { usePostSessionsMutation as useLaunchSessionMutation } from "./api/sessionsV2.api"; +import { + usePostSessionsMutation as useLaunchSessionMutation, + useGetSessionsImagesQuery as useGetDockerImageQuery, +} from "./api/sessionsV2.api"; import { SessionLauncher } from "./sessionsV2.types"; import startSessionOptionsV2Slice from "./startSessionOptionsV2.slice"; import { @@ -456,7 +459,7 @@ function StartSessionFromLauncher({ isError: isErrorDockerImageStatus, error: errorDockerImageStatus, } = useGetDockerImageQuery( - containerImage ? { image_url: containerImage } : skipToken + containerImage ? { imageUrl: containerImage } : skipToken ); const needsCredentials = startSessionOptionsV2.cloudStorage?.some( diff --git a/client/src/features/sessionsV2/api/sessionsV2.api.ts b/client/src/features/sessionsV2/api/sessionsV2.api.ts index b3cb527ed2..11d7bb3867 100644 --- a/client/src/features/sessionsV2/api/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/api/sessionsV2.api.ts @@ -49,6 +49,14 @@ export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ deleteSessionsBySessionId: { invalidatesTags: ["Session"], }, + getSessionsBySessionIdLogs: { + transformResponse: (result: unknown) => { + return result && typeof result == "string" + ? JSON.parse(result) + : result; + }, + keepUnusedDataFor: 0, + }, }, }); @@ -75,6 +83,8 @@ export const { usePostSessionsMutation, usePatchSessionsBySessionIdMutation, useDeleteSessionsBySessionIdMutation, + useGetSessionsBySessionIdLogsQuery, + useGetSessionsImagesQuery, } = sessionsV2Api; export type * from "./sessionsV2.generated-api"; diff --git a/client/src/features/sessionsV2/sessionsV2.api.ts b/client/src/features/sessionsV2/sessionsV2.api.ts index 38dd53f676..b0357404a5 100644 --- a/client/src/features/sessionsV2/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/sessionsV2.api.ts @@ -227,6 +227,6 @@ export const { // useLaunchSessionMutation, // usePatchSessionMutation, // useStopSessionMutation, - useGetLogsQuery, - useGetDockerImageQuery, + // useGetLogsQuery, + // useGetDockerImageQuery, } = sessionsV2Api; diff --git a/client/src/utils/customHooks/UseGetSessionLogs.ts b/client/src/utils/customHooks/UseGetSessionLogs.ts index f303d99b83..a55930aee0 100644 --- a/client/src/utils/customHooks/UseGetSessionLogs.ts +++ b/client/src/utils/customHooks/UseGetSessionLogs.ts @@ -17,7 +17,7 @@ */ import { useEffect, useState } from "react"; import { useGetLogsQuery } from "../../features/session/sessions.api"; -import { useGetLogsQuery as useGetLogsQueryV2 } from "../../features/sessionsV2/sessionsV2.api"; +import { useGetSessionsBySessionIdLogsQuery as useGetLogsQueryV2 } from "../../features/sessionsV2/api/sessionsV2.api"; import { ILogs } from "../../components/Logs"; /** @@ -57,7 +57,7 @@ export function useGetSessionLogsV2( show: boolean | string ) { const { data, isFetching, isLoading, error, refetch } = useGetLogsQueryV2( - { session_id: serverName, max_lines: 250 }, + { sessionId: serverName, maxLines: 250 }, { skip: !serverName } ); const [logs, setLogs] = useState(undefined); @@ -71,8 +71,8 @@ export function useGetSessionLogsV2( useEffect(() => { setLogs({ - data, - fetched: !isLoading && !error && data, + data: data ?? {}, + fetched: !isLoading && !error && !!data, fetching: isFetching, show: show ? serverName : false, }); From 027c571c885e8663d9af2cd80a24658e9a2133b4 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 16:06:26 +0100 Subject: [PATCH 05/11] wip: env/launchers API --- .../api/sessionLaunchersV2.openapi.json | 946 ++++++++++++++++++ .../features/sessionsV2/api/sessionsV2.api.ts | 17 - 2 files changed, 946 insertions(+), 17 deletions(-) create mode 100644 client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json b/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json new file mode 100644 index 0000000000..92a5728277 --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json @@ -0,0 +1,946 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Renku Data Services API", + "description": "This service is the main backend for Renku. It provides information about users, projects,\ncloud storage, access to compute resources and many other things.\n", + "version": "v1" + }, + "servers": [ + { + "url": "/api/data" + } + ], + "paths": { + "/environments": { + "get": { + "summary": "Get all global environments", + "parameters": [ + { + "in": "query", + "style": "form", + "explode": true, + "name": "get_environment_params", + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "include_archived": { + "type": "boolean", + "default": false, + "description": "Whether to return archived environments or not" + } + } + } + } + ], + "responses": { + "200": { + "description": "List of global environments", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentList" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["environments"] + }, + "post": { + "summary": "Create a new global session environment", + "description": "Requires admin permissions", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentPost" + } + } + } + }, + "responses": { + "201": { + "description": "The session environment was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Environment" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["environments"] + } + }, + "/environments/{environment_id}": { + "get": { + "summary": "Get a global session environment", + "parameters": [ + { + "in": "path", + "name": "environment_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "responses": { + "200": { + "description": "The session environment", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Environment" + } + } + } + }, + "404": { + "description": "The session environment does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["environments"] + }, + "patch": { + "summary": "Update specific fields of an existing global session environment", + "description": "Requires admin permissions", + "parameters": [ + { + "in": "path", + "name": "environment_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentPatch" + } + } + } + }, + "responses": { + "200": { + "description": "The patched session environment", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Environment" + } + } + } + }, + "404": { + "description": "The session environment does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["environments"] + }, + "delete": { + "summary": "Remove a global session environment", + "parameters": [ + { + "in": "path", + "name": "environment_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "responses": { + "204": { + "description": "The session environment was removed or did not exist in the first place" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["environments"] + } + }, + "/session_launchers": { + "get": { + "summary": "Get all user's session launchers", + "responses": { + "200": { + "description": "List of sessions launchers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLaunchersList" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + }, + "post": { + "summary": "Create a new session launcher", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLauncherPost" + } + } + } + }, + "responses": { + "201": { + "description": "The session launcher was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLauncher" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + } + }, + "/session_launchers/{launcher_id}": { + "get": { + "summary": "Get a session launcher", + "parameters": [ + { + "in": "path", + "name": "launcher_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "responses": { + "200": { + "description": "The session launcher", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLauncher" + } + } + } + }, + "404": { + "description": "The session launcher does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + }, + "patch": { + "summary": "Update specific fields of an existing session launcher", + "parameters": [ + { + "in": "path", + "name": "launcher_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLauncherPatch" + } + } + } + }, + "responses": { + "200": { + "description": "The patched session launcher", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLauncher" + } + } + } + }, + "404": { + "description": "The session launcher does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + }, + "delete": { + "summary": "Remove a session launcher", + "parameters": [ + { + "in": "path", + "name": "launcher_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "responses": { + "204": { + "description": "The session was removed or did not exist in the first place" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + } + }, + "/projects/{project_id}/session_launchers": { + "get": { + "summary": "Get a project's session launchers", + "parameters": [ + { + "in": "path", + "name": "project_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "responses": { + "200": { + "description": "List of sessions launchers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionLaunchersList" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["session_launchers"] + } + } + }, + "components": { + "schemas": { + "EnvironmentList": { + "description": "A list of session environments", + "type": "array", + "items": { + "$ref": "#/components/schemas/Environment" + } + }, + "Environment": { + "description": "A Renku 2.0 session environment", + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Ulid" + }, + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "creation_date": { + "$ref": "#/components/schemas/CreationDate" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "container_image": { + "$ref": "#/components/schemas/ContainerImage" + }, + "default_url": { + "$ref": "#/components/schemas/DefaultUrl" + }, + "uid": { + "$ref": "#/components/schemas/EnvironmentUid" + }, + "gid": { + "$ref": "#/components/schemas/EnvironmentGid" + }, + "working_directory": { + "$ref": "#/components/schemas/EnvironmentWorkingDirectory" + }, + "mount_directory": { + "$ref": "#/components/schemas/EnvironmentMountDirectory" + }, + "port": { + "$ref": "#/components/schemas/EnvironmentPort" + }, + "command": { + "$ref": "#/components/schemas/EnvironmentCommand" + }, + "args": { + "$ref": "#/components/schemas/EnvironmentArgs" + }, + "is_archived": { + "$ref": "#/components/schemas/IsArchived" + } + }, + "required": [ + "id", + "name", + "creation_date", + "container_image", + "port", + "uid", + "gid", + "default_url" + ], + "example": { + "id": "01AN4Z79ZS6XX96588FDX0H099", + "name": "JupyterLab environment", + "creation_date": "2023-11-01T17:32:28Z", + "description": "JupyterLab session environment", + "container_image": "renku-jupyter:latest", + "default_url": "/lab", + "port": 8080, + "working_directory": "/home/jovyan/work", + "mount_directory": "/home/jovyan/work", + "uid": 1000, + "gid": 1000, + "is_archive": false + } + }, + "EnvironmentGetInLauncher": { + "allOf": [ + { + "$ref": "#/components/schemas/Environment" + }, + { + "type": "object", + "properties": { + "environment_kind": { + "$ref": "#/components/schemas/EnvironmentKind" + } + }, + "required": ["environment_kind"], + "example": { + "environment_kind": "global_environment" + } + } + ] + }, + "EnvironmentPostInLauncher": { + "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentPost" + }, + { + "type": "object", + "properties": { + "environment_kind": { + "$ref": "#/components/schemas/EnvironmentKind" + } + }, + "required": ["environment_kind"], + "example": { + "environment_kind": "global_environment" + } + } + ] + }, + "EnvironmentPost": { + "description": "Data required to create a session environment", + "type": "object", + "properties": { + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "container_image": { + "$ref": "#/components/schemas/ContainerImage" + }, + "default_url": { + "allOf": [ + { + "$ref": "#/components/schemas/DefaultUrl" + }, + { + "default": "/lab" + } + ], + "default": "/lab" + }, + "uid": { + "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentUid" + }, + { + "default": 1000 + } + ], + "default": 1000 + }, + "gid": { + "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentGid" + }, + { + "default": 1000 + } + ], + "default": 1000 + }, + "working_directory": { + "$ref": "#/components/schemas/EnvironmentWorkingDirectory" + }, + "mount_directory": { + "$ref": "#/components/schemas/EnvironmentMountDirectory" + }, + "port": { + "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentPort" + }, + { + "default": 8080 + } + ], + "default": 8080 + }, + "command": { + "$ref": "#/components/schemas/EnvironmentCommand" + }, + "args": { + "$ref": "#/components/schemas/EnvironmentArgs" + }, + "is_archived": { + "$ref": "#/components/schemas/IsArchived", + "default": false + } + }, + "required": ["name", "container_image"] + }, + "EnvironmentPatchInLauncher": { + "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentPatch" + }, + { + "type": "object", + "properties": { + "environment_kind": { + "$ref": "#/components/schemas/EnvironmentKind" + } + } + } + ] + }, + "EnvironmentPatch": { + "type": "object", + "description": "Update a session environment", + "additionalProperties": false, + "properties": { + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "container_image": { + "$ref": "#/components/schemas/ContainerImage" + }, + "default_url": { + "$ref": "#/components/schemas/DefaultUrl" + }, + "uid": { + "$ref": "#/components/schemas/EnvironmentUid" + }, + "gid": { + "$ref": "#/components/schemas/EnvironmentGid" + }, + "working_directory": { + "$ref": "#/components/schemas/EnvironmentWorkingDirectoryPatch" + }, + "mount_directory": { + "$ref": "#/components/schemas/EnvironmentMountDirectoryPatch" + }, + "port": { + "$ref": "#/components/schemas/EnvironmentPort" + }, + "command": { + "$ref": "#/components/schemas/EnvironmentCommand" + }, + "args": { + "$ref": "#/components/schemas/EnvironmentArgs" + }, + "is_archived": { + "$ref": "#/components/schemas/IsArchived" + } + } + }, + "SessionLaunchersList": { + "description": "A list of Renku session launchers", + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionLauncher" + }, + "minItems": 0 + }, + "SessionLauncher": { + "description": "A Renku 2.0 session definition and metadata", + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Ulid" + }, + "project_id": { + "$ref": "#/components/schemas/Ulid" + }, + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "creation_date": { + "$ref": "#/components/schemas/CreationDate" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "environment": { + "$ref": "#/components/schemas/EnvironmentGetInLauncher" + }, + "resource_class_id": { + "$ref": "#/components/schemas/ResourceClassId" + }, + "disk_storage": { + "$ref": "#/components/schemas/DiskStorage" + } + }, + "required": [ + "id", + "project_id", + "name", + "creation_date", + "environment", + "resource_class_id" + ] + }, + "SessionLauncherPost": { + "description": "Data required to create a session launcher", + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "project_id": { + "$ref": "#/components/schemas/Ulid" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "resource_class_id": { + "$ref": "#/components/schemas/ResourceClassId" + }, + "disk_storage": { + "$ref": "#/components/schemas/DiskStorage" + }, + "environment": { + "oneOf": [ + { + "$ref": "#/components/schemas/EnvironmentPostInLauncher" + }, + { + "$ref": "#/components/schemas/EnvironmentIdOnlyPost" + } + ] + } + }, + "required": ["name", "project_id", "environment"], + "example": { + "project_id": "01AN4Z79ZS5XN0F25N3DB94T4R", + "name": "Renku R Session", + "environment": { + "id": "01AN4Z79ZS6XX96588FDX0H099" + } + } + }, + "SessionLauncherPatch": { + "type": "object", + "description": "Update a session launcher", + "additionalProperties": false, + "properties": { + "name": { + "$ref": "#/components/schemas/SessionName" + }, + "description": { + "$ref": "#/components/schemas/Description" + }, + "resource_class_id": { + "$ref": "#/components/schemas/ResourceClassId" + }, + "disk_storage": { + "$ref": "#/components/schemas/DiskStoragePatch" + }, + "environment": { + "oneOf": [ + { + "$ref": "#/components/schemas/EnvironmentPatchInLauncher" + }, + { + "$ref": "#/components/schemas/EnvironmentIdOnlyPatch" + } + ] + } + } + }, + "Ulid": { + "description": "ULID identifier", + "type": "string", + "minLength": 26, + "maxLength": 26, + "pattern": "^[0-7][0-9A-HJKMNP-TV-Z]{25}$" + }, + "SessionName": { + "description": "Renku session name", + "type": "string", + "minLength": 1, + "maxLength": 99, + "example": "My Renku Session :)" + }, + "EnvironmentIdOnlyPatch": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/EnvironmentId" + } + } + }, + "EnvironmentIdOnlyPost": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/EnvironmentId" + } + }, + "required": ["id"] + }, + "EnvironmentKind": { + "description": "Kind of environment to use", + "type": "string", + "enum": ["GLOBAL", "CUSTOM"], + "example": "CUSTOM" + }, + "EnvironmentId": { + "description": "Id of the environment to use", + "type": "string", + "minLength": 1, + "example": "01AN4Z79ZS6XX96588FDX0H099" + }, + "CreationDate": { + "description": "The date and time the resource was created (in UTC and ISO-8601 format)", + "type": "string", + "format": "date-time", + "example": "2023-11-01T17:32:28Z" + }, + "Description": { + "description": "A description for the resource", + "type": "string", + "maxLength": 500 + }, + "ContainerImage": { + "description": "A container image", + "type": "string", + "maxLength": 500, + "pattern": "^[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*(\\/[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*)*(:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}|@sha256:[a-fA-F0-9]{64}){0,1}$", + "example": "renku/renkulab-py:3.10-0.18.1" + }, + "DefaultUrl": { + "description": "The default path to open in a session", + "type": "string", + "maxLength": 200, + "example": "/lab" + }, + "ResourceClassId": { + "description": "The identifier of a resource class", + "type": "integer", + "default": null, + "nullable": true + }, + "DiskStorage": { + "description": "The size of disk storage for the session, in gigabytes", + "type": "integer", + "minimum": 1, + "example": 8 + }, + "DiskStoragePatch": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "EnvironmentPort": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "exclusiveMaximum": true, + "maximum": 65400, + "description": "The TCP port (on any container in the session) where user requests will be routed to from the ingress" + }, + "EnvironmentUid": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "maximum": 65535, + "description": "The user ID used to run the session" + }, + "EnvironmentGid": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "maximum": 65535, + "description": "The group ID used to run the session" + }, + "EnvironmentWorkingDirectory": { + "type": "string", + "description": "The location where the session will start, if left unset it will default to the session image working directory.", + "minLength": 1, + "example": "/home/jovyan/work" + }, + "EnvironmentWorkingDirectoryPatch": { + "type": "string", + "example": "/home/jovyan/work" + }, + "EnvironmentMountDirectory": { + "type": "string", + "description": "The location where the persistent storage for the session will be mounted, usually it should be identical to or a parent of the working directory, if left unset will default to the working directory.", + "minLength": 1, + "example": "/home/jovyan/work" + }, + "EnvironmentMountDirectoryPatch": { + "type": "string", + "example": "/home/jovyan/work" + }, + "EnvironmentCommand": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The command that will be run i.e. will overwrite the image Dockerfile ENTRYPOINT, equivalent to command in Kubernetes", + "minLength": 1 + }, + "EnvironmentArgs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The arguments that will follow the command, i.e. will overwrite the image Dockerfile CMD, equivalent to args in Kubernetes", + "minLength": 1 + }, + "IsArchived": { + "type": "boolean", + "description": "Whether this environment is archived and not for use in new projects or not", + "default": false + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true, + "example": 1404 + }, + "detail": { + "type": "string", + "example": "A more detailed optional message showing what the problem was" + }, + "message": { + "type": "string", + "example": "Something went wrong - please try again later" + } + }, + "required": ["code", "message"] + } + }, + "required": ["error"] + } + }, + "responses": { + "Error": { + "description": "The schema for all 4xx and 5xx responses", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } +} diff --git a/client/src/features/sessionsV2/api/sessionsV2.api.ts b/client/src/features/sessionsV2/api/sessionsV2.api.ts index 11d7bb3867..f128f14848 100644 --- a/client/src/features/sessionsV2/api/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/api/sessionsV2.api.ts @@ -60,23 +60,6 @@ export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ }, }); -// useGetNotebooksImagesQuery, -// useGetNotebooksLogsByServerNameQuery, -// useGetNotebooksServerOptionsQuery, -// usePostNotebooksServersMutation, -// useGetNotebooksServersQuery, -// useDeleteNotebooksServersByServerNameMutation, -// useGetNotebooksServersByServerNameQuery, -// usePatchNotebooksServersByServerNameMutation, -// "sessions" -// usePostSessionsMutation, -// useGetSessionsQuery, -// useGetSessionsBySessionIdQuery, -// useDeleteSessionsBySessionIdMutation, -// usePatchSessionsBySessionIdMutation, -// useGetSessionsBySessionIdLogsQuery, -// useGetSessionsImagesQuery, - export const { // "sessions" hooks useGetSessionsQuery, From b3511067a79f86fb6245b52001ff92aeae08468e Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Tue, 28 Jan 2025 16:12:05 +0100 Subject: [PATCH 06/11] wip: launchers api --- client/package.json | 3 +- .../api/sessionLaunchersV2.api-config.ts | 32 ++ .../api/sessionLaunchersV2.empty-api.ts | 26 ++ .../api/sessionLaunchersV2.generated-api.ts | 287 ++++++++++++++++++ 4 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 client/src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts create mode 100644 client/src/features/sessionsV2/api/sessionLaunchersV2.empty-api.ts create mode 100644 client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts diff --git a/client/package.json b/client/package.json index 36cc7a736a..7e5a6a6a70 100644 --- a/client/package.json +++ b/client/package.json @@ -30,7 +30,8 @@ "generate-api:platform": "rtk-query-codegen-openapi src/features/platform/api/platform.api-config.ts", "generate-api:searchV2": "rtk-query-codegen-openapi src/features/searchV2/api/searchV2.api-config.ts", "generate-api:users": "rtk-query-codegen-openapi src/features/usersV2/api/users.api-config.ts", - "generate-api:sessionsV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionsV2.api-config.ts" + "generate-api:sessionsV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionsV2.api-config.ts", + "generate-api:sessionLaunchersV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts" }, "type": "module", "dependencies": { diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts new file mode 100644 index 0000000000..d765cec7fb --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2025 - 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. + */ + +// Run `npm run generate-api:sessionLaunchersV2` to generate the API +import type { ConfigFile } from "@rtk-query/codegen-openapi"; +import path from "path"; + +const config: ConfigFile = { + apiFile: "./sessionLaunchersV2.empty-api.ts", + apiImport: "sessionLaunchersV2EmptyApi", + outputFile: "./sessionLaunchersV2.generated-api.ts", + exportName: "sessionLaunchersV2GeneratedApi", + hooks: true, + schemaFile: path.join(__dirname, "sessionLaunchersV2.openapi.json"), +}; + +export default config; diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.empty-api.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.empty-api.ts new file mode 100644 index 0000000000..fe7921893e --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.empty-api.ts @@ -0,0 +1,26 @@ +/*! + * Copyright 2025 - 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 { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; + +// initialize an empty api service that we'll inject endpoints into later as needed +export const sessionLaunchersV2EmptyApi = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: "/api/data" }), + endpoints: () => ({}), + reducerPath: "sessionLaunchersV2Api", +}); diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts new file mode 100644 index 0000000000..10254532f8 --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts @@ -0,0 +1,287 @@ +import { sessionLaunchersV2EmptyApi as api } from "./sessionLaunchersV2.empty-api"; +const injectedRtkApi = api.injectEndpoints({ + endpoints: (build) => ({ + getEnvironments: build.query< + GetEnvironmentsApiResponse, + GetEnvironmentsApiArg + >({ + query: (queryArg) => ({ + url: `/environments`, + params: { get_environment_params: queryArg.getEnvironmentParams }, + }), + }), + postEnvironments: build.mutation< + PostEnvironmentsApiResponse, + PostEnvironmentsApiArg + >({ + query: (queryArg) => ({ + url: `/environments`, + method: "POST", + body: queryArg.environmentPost, + }), + }), + getEnvironmentsByEnvironmentId: build.query< + GetEnvironmentsByEnvironmentIdApiResponse, + GetEnvironmentsByEnvironmentIdApiArg + >({ + query: (queryArg) => ({ url: `/environments/${queryArg.environmentId}` }), + }), + patchEnvironmentsByEnvironmentId: build.mutation< + PatchEnvironmentsByEnvironmentIdApiResponse, + PatchEnvironmentsByEnvironmentIdApiArg + >({ + query: (queryArg) => ({ + url: `/environments/${queryArg.environmentId}`, + method: "PATCH", + body: queryArg.environmentPatch, + }), + }), + deleteEnvironmentsByEnvironmentId: build.mutation< + DeleteEnvironmentsByEnvironmentIdApiResponse, + DeleteEnvironmentsByEnvironmentIdApiArg + >({ + query: (queryArg) => ({ + url: `/environments/${queryArg.environmentId}`, + method: "DELETE", + }), + }), + getSessionLaunchers: build.query< + GetSessionLaunchersApiResponse, + GetSessionLaunchersApiArg + >({ + query: () => ({ url: `/session_launchers` }), + }), + postSessionLaunchers: build.mutation< + PostSessionLaunchersApiResponse, + PostSessionLaunchersApiArg + >({ + query: (queryArg) => ({ + url: `/session_launchers`, + method: "POST", + body: queryArg.sessionLauncherPost, + }), + }), + getSessionLaunchersByLauncherId: build.query< + GetSessionLaunchersByLauncherIdApiResponse, + GetSessionLaunchersByLauncherIdApiArg + >({ + query: (queryArg) => ({ + url: `/session_launchers/${queryArg.launcherId}`, + }), + }), + patchSessionLaunchersByLauncherId: build.mutation< + PatchSessionLaunchersByLauncherIdApiResponse, + PatchSessionLaunchersByLauncherIdApiArg + >({ + query: (queryArg) => ({ + url: `/session_launchers/${queryArg.launcherId}`, + method: "PATCH", + body: queryArg.sessionLauncherPatch, + }), + }), + deleteSessionLaunchersByLauncherId: build.mutation< + DeleteSessionLaunchersByLauncherIdApiResponse, + DeleteSessionLaunchersByLauncherIdApiArg + >({ + query: (queryArg) => ({ + url: `/session_launchers/${queryArg.launcherId}`, + method: "DELETE", + }), + }), + getProjectsByProjectIdSessionLaunchers: build.query< + GetProjectsByProjectIdSessionLaunchersApiResponse, + GetProjectsByProjectIdSessionLaunchersApiArg + >({ + query: (queryArg) => ({ + url: `/projects/${queryArg.projectId}/session_launchers`, + }), + }), + }), + overrideExisting: false, +}); +export { injectedRtkApi as sessionLaunchersV2GeneratedApi }; +export type GetEnvironmentsApiResponse = + /** status 200 List of global environments */ EnvironmentList; +export type GetEnvironmentsApiArg = { + getEnvironmentParams?: { + /** Whether to return archived environments or not */ + include_archived?: boolean; + }; +}; +export type PostEnvironmentsApiResponse = + /** status 201 The session environment was created */ Environment; +export type PostEnvironmentsApiArg = { + environmentPost: EnvironmentPost; +}; +export type GetEnvironmentsByEnvironmentIdApiResponse = + /** status 200 The session environment */ Environment; +export type GetEnvironmentsByEnvironmentIdApiArg = { + environmentId: Ulid; +}; +export type PatchEnvironmentsByEnvironmentIdApiResponse = + /** status 200 The patched session environment */ Environment; +export type PatchEnvironmentsByEnvironmentIdApiArg = { + environmentId: Ulid; + environmentPatch: EnvironmentPatch; +}; +export type DeleteEnvironmentsByEnvironmentIdApiResponse = + /** status 204 The session environment was removed or did not exist in the first place */ void; +export type DeleteEnvironmentsByEnvironmentIdApiArg = { + environmentId: Ulid; +}; +export type GetSessionLaunchersApiResponse = + /** status 200 List of sessions launchers */ SessionLaunchersList; +export type GetSessionLaunchersApiArg = void; +export type PostSessionLaunchersApiResponse = + /** status 201 The session launcher was created */ SessionLauncher; +export type PostSessionLaunchersApiArg = { + sessionLauncherPost: SessionLauncherPost; +}; +export type GetSessionLaunchersByLauncherIdApiResponse = + /** status 200 The session launcher */ SessionLauncher; +export type GetSessionLaunchersByLauncherIdApiArg = { + launcherId: Ulid; +}; +export type PatchSessionLaunchersByLauncherIdApiResponse = + /** status 200 The patched session launcher */ SessionLauncher; +export type PatchSessionLaunchersByLauncherIdApiArg = { + launcherId: Ulid; + sessionLauncherPatch: SessionLauncherPatch; +}; +export type DeleteSessionLaunchersByLauncherIdApiResponse = + /** status 204 The session was removed or did not exist in the first place */ void; +export type DeleteSessionLaunchersByLauncherIdApiArg = { + launcherId: Ulid; +}; +export type GetProjectsByProjectIdSessionLaunchersApiResponse = + /** status 200 List of sessions launchers */ SessionLaunchersList; +export type GetProjectsByProjectIdSessionLaunchersApiArg = { + projectId: Ulid; +}; +export type Ulid = string; +export type SessionName = string; +export type CreationDate = string; +export type Description = string; +export type ContainerImage = string; +export type DefaultUrl = string; +export type EnvironmentUid = number; +export type EnvironmentGid = number; +export type EnvironmentWorkingDirectory = string; +export type EnvironmentMountDirectory = string; +export type EnvironmentPort = number; +export type EnvironmentCommand = string[]; +export type EnvironmentArgs = string[]; +export type IsArchived = boolean; +export type Environment = { + id: Ulid; + name: SessionName; + creation_date: CreationDate; + description?: Description; + container_image: ContainerImage; + default_url: DefaultUrl; + uid: EnvironmentUid; + gid: EnvironmentGid; + working_directory?: EnvironmentWorkingDirectory; + mount_directory?: EnvironmentMountDirectory; + port: EnvironmentPort; + command?: EnvironmentCommand; + args?: EnvironmentArgs; + is_archived?: IsArchived; +}; +export type EnvironmentList = Environment[]; +export type ErrorResponse = { + error: { + code: number; + detail?: string; + message: string; + }; +}; +export type EnvironmentPost = { + name: SessionName; + description?: Description; + container_image: ContainerImage; + default_url?: DefaultUrl & any; + uid?: EnvironmentUid & any; + gid?: EnvironmentGid & any; + working_directory?: EnvironmentWorkingDirectory; + mount_directory?: EnvironmentMountDirectory; + port?: EnvironmentPort & any; + command?: EnvironmentCommand; + args?: EnvironmentArgs; + is_archived?: IsArchived; +}; +export type EnvironmentWorkingDirectoryPatch = string; +export type EnvironmentMountDirectoryPatch = string; +export type EnvironmentPatch = { + name?: SessionName; + description?: Description; + container_image?: ContainerImage; + default_url?: DefaultUrl; + uid?: EnvironmentUid; + gid?: EnvironmentGid; + working_directory?: EnvironmentWorkingDirectoryPatch; + mount_directory?: EnvironmentMountDirectoryPatch; + port?: EnvironmentPort; + command?: EnvironmentCommand; + args?: EnvironmentArgs; + is_archived?: IsArchived; +}; +export type EnvironmentKind = "GLOBAL" | "CUSTOM"; +export type EnvironmentGetInLauncher = Environment & { + environment_kind: EnvironmentKind; +}; +export type ResourceClassId = number | null; +export type DiskStorage = number; +export type SessionLauncher = { + id: Ulid; + project_id: Ulid; + name: SessionName; + creation_date: CreationDate; + description?: Description; + environment: EnvironmentGetInLauncher; + resource_class_id: ResourceClassId; + disk_storage?: DiskStorage; +}; +export type SessionLaunchersList = SessionLauncher[]; +export type EnvironmentPostInLauncher = EnvironmentPost & { + environment_kind: EnvironmentKind; +}; +export type EnvironmentId = string; +export type EnvironmentIdOnlyPost = { + id: EnvironmentId; +}; +export type SessionLauncherPost = { + name: SessionName; + project_id: Ulid; + description?: Description; + resource_class_id?: ResourceClassId; + disk_storage?: DiskStorage; + environment: EnvironmentPostInLauncher | EnvironmentIdOnlyPost; +}; +export type DiskStoragePatch = number | null; +export type EnvironmentPatchInLauncher = EnvironmentPatch & { + environment_kind?: EnvironmentKind; +}; +export type EnvironmentIdOnlyPatch = { + id?: EnvironmentId; +}; +export type SessionLauncherPatch = { + name?: SessionName; + description?: Description; + resource_class_id?: ResourceClassId; + disk_storage?: DiskStoragePatch; + environment?: EnvironmentPatchInLauncher | EnvironmentIdOnlyPatch; +}; +export const { + useGetEnvironmentsQuery, + usePostEnvironmentsMutation, + useGetEnvironmentsByEnvironmentIdQuery, + usePatchEnvironmentsByEnvironmentIdMutation, + useDeleteEnvironmentsByEnvironmentIdMutation, + useGetSessionLaunchersQuery, + usePostSessionLaunchersMutation, + useGetSessionLaunchersByLauncherIdQuery, + usePatchSessionLaunchersByLauncherIdMutation, + useDeleteSessionLaunchersByLauncherIdMutation, + useGetProjectsByProjectIdSessionLaunchersQuery, +} = injectedRtkApi; From b665dbf49cec616f0abb2c8b7031f5af116b55e4 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Wed, 29 Jan 2025 11:08:48 +0100 Subject: [PATCH 07/11] more api refactor --- client/.eslintignore | 1 + .../dashboardV2/DashboardV2Sessions.tsx | 4 +- .../sessionsV2/DeleteSessionLauncherModal.tsx | 2 +- .../SessionList/SessionItemDisplay.tsx | 1 - .../SessionShowPage/ShowSessionPage.tsx | 5 +- .../features/sessionsV2/SessionStartPage.tsx | 6 +- client/src/features/sessionsV2/SessionsV2.tsx | 5 +- .../sessionsV2/api/sessionLaunchersV2.api.ts | 77 ++++++ .../features/sessionsV2/api/sessionsV2.api.ts | 13 +- .../SessionForm/EditLauncherFormContent.tsx | 5 +- .../SessionForm/GlobalEnvironmentFields.tsx | 5 +- .../SessionModals/ModifyResourcesLauncher.tsx | 8 +- .../SessionModals/NewSessionLauncherModal.tsx | 22 +- .../SessionModals/SelectResourceClass.tsx | 2 +- .../UpdateSessionLauncherModal.tsx | 18 +- .../src/features/sessionsV2/sessionsV2.api.ts | 232 ------------------ .../features/sessionsV2/sessionsV2.types.ts | 4 +- client/src/utils/helpers/EnhancedState.ts | 5 +- .../handlers/sessionStatusHandlerV2.ts | 2 +- 19 files changed, 139 insertions(+), 278 deletions(-) create mode 100644 client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts delete mode 100644 client/src/features/sessionsV2/sessionsV2.api.ts diff --git a/client/.eslintignore b/client/.eslintignore index 6eb2d9a87f..865ee4b376 100644 --- a/client/.eslintignore +++ b/client/.eslintignore @@ -6,3 +6,4 @@ src/features/projectsV2/api/storagesV2.api.ts src/features/dataConnectorsV2/api/data-connectors.api.ts src/features/usersV2/api/users.generated-api.ts src/features/sessionsV2/api/sessionsV2.generated-api.ts +src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts diff --git a/client/src/features/dashboardV2/DashboardV2Sessions.tsx b/client/src/features/dashboardV2/DashboardV2Sessions.tsx index 53d1dc48f7..7a948d7572 100644 --- a/client/src/features/dashboardV2/DashboardV2Sessions.tsx +++ b/client/src/features/dashboardV2/DashboardV2Sessions.tsx @@ -25,7 +25,7 @@ import { Col, ListGroup, Row } from "reactstrap"; import { Loader } from "../../components/Loader"; import EnvironmentLogsV2 from "../../components/LogsV2"; import { RtkErrorAlert } from "../../components/errors/RtkErrorAlert"; -import { useGetProjectSessionLauncherQuery } from "../../features/sessionsV2/sessionsV2.api"; +import { useGetSessionLaunchersByLauncherIdQuery as useGetProjectSessionLauncherQuery } from "../sessionsV2/api/sessionLaunchersV2.generated-api"; import { ABSOLUTE_ROUTES } from "../../routing/routes.constants"; import useAppSelector from "../../utils/customHooks/useAppSelector.hook"; import { useGetProjectsByProjectIdQuery } from "../projectsV2/api/projectV2.enhanced-api"; @@ -122,7 +122,7 @@ function DashboardSession({ session }: DashboardSessionProps) { projectId ? { projectId } : skipToken ); const { data: launcher } = useGetProjectSessionLauncherQuery( - launcherId ? { id: launcherId } : skipToken + launcherId ? { launcherId } : skipToken ); const projectUrl = project diff --git a/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx b/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx index 544dc206c5..79fd2b774f 100644 --- a/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx @@ -22,7 +22,7 @@ import { Trash, XLg } from "react-bootstrap-icons"; import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; import { RtkErrorAlert } from "../../components/errors/RtkErrorAlert"; -import { useDeleteSessionLauncherMutation } from "./sessionsV2.api"; +import { useDeleteSessionLaunchersByLauncherIdMutation as useDeleteSessionLauncherMutation } from "./api/sessionLaunchersV2.api"; import { SessionLauncher } from "./sessionsV2.types"; import { WarnAlert } from "../../components/Alert"; diff --git a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx index 31a8a4282d..9cc167469c 100644 --- a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx +++ b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx @@ -20,7 +20,6 @@ import { useCallback, useMemo } from "react"; import useLocationHash from "../../../utils/customHooks/useLocationHash.hook"; import { Project } from "../../projectsV2/api/projectV2.api"; -// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../api/sessionsV2.api"; import { SessionView } from "../SessionView/SessionView"; import { SessionLauncher } from "../sessionsV2.types"; diff --git a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx index 29b9d2a822..3247d703ee 100644 --- a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx +++ b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx @@ -61,10 +61,7 @@ import styles from "../../session/components/ShowSession.module.scss"; import { StartSessionProgressBarV2 } from "../../session/components/StartSessionProgressBar"; import PauseOrDeleteSessionModal from "../PauseOrDeleteSessionModal"; import { getSessionFavicon } from "../session.utils"; -import { - useGetProjectSessionLaunchersQuery, - // useGetSessionsQuery, -} from "../sessionsV2.api"; +import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "../api/sessionLaunchersV2.api"; import { useGetSessionsQuery } from "../api/sessionsV2.api"; import { SessionV2 } from "../sessionsV2.types"; import SessionIframe from "./SessionIframe"; diff --git a/client/src/features/sessionsV2/SessionStartPage.tsx b/client/src/features/sessionsV2/SessionStartPage.tsx index f148691076..cf1875915d 100644 --- a/client/src/features/sessionsV2/SessionStartPage.tsx +++ b/client/src/features/sessionsV2/SessionStartPage.tsx @@ -58,11 +58,7 @@ import { storageSecretNameToFieldName } from "../secretsV2/secrets.utils"; import DataConnectorSecretsModal from "./DataConnectorSecretsModal"; import SessionSecretsModal from "./SessionSecretsModal"; import { SelectResourceClassModal } from "./components/SessionModals/SelectResourceClass"; -import { - // useGetDockerImageQuery, - useGetProjectSessionLaunchersQuery, - // useLaunchSessionMutation, -} from "./sessionsV2.api"; +import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "./api/sessionLaunchersV2.api"; import { usePostSessionsMutation as useLaunchSessionMutation, useGetSessionsImagesQuery as useGetDockerImageQuery, diff --git a/client/src/features/sessionsV2/SessionsV2.tsx b/client/src/features/sessionsV2/SessionsV2.tsx index 200df402f7..bea45492e9 100644 --- a/client/src/features/sessionsV2/SessionsV2.tsx +++ b/client/src/features/sessionsV2/SessionsV2.tsx @@ -44,10 +44,7 @@ import SessionItem from "./SessionList/SessionItem"; import { SessionItemDisplay } from "./SessionList/SessionItemDisplay"; import { SessionView } from "./SessionView/SessionView"; import UpdateSessionLauncherModal from "./components/SessionModals/UpdateSessionLauncherModal"; -import { - useGetProjectSessionLaunchersQuery, - // useGetSessionsQuery as useGetSessionsQueryV2, -} from "./sessionsV2.api"; +import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "./api/sessionLaunchersV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "./api/sessionsV2.api"; import { SessionLauncher, SessionV2 } from "./sessionsV2.types"; diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts new file mode 100644 index 0000000000..ab4f1240fc --- /dev/null +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts @@ -0,0 +1,77 @@ +/*! + * Copyright 2025 - 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 { sessionLaunchersV2GeneratedApi } from "./sessionLaunchersV2.generated-api"; + +// Adds tag handling for cache management +export const sessionLaunchersV2Api = + sessionLaunchersV2GeneratedApi.enhanceEndpoints({ + addTagTypes: ["Environment", "Launcher"], + endpoints: { + getEnvironments: { + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ + id, + type: "Environment" as const, + })), + "Environment", + ] + : ["Environment"], + }, + getSessionLaunchersByLauncherId: { + providesTags: (result) => + result + ? [{ id: result.id, type: "Launcher" }, "Launcher"] + : ["Launcher"], + }, + postSessionLaunchers: { + invalidatesTags: ["Launcher"], + }, + patchSessionLaunchersByLauncherId: { + invalidatesTags: (result) => + result ? [{ id: result.id, type: "Launcher" }] : ["Launcher"], + }, + deleteSessionLaunchersByLauncherId: { + invalidatesTags: ["Launcher"], + }, + getProjectsByProjectIdSessionLaunchers: { + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ id, type: "Launcher" as const })), + "Launcher", + ] + : ["Launcher"], + }, + }, + }); + +export const { + // "environments" hooks + useGetEnvironmentsQuery, + // "launchers" hooks + useGetSessionLaunchersByLauncherIdQuery, + usePostSessionLaunchersMutation, + usePatchSessionLaunchersByLauncherIdMutation, + useDeleteSessionLaunchersByLauncherIdMutation, + useGetProjectsByProjectIdSessionLaunchersQuery, +} = sessionLaunchersV2Api; + +export type * from "./sessionLaunchersV2.generated-api"; diff --git a/client/src/features/sessionsV2/api/sessionsV2.api.ts b/client/src/features/sessionsV2/api/sessionsV2.api.ts index f128f14848..bdcfd99f65 100644 --- a/client/src/features/sessionsV2/api/sessionsV2.api.ts +++ b/client/src/features/sessionsV2/api/sessionsV2.api.ts @@ -18,7 +18,8 @@ import { sessionsV2GeneratedApi } from "./sessionsV2.generated-api"; -export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ +// Adds tag handling for cache management +const withTagHandling = sessionsV2GeneratedApi.enhanceEndpoints({ addTagTypes: ["Session"], endpoints: { getSessions: { @@ -60,6 +61,16 @@ export const sessionsV2Api = sessionsV2GeneratedApi.enhanceEndpoints({ }, }); +// Adds tag invalidation endpoints +export const sessionsV2Api = withTagHandling.injectEndpoints({ + endpoints: (build) => ({ + invalidateSessions: build.mutation({ + queryFn: () => ({ data: null }), + invalidatesTags: ["Session"], + }), + }), +}); + export const { // "sessions" hooks useGetSessionsQuery, diff --git a/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx b/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx index e7a1529689..8c6ff9eafc 100644 --- a/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx +++ b/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx @@ -33,7 +33,8 @@ import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon" import { Loader } from "../../../../components/Loader"; import { CONTAINER_IMAGE_PATTERN } from "../../session.constants"; import { prioritizeSelectedEnvironment } from "../../session.utils"; -import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; +// import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; +import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery } from "../../api/sessionLaunchersV2.api"; import { SessionLauncherForm } from "../../sessionsV2.types"; import { AdvancedSettingsFields } from "./AdvancedSettingsFields"; import { EnvironmentKindField } from "./EnvironmentKindField"; @@ -64,7 +65,7 @@ export default function EditLauncherFormContent({ data: environments, error, isLoading, - } = useGetSessionEnvironmentsQuery(); + } = useGetSessionEnvironmentsQuery({}); const environmentKind = watch("environment_kind"); const [isAdvanceSettingOpen, setIsAdvanceSettingsOpen] = useState(false); const toggleIsOpen = useCallback( diff --git a/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx b/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx index 864e312640..f7c6804e29 100644 --- a/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx +++ b/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx @@ -21,7 +21,8 @@ import { Input, ListGroup } from "reactstrap"; import { WarnAlert } from "../../../../components/Alert"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; import { Loader } from "../../../../components/Loader"; -import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; +// import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; +import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery } from "../../api/sessionLaunchersV2.api"; import { EnvironmentFieldsProps } from "./EnvironmentField"; import { SessionEnvironmentItem } from "./SessionEnvironmentItem"; @@ -35,7 +36,7 @@ export function GlobalEnvironmentFields({ data: environments, error, isLoading, - } = useGetSessionEnvironmentsQuery(); + } = useGetSessionEnvironmentsQuery({}); const watchEnvironmentKind = watch("environment_kind"); return ( diff --git a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx index d897d56035..9eccb7c9d0 100644 --- a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx @@ -24,7 +24,7 @@ import { MIN_SESSION_STORAGE_GB, STEP_SESSION_STORAGE_GB, } from "../../../session/startSessionOptions.constants"; -import { useUpdateSessionLauncherMutation } from "../../sessionsV2.api"; +import { usePatchSessionLaunchersByLauncherIdMutation as useUpdateSessionLauncherMutation } from "../../api/sessionLaunchersV2.api"; import { ErrorOrNotAvailableResourcePools, FetchingResourcePools, @@ -74,8 +74,10 @@ export function ModifyResourcesLauncherModal({ : null; updateSessionLauncher({ launcherId: sessionLauncherId, - resource_class_id: data.resourceClass.id, - disk_storage: diskStorage, + sessionLauncherPatch: { + resource_class_id: data.resourceClass.id, + disk_storage: diskStorage, + }, }); } }, diff --git a/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx b/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx index 27a49f4bd3..c9fc57092f 100644 --- a/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx @@ -37,9 +37,9 @@ import { useGetNamespacesByNamespaceProjectsAndSlugQuery } from "../../../projec import { DEFAULT_PORT, DEFAULT_URL } from "../../session.constants"; import { getFormattedEnvironmentValues } from "../../session.utils"; import { - useAddSessionLauncherMutation, - useGetSessionEnvironmentsQuery, -} from "../../sessionsV2.api"; + useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery, + usePostSessionLaunchersMutation as useAddSessionLauncherMutation, +} from "../../api/sessionLaunchersV2.api"; import { SessionLauncherForm } from "../../sessionsV2.types"; import { EnvironmentFields } from "../SessionForm/EnvironmentField"; import { LauncherDetailsFields } from "../SessionForm/LauncherDetailsFields"; @@ -59,7 +59,7 @@ export default function NewSessionLauncherModal({ }: NewSessionLauncherModalProps) { const [step, setStep] = useState(LauncherType.Environment); const { namespace, slug } = useParams<{ namespace: string; slug: string }>(); - const { data: environments } = useGetSessionEnvironmentsQuery(); + const { data: environments } = useGetSessionEnvironmentsQuery({}); const [addSessionLauncher, result] = useAddSessionLauncherMutation(); const { data: project } = useGetNamespacesByNamespaceProjectsAndSlugQuery( namespace && slug ? { namespace, slug } : skipToken @@ -120,11 +120,15 @@ export default function NewSessionLauncherModal({ : undefined; if (environment.success && environment.data) addSessionLauncher({ - project_id: projectId ?? "", - resource_class_id: resourceClass.id, - disk_storage: diskStorage, - name, - environment: environment.data, + sessionLauncherPost: { + project_id: projectId ?? "", + resource_class_id: resourceClass.id, + disk_storage: diskStorage, + name, + // TODO: fix types for this session environment + // eslint-disable-next-line @typescript-eslint/no-explicit-any + environment: environment.data as any, + }, }); }, [projectId, addSessionLauncher] diff --git a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx index 6abed19b13..86560aaa01 100644 --- a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx @@ -55,7 +55,7 @@ interface SelectResourceClassModalProps { isOpen: boolean; onContinue: (env: ResourceClass, diskStorage: number | undefined) => void; projectUrl: string; - resourceClassId?: number; + resourceClassId?: number | null; isCustom: boolean; } export function SelectResourceClassModal({ diff --git a/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx b/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx index c6a337f5d9..de52c949b7 100644 --- a/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx @@ -36,9 +36,9 @@ import { getLauncherDefaultValues, } from "../../session.utils"; import { - useGetSessionEnvironmentsQuery, - useUpdateSessionLauncherMutation, -} from "../../sessionsV2.api"; + useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery, + usePatchSessionLaunchersByLauncherIdMutation as useUpdateSessionLauncherMutation, +} from "../../api/sessionLaunchersV2.api"; import { SessionLauncher, SessionLauncherForm } from "../../sessionsV2.types"; import EditLauncherFormContent from "../SessionForm/EditLauncherFormContent"; @@ -53,7 +53,7 @@ export default function UpdateSessionLauncherModal({ launcher, toggle, }: UpdateSessionLauncherModalProps) { - const { data: environments } = useGetSessionEnvironmentsQuery(); + const { data: environments } = useGetSessionEnvironmentsQuery({}); const [updateSessionLauncher, result] = useUpdateSessionLauncherMutation(); const defaultValues = useMemo( () => getLauncherDefaultValues(launcher), @@ -77,9 +77,13 @@ export default function UpdateSessionLauncherModal({ if (environment.success && environment.data) updateSessionLauncher({ launcherId: launcher.id, - name, - description: description?.trim() || undefined, - environment: environment.data, + sessionLauncherPatch: { + name, + description: description?.trim() || undefined, + // TODO: fix types for this session environment + // eslint-disable-next-line @typescript-eslint/no-explicit-any + environment: environment.data as any, + }, }); }, [launcher.id, updateSessionLauncher] diff --git a/client/src/features/sessionsV2/sessionsV2.api.ts b/client/src/features/sessionsV2/sessionsV2.api.ts deleted file mode 100644 index b0357404a5..0000000000 --- a/client/src/features/sessionsV2/sessionsV2.api.ts +++ /dev/null @@ -1,232 +0,0 @@ -/*! - * Copyright 2024 - 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 { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; - -import { - AddSessionLauncherParams, - DeleteSessionLauncherParams, - DockerImage, - GetLogsParams, - GetProjectSessionLauncherParams, - GetProjectSessionLaunchersParams, - LaunchSessionParams, - PatchSessionParams, - SessionEnvironmentList, - SessionImageParams, - SessionLauncher, - SessionLauncherList, - SessionList, - SessionV2, - StopSessionParams, - UpdateSessionLauncherParams, -} from "./sessionsV2.types"; - -const sessionsV2Api = createApi({ - reducerPath: "sessionsV2Api", - baseQuery: fetchBaseQuery({ - baseUrl: "/api/data", - }), - tagTypes: ["Environment", "Launcher", "SessionsV2"], - endpoints: (builder) => ({ - getSessionEnvironments: builder.query({ - query: () => { - return { - url: "environments", - }; - }, - }), - getSessionLaunchers: builder.query({ - query: () => { - return { - url: "session_launchers", - }; - }, - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ type: "Launcher" as const, id })), - "Launcher", - ] - : ["Launcher"], - }), - getProjectSessionLaunchers: builder.query< - SessionLauncherList, - GetProjectSessionLaunchersParams - >({ - query: ({ projectId }) => { - return { - url: `/projects/${projectId}/session_launchers`, - }; - }, - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ type: "Launcher" as const, id })), - "Launcher", - ] - : ["Launcher"], - }), - getProjectSessionLauncher: builder.query< - SessionLauncher, - GetProjectSessionLauncherParams - >({ - query: ({ id }) => { - return { - url: `/session_launchers/${id}`, - }; - }, - providesTags: (result) => - result ? [{ type: "Launcher" as const, id: result.id }] : ["Launcher"], - }), - addSessionLauncher: builder.mutation< - SessionLauncher, - AddSessionLauncherParams - >({ - query: ({ ...params }) => { - return { - url: "session_launchers", - method: "POST", - body: params, - }; - }, - invalidatesTags: ["Launcher"], - }), - updateSessionLauncher: builder.mutation< - SessionLauncher, - UpdateSessionLauncherParams - >({ - query: ({ launcherId, ...params }) => { - return { - url: `session_launchers/${launcherId}`, - method: "PATCH", - body: params, - }; - }, - invalidatesTags: (_result, _error, { launcherId }) => [ - { id: launcherId, type: "Launcher" }, - ], - }), - deleteSessionLauncher: builder.mutation({ - query: ({ launcherId }) => { - return { - url: `session_launchers/${launcherId}`, - method: "DELETE", - }; - }, - invalidatesTags: ["Launcher"], - }), - getSessions: builder.query({ - query: () => ({ url: "sessions" }), - providesTags: (result) => - result - ? [ - ...result.map(({ name }) => ({ - type: "SessionsV2" as const, - name, - })), - "SessionsV2", - ] - : ["SessionsV2"], - }), - launchSession: builder.mutation({ - query: ({ - launcher_id, - disk_storage, - resource_class_id, - cloudstorage, - }) => { - const body = { - launcher_id, - disk_storage, - resource_class_id, - cloudstorage, - }; - return { - body, - method: "POST", - url: "/sessions", - }; - }, - }), - patchSession: builder.mutation({ - query: ({ session_id, state, resource_class_id }) => ({ - method: "PATCH", - url: `sessions/${session_id}`, - body: { - ...(state ? { state } : {}), - ...(resource_class_id ? { resource_class_id } : {}), - }, - }), - transformResponse: () => null, - invalidatesTags: (_result, _error, { session_id }) => [ - { id: session_id, type: "SessionsV2" }, - ], - }), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getLogs: builder.query({ - query: ({ session_id, max_lines }) => { - return { - url: `sessions/${session_id}/logs`, - params: { max_lines }, - }; - }, - transformResponse: (result: unknown) => { - return result && typeof result == "string" - ? JSON.parse(result) - : result; - }, - keepUnusedDataFor: 0, - }), - stopSession: builder.mutation({ - query: ({ session_id }) => ({ - method: "DELETE", - url: `sessions/${session_id}`, - }), - invalidatesTags: ["SessionsV2"], - }), - invalidateSessions: builder.mutation({ - queryFn: () => ({ data: null }), - invalidatesTags: ["SessionsV2"], - }), - getDockerImage: builder.query({ - query: ({ image_url }) => ({ - method: "GET", - url: "sessions/images", - params: { image_url }, - }), - }), - }), -}); - -export default sessionsV2Api; -export const { - // useGetSessionsQuery, - useGetSessionEnvironmentsQuery, - useGetSessionLaunchersQuery, - useGetProjectSessionLauncherQuery, - useGetProjectSessionLaunchersQuery, - useAddSessionLauncherMutation, - useUpdateSessionLauncherMutation, - useDeleteSessionLauncherMutation, - // useLaunchSessionMutation, - // usePatchSessionMutation, - // useStopSessionMutation, - // useGetLogsQuery, - // useGetDockerImageQuery, -} = sessionsV2Api; diff --git a/client/src/features/sessionsV2/sessionsV2.types.ts b/client/src/features/sessionsV2/sessionsV2.types.ts index 84f8990489..efcc4a11ac 100644 --- a/client/src/features/sessionsV2/sessionsV2.types.ts +++ b/client/src/features/sessionsV2/sessionsV2.types.ts @@ -31,7 +31,7 @@ export interface SessionEnvironment { working_directory?: string; mount_directory?: string; port?: number; - environment_kind: EnvironmentKind; + environment_kind?: EnvironmentKind; command?: string[]; args?: string[]; } @@ -44,7 +44,7 @@ export type SessionLauncher = { name: string; creation_date: string; description?: string; - resource_class_id?: number; + resource_class_id?: number | null; disk_storage?: number; environment: SessionLauncherEnvironment; }; diff --git a/client/src/utils/helpers/EnhancedState.ts b/client/src/utils/helpers/EnhancedState.ts index a11a6ef715..b726f6abf6 100644 --- a/client/src/utils/helpers/EnhancedState.ts +++ b/client/src/utils/helpers/EnhancedState.ts @@ -57,7 +57,8 @@ import sessionsApi from "../../features/session/sessions.api"; import sessionSidecarApi from "../../features/session/sidecar.api"; import startSessionSlice from "../../features/session/startSession.slice"; import { startSessionOptionsSlice } from "../../features/session/startSessionOptionsSlice"; -import sessionsV2Api from "../../features/sessionsV2/sessionsV2.api"; +import { sessionLaunchersV2EmptyApi as sessionLaunchersV2Api } from "../../features/sessionsV2/api/sessionLaunchersV2.empty-api"; +import { sessionsV2EmptyApi as sessionsV2Api } from "../../features/sessionsV2/api/sessionsV2.empty-api"; import startSessionOptionsV2Slice from "../../features/sessionsV2/startSessionOptionsV2.slice"; import termsApi from "../../features/terms/terms.api"; import { usersEmptyApi as usersApi } from "../../features/usersV2/api/users.empty-api"; @@ -107,6 +108,7 @@ export const createStore = ( [sessionsApi.reducerPath]: sessionsApi.reducer, [sessionSidecarApi.reducerPath]: sessionSidecarApi.reducer, [sessionsV2Api.reducerPath]: sessionsV2Api.reducer, + [sessionLaunchersV2Api.reducerPath]: sessionLaunchersV2Api.reducer, [statuspageApi.reducerPath]: statuspageApi.reducer, [termsApi.reducerPath]: termsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, @@ -145,6 +147,7 @@ export const createStore = ( .concat(sessionSidecarApi.middleware) .concat(sessionSidecarApi.middleware) .concat(sessionsV2Api.middleware) + .concat(sessionLaunchersV2Api.middleware) .concat(statuspageApi.middleware) .concat(termsApi.middleware) .concat(usersApi.middleware) diff --git a/client/src/websocket/handlers/sessionStatusHandlerV2.ts b/client/src/websocket/handlers/sessionStatusHandlerV2.ts index 5462accf3c..724ee64b9e 100644 --- a/client/src/websocket/handlers/sessionStatusHandlerV2.ts +++ b/client/src/websocket/handlers/sessionStatusHandlerV2.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -import sessionsV2Api from "../../features/sessionsV2/sessionsV2.api"; +import { sessionsV2Api } from "../../features/sessionsV2/api/sessionsV2.api"; import { StateModel } from "../../model"; function handleSessionsStatusV2( From e01d84555e46e4e6220b2e9d4eb442306ae4d5e3 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Wed, 29 Jan 2025 11:49:10 +0100 Subject: [PATCH 08/11] update e2e fixture --- tests/cypress/support/renkulab-fixtures/newSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/support/renkulab-fixtures/newSession.ts b/tests/cypress/support/renkulab-fixtures/newSession.ts index 072a24a125..92fda5d718 100644 --- a/tests/cypress/support/renkulab-fixtures/newSession.ts +++ b/tests/cypress/support/renkulab-fixtures/newSession.ts @@ -128,7 +128,7 @@ export function NewSession(Parent: T) { name = "getEnvironments", } = args ?? {}; const response = { fixture }; - cy.intercept("GET", `/api/data/environments`, response).as(name); + cy.intercept("GET", `/api/data/environments*`, response).as(name); return this; } }; From 646afcaf0dcb1dec0a893ceff387bcfc0bb7993d Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Wed, 29 Jan 2025 13:14:55 +0100 Subject: [PATCH 09/11] sorting --- client/package.json | 8 ++++---- client/src/utils/helpers/EnhancedState.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/package.json b/client/package.json index 7e5a6a6a70..9397af42d4 100644 --- a/client/package.json +++ b/client/package.json @@ -22,16 +22,16 @@ "storybook-wait-server": "wait-on http://127.0.0.1:6006", "storybook-test": "test-storybook", "storybook-compile-and-test": "concurrently -k -s first -n 'BUILD,TEST' -c 'magenta,blue' 'npm run storybook-build && npm run storybook-start-server' 'npm run storybook-wait-server && npm run storybook-test'", - "generate-api": "npm run generate-api:connectedServices && npm run generate-api:data-connectors && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:platform && npm run generate-api:searchV2 && npm run generate-api:users", + "generate-api": "npm run generate-api:connectedServices && npm run generate-api:data-connectors && npm run generate-api:namespaceV2 && npm run generate-api:platform && npm run generate-api:projectV2 && npm run generate-api:searchV2 && npm run generate-api:sessionLaunchersV2 && npm run generate-api:sessionsV2 && npm run generate-api:users", "generate-api:connectedServices": "rtk-query-codegen-openapi src/features/connectedServices/api/connectedServices.api-config.ts", "generate-api:data-connectors": "rtk-query-codegen-openapi src/features/dataConnectorsV2/api/data-connectors.api-config.ts", "generate-api:namespaceV2": "rtk-query-codegen-openapi src/features/projectsV2/api/namespace.api-config.ts", - "generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts", "generate-api:platform": "rtk-query-codegen-openapi src/features/platform/api/platform.api-config.ts", + "generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts", "generate-api:searchV2": "rtk-query-codegen-openapi src/features/searchV2/api/searchV2.api-config.ts", - "generate-api:users": "rtk-query-codegen-openapi src/features/usersV2/api/users.api-config.ts", + "generate-api:sessionLaunchersV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts", "generate-api:sessionsV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionsV2.api-config.ts", - "generate-api:sessionLaunchersV2": "rtk-query-codegen-openapi src/features/sessionsV2/api/sessionLaunchersV2.api-config.ts" + "generate-api:users": "rtk-query-codegen-openapi src/features/usersV2/api/users.api-config.ts" }, "type": "module", "dependencies": { diff --git a/client/src/utils/helpers/EnhancedState.ts b/client/src/utils/helpers/EnhancedState.ts index b726f6abf6..deaf457f8a 100644 --- a/client/src/utils/helpers/EnhancedState.ts +++ b/client/src/utils/helpers/EnhancedState.ts @@ -105,10 +105,10 @@ export const createStore = ( [recentUserActivityApi.reducerPath]: recentUserActivityApi.reducer, [repositoriesApi.reducerPath]: repositoriesApi.reducer, [searchV2Api.reducerPath]: searchV2Api.reducer, + [sessionLaunchersV2Api.reducerPath]: sessionLaunchersV2Api.reducer, [sessionsApi.reducerPath]: sessionsApi.reducer, [sessionSidecarApi.reducerPath]: sessionSidecarApi.reducer, [sessionsV2Api.reducerPath]: sessionsV2Api.reducer, - [sessionLaunchersV2Api.reducerPath]: sessionLaunchersV2Api.reducer, [statuspageApi.reducerPath]: statuspageApi.reducer, [termsApi.reducerPath]: termsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, @@ -143,11 +143,11 @@ export const createStore = ( .concat(recentUserActivityApi.middleware) .concat(repositoriesApi.middleware) .concat(searchV2Api.middleware) + .concat(sessionLaunchersV2Api.middleware) .concat(sessionsApi.middleware) .concat(sessionSidecarApi.middleware) .concat(sessionSidecarApi.middleware) .concat(sessionsV2Api.middleware) - .concat(sessionLaunchersV2Api.middleware) .concat(statuspageApi.middleware) .concat(termsApi.middleware) .concat(usersApi.middleware) From 44e8126527100c5320fdf95d7e81870db819fce7 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Wed, 29 Jan 2025 13:23:51 +0100 Subject: [PATCH 10/11] cleanup --- client/.eslintignore | 6 +++--- client/src/features/dashboardV2/DashboardV2.tsx | 1 - client/src/features/dashboardV2/DashboardV2Sessions.tsx | 2 +- .../src/features/session/useWaitForSessionStatus.hook.ts | 3 +-- .../features/sessionsV2/DeleteSessionLauncherModal.tsx | 2 +- .../features/sessionsV2/PauseOrDeleteSessionModal.tsx | 6 ++++-- .../sessionsV2/SessionList/SessionItemDisplay.tsx | 2 +- .../sessionsV2/SessionShowPage/SessionPaused.tsx | 1 - .../sessionsV2/SessionShowPage/ShowSessionPage.tsx | 6 ++++-- client/src/features/sessionsV2/SessionStartPage.tsx | 5 +++-- client/src/features/sessionsV2/SessionsV2.tsx | 2 +- .../components/SessionForm/EditLauncherFormContent.tsx | 3 +-- .../components/SessionForm/GlobalEnvironmentFields.tsx | 3 ++- .../components/SessionModals/NewSessionLauncherModal.tsx | 7 ++++--- .../SessionModals/UpdateSessionLauncherModal.tsx | 9 +++++---- client/src/utils/customHooks/UseGetSessionLogs.ts | 4 +++- 16 files changed, 34 insertions(+), 28 deletions(-) diff --git a/client/.eslintignore b/client/.eslintignore index 865ee4b376..19e7e5d6d8 100644 --- a/client/.eslintignore +++ b/client/.eslintignore @@ -1,9 +1,9 @@ *.css *.svg # Generated files should not be linted +src/features/dataConnectorsV2/api/data-connectors.api.ts src/features/projectsV2/api/projectV2.api.ts src/features/projectsV2/api/storagesV2.api.ts -src/features/dataConnectorsV2/api/data-connectors.api.ts -src/features/usersV2/api/users.generated-api.ts -src/features/sessionsV2/api/sessionsV2.generated-api.ts src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts +src/features/sessionsV2/api/sessionsV2.generated-api.ts +src/features/usersV2/api/users.generated-api.ts diff --git a/client/src/features/dashboardV2/DashboardV2.tsx b/client/src/features/dashboardV2/DashboardV2.tsx index 72e60cec78..e1d3cee551 100644 --- a/client/src/features/dashboardV2/DashboardV2.tsx +++ b/client/src/features/dashboardV2/DashboardV2.tsx @@ -61,7 +61,6 @@ import CreateProjectV2Button from "../projectsV2/new/CreateProjectV2Button"; import GroupShortHandDisplay from "../projectsV2/show/GroupShortHandDisplay"; import ProjectShortHandDisplay from "../projectsV2/show/ProjectShortHandDisplay"; import SearchV2Bar from "../searchV2/components/SearchV2Bar"; -// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/api/sessionsV2.api"; import { useGetUserQuery } from "../usersV2/api/users.api"; import UserAvatar from "../usersV2/show/UserAvatar"; diff --git a/client/src/features/dashboardV2/DashboardV2Sessions.tsx b/client/src/features/dashboardV2/DashboardV2Sessions.tsx index 7a948d7572..1d8d22a5ef 100644 --- a/client/src/features/dashboardV2/DashboardV2Sessions.tsx +++ b/client/src/features/dashboardV2/DashboardV2Sessions.tsx @@ -25,10 +25,10 @@ import { Col, ListGroup, Row } from "reactstrap"; import { Loader } from "../../components/Loader"; import EnvironmentLogsV2 from "../../components/LogsV2"; import { RtkErrorAlert } from "../../components/errors/RtkErrorAlert"; -import { useGetSessionLaunchersByLauncherIdQuery as useGetProjectSessionLauncherQuery } from "../sessionsV2/api/sessionLaunchersV2.generated-api"; import { ABSOLUTE_ROUTES } from "../../routing/routes.constants"; import useAppSelector from "../../utils/customHooks/useAppSelector.hook"; import { useGetProjectsByProjectIdQuery } from "../projectsV2/api/projectV2.enhanced-api"; +import { useGetSessionLaunchersByLauncherIdQuery as useGetProjectSessionLauncherQuery } from "../sessionsV2/api/sessionLaunchersV2.api"; import ActiveSessionButton from "../sessionsV2/components/SessionButton/ActiveSessionButton"; import { SessionStatusV2Description, diff --git a/client/src/features/session/useWaitForSessionStatus.hook.ts b/client/src/features/session/useWaitForSessionStatus.hook.ts index c88a267a44..4679a13044 100644 --- a/client/src/features/session/useWaitForSessionStatus.hook.ts +++ b/client/src/features/session/useWaitForSessionStatus.hook.ts @@ -17,9 +17,8 @@ */ import { useEffect, useMemo, useState } from "react"; -import { useGetSessionsQuery } from "./sessions.api"; -// import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/sessionsV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../sessionsV2/api/sessionsV2.api"; +import { useGetSessionsQuery } from "./sessions.api"; import { SessionStatusState } from "./sessions.types"; const DEFAULT_POLLING_INTERVAL_MS = 5_000; diff --git a/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx b/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx index 79fd2b774f..7a0ca31de9 100644 --- a/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/DeleteSessionLauncherModal.tsx @@ -21,10 +21,10 @@ import { useCallback, useEffect } from "react"; import { Trash, XLg } from "react-bootstrap-icons"; import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { WarnAlert } from "../../components/Alert"; import { RtkErrorAlert } from "../../components/errors/RtkErrorAlert"; import { useDeleteSessionLaunchersByLauncherIdMutation as useDeleteSessionLauncherMutation } from "./api/sessionLaunchersV2.api"; import { SessionLauncher } from "./sessionsV2.types"; -import { WarnAlert } from "../../components/Alert"; interface DeleteSessionLauncherModalProps { isOpen: boolean; diff --git a/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx b/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx index 99f6add6ea..6267782fd0 100644 --- a/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx +++ b/client/src/features/sessionsV2/PauseOrDeleteSessionModal.tsx @@ -26,20 +26,22 @@ import { useParams, } from "react-router-dom-v5-compat"; import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; + +import { Loader } from "../../components/Loader"; import { User } from "../../model/renkuModels.types"; import { NOTIFICATION_TOPICS } from "../../notifications/Notifications.constants"; import { NotificationsManager } from "../../notifications/notifications.types"; import { ABSOLUTE_ROUTES } from "../../routing/routes.constants"; import AppContext from "../../utils/context/appContext"; import useLegacySelector from "../../utils/customHooks/useLegacySelector.hook"; -import styles from "../session/components/SessionModals.module.scss"; import { useWaitForSessionStatusV2 } from "../session/useWaitForSessionStatus.hook"; import { usePatchSessionsBySessionIdMutation as usePatchSessionMutation, useDeleteSessionsBySessionIdMutation as useStopSessionMutation, } from "./api/sessionsV2.api"; import { SessionV2 } from "./sessionsV2.types"; -import { Loader } from "../../components/Loader"; + +import styles from "../session/components/SessionModals.module.scss"; interface PauseOrDeleteSessionModalProps { action?: "pause" | "delete"; diff --git a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx index 9cc167469c..e7c53740e9 100644 --- a/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx +++ b/client/src/features/sessionsV2/SessionList/SessionItemDisplay.tsx @@ -21,8 +21,8 @@ import { useCallback, useMemo } from "react"; import useLocationHash from "../../../utils/customHooks/useLocationHash.hook"; import { Project } from "../../projectsV2/api/projectV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "../api/sessionsV2.api"; -import { SessionView } from "../SessionView/SessionView"; import { SessionLauncher } from "../sessionsV2.types"; +import { SessionView } from "../SessionView/SessionView"; import SessionItem from "./SessionItem"; interface SessionLauncherDisplayProps { diff --git a/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx b/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx index 4b21c73872..c595047817 100644 --- a/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx +++ b/client/src/features/sessionsV2/SessionShowPage/SessionPaused.tsx @@ -29,7 +29,6 @@ import { NOTIFICATION_TOPICS } from "../../../notifications/Notifications.consta import type { NotificationsManager } from "../../../notifications/notifications.types"; import { ABSOLUTE_ROUTES } from "../../../routing/routes.constants"; import AppContext from "../../../utils/context/appContext"; -// import { usePatchSessionMutation } from "../sessionsV2.api"; import { usePatchSessionsBySessionIdMutation as usePatchSessionMutation } from "../api/sessionsV2.api"; import type { SessionV2 } from "../sessionsV2.types"; diff --git a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx index 3247d703ee..ac34bee6cf 100644 --- a/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx +++ b/client/src/features/sessionsV2/SessionShowPage/ShowSessionPage.tsx @@ -43,6 +43,7 @@ import { ModalHeader, UncontrolledTooltip, } from "reactstrap"; + import { Loader } from "../../../components/Loader"; import EnvironmentLogsV2 from "../../../components/LogsV2"; import { TimeCaption } from "../../../components/TimeCaption"; @@ -57,16 +58,17 @@ import { displaySlice, resetFavicon, setFavicon } from "../../display"; import { useGetNamespacesByNamespaceProjectsAndSlugQuery } from "../../projectsV2/api/projectV2.enhanced-api"; import SessionUnavailable from "../../session/components/SessionUnavailable"; import { SessionRowResourceRequests } from "../../session/components/SessionsList"; -import styles from "../../session/components/ShowSession.module.scss"; import { StartSessionProgressBarV2 } from "../../session/components/StartSessionProgressBar"; import PauseOrDeleteSessionModal from "../PauseOrDeleteSessionModal"; -import { getSessionFavicon } from "../session.utils"; import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "../api/sessionLaunchersV2.api"; import { useGetSessionsQuery } from "../api/sessionsV2.api"; +import { getSessionFavicon } from "../session.utils"; import { SessionV2 } from "../sessionsV2.types"; import SessionIframe from "./SessionIframe"; import SessionPaused from "./SessionPaused"; +import styles from "../../session/components/ShowSession.module.scss"; + export default function ShowSessionPage() { const dispatch = useAppDispatch(); const { diff --git a/client/src/features/sessionsV2/SessionStartPage.tsx b/client/src/features/sessionsV2/SessionStartPage.tsx index cf1875915d..94604a38f3 100644 --- a/client/src/features/sessionsV2/SessionStartPage.tsx +++ b/client/src/features/sessionsV2/SessionStartPage.tsx @@ -28,6 +28,7 @@ import { useParams, useSearchParams, } from "react-router-dom-v5-compat"; + import { ErrorAlert } from "../../components/Alert"; import PageLoader from "../../components/PageLoader"; import { @@ -57,12 +58,12 @@ import { useGetNamespacesByNamespaceProjectsAndSlugQuery } from "../projectsV2/a import { storageSecretNameToFieldName } from "../secretsV2/secrets.utils"; import DataConnectorSecretsModal from "./DataConnectorSecretsModal"; import SessionSecretsModal from "./SessionSecretsModal"; -import { SelectResourceClassModal } from "./components/SessionModals/SelectResourceClass"; import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "./api/sessionLaunchersV2.api"; import { - usePostSessionsMutation as useLaunchSessionMutation, useGetSessionsImagesQuery as useGetDockerImageQuery, + usePostSessionsMutation as useLaunchSessionMutation, } from "./api/sessionsV2.api"; +import { SelectResourceClassModal } from "./components/SessionModals/SelectResourceClass"; import { SessionLauncher } from "./sessionsV2.types"; import startSessionOptionsV2Slice from "./startSessionOptionsV2.slice"; import { diff --git a/client/src/features/sessionsV2/SessionsV2.tsx b/client/src/features/sessionsV2/SessionsV2.tsx index bea45492e9..589ba4f2a2 100644 --- a/client/src/features/sessionsV2/SessionsV2.tsx +++ b/client/src/features/sessionsV2/SessionsV2.tsx @@ -43,9 +43,9 @@ import DeleteSessionV2Modal from "./DeleteSessionLauncherModal"; import SessionItem from "./SessionList/SessionItem"; import { SessionItemDisplay } from "./SessionList/SessionItemDisplay"; import { SessionView } from "./SessionView/SessionView"; -import UpdateSessionLauncherModal from "./components/SessionModals/UpdateSessionLauncherModal"; import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionLaunchersQuery } from "./api/sessionLaunchersV2.api"; import { useGetSessionsQuery as useGetSessionsQueryV2 } from "./api/sessionsV2.api"; +import UpdateSessionLauncherModal from "./components/SessionModals/UpdateSessionLauncherModal"; import { SessionLauncher, SessionV2 } from "./sessionsV2.types"; // Required for logs formatting diff --git a/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx b/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx index 8c6ff9eafc..3444e1ebe7 100644 --- a/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx +++ b/client/src/features/sessionsV2/components/SessionForm/EditLauncherFormContent.tsx @@ -31,10 +31,9 @@ import { Collapse, Input, Label, ListGroup } from "reactstrap"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon"; import { Loader } from "../../../../components/Loader"; +import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery } from "../../api/sessionLaunchersV2.api"; import { CONTAINER_IMAGE_PATTERN } from "../../session.constants"; import { prioritizeSelectedEnvironment } from "../../session.utils"; -// import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; -import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery } from "../../api/sessionLaunchersV2.api"; import { SessionLauncherForm } from "../../sessionsV2.types"; import { AdvancedSettingsFields } from "./AdvancedSettingsFields"; import { EnvironmentKindField } from "./EnvironmentKindField"; diff --git a/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx b/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx index f7c6804e29..fe45f45d0b 100644 --- a/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx +++ b/client/src/features/sessionsV2/components/SessionForm/GlobalEnvironmentFields.tsx @@ -15,13 +15,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import cx from "classnames"; import { Controller } from "react-hook-form"; import { Input, ListGroup } from "reactstrap"; + import { WarnAlert } from "../../../../components/Alert"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; import { Loader } from "../../../../components/Loader"; -// import { useGetSessionEnvironmentsQuery } from "../../sessionsV2.api"; import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery } from "../../api/sessionLaunchersV2.api"; import { EnvironmentFieldsProps } from "./EnvironmentField"; import { SessionEnvironmentItem } from "./SessionEnvironmentItem"; diff --git a/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx b/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx index c9fc57092f..4443be3d28 100644 --- a/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/NewSessionLauncherModal.tsx @@ -30,16 +30,17 @@ import { ModalFooter, ModalHeader, } from "reactstrap"; + import { SuccessAlert } from "../../../../components/Alert"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; import { Loader } from "../../../../components/Loader"; import { useGetNamespacesByNamespaceProjectsAndSlugQuery } from "../../../projectsV2/api/projectV2.enhanced-api"; -import { DEFAULT_PORT, DEFAULT_URL } from "../../session.constants"; -import { getFormattedEnvironmentValues } from "../../session.utils"; import { - useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery, usePostSessionLaunchersMutation as useAddSessionLauncherMutation, + useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery, } from "../../api/sessionLaunchersV2.api"; +import { DEFAULT_PORT, DEFAULT_URL } from "../../session.constants"; +import { getFormattedEnvironmentValues } from "../../session.utils"; import { SessionLauncherForm } from "../../sessionsV2.types"; import { EnvironmentFields } from "../SessionForm/EnvironmentField"; import { LauncherDetailsFields } from "../SessionForm/LauncherDetailsFields"; diff --git a/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx b/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx index de52c949b7..c1411f2f59 100644 --- a/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/UpdateSessionLauncherModal.tsx @@ -28,17 +28,18 @@ import { ModalFooter, ModalHeader, } from "reactstrap"; + import { SuccessAlert } from "../../../../components/Alert"; import { Loader } from "../../../../components/Loader"; import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert"; -import { - getFormattedEnvironmentValues, - getLauncherDefaultValues, -} from "../../session.utils"; import { useGetEnvironmentsQuery as useGetSessionEnvironmentsQuery, usePatchSessionLaunchersByLauncherIdMutation as useUpdateSessionLauncherMutation, } from "../../api/sessionLaunchersV2.api"; +import { + getFormattedEnvironmentValues, + getLauncherDefaultValues, +} from "../../session.utils"; import { SessionLauncher, SessionLauncherForm } from "../../sessionsV2.types"; import EditLauncherFormContent from "../SessionForm/EditLauncherFormContent"; diff --git a/client/src/utils/customHooks/UseGetSessionLogs.ts b/client/src/utils/customHooks/UseGetSessionLogs.ts index a55930aee0..8d095cdb6f 100644 --- a/client/src/utils/customHooks/UseGetSessionLogs.ts +++ b/client/src/utils/customHooks/UseGetSessionLogs.ts @@ -15,10 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { useEffect, useState } from "react"; + +import { ILogs } from "../../components/Logs"; import { useGetLogsQuery } from "../../features/session/sessions.api"; import { useGetSessionsBySessionIdLogsQuery as useGetLogsQueryV2 } from "../../features/sessionsV2/api/sessionsV2.api"; -import { ILogs } from "../../components/Logs"; /** * useGetSessionLogs custom hook From 102b81e848380a30bc2ea3f4d222b80d02e9174a Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Wed, 29 Jan 2025 13:30:58 +0100 Subject: [PATCH 11/11] fix explode query args --- .../sessionsV2/api/sessionLaunchersV2.api.ts | 105 +++++++++++------- .../support/renkulab-fixtures/newSession.ts | 2 +- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts index ab4f1240fc..94b18943f6 100644 --- a/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.api.ts @@ -16,52 +16,71 @@ * limitations under the License. */ -import { sessionLaunchersV2GeneratedApi } from "./sessionLaunchersV2.generated-api"; +import { + type GetEnvironmentsApiArg, + type GetEnvironmentsApiResponse, + sessionLaunchersV2GeneratedApi, +} from "./sessionLaunchersV2.generated-api"; + +// Fixes some API endpoints +const withFixedEndpoints = sessionLaunchersV2GeneratedApi.injectEndpoints({ + overrideExisting: true, + endpoints: (build) => ({ + getEnvironments: build.query< + GetEnvironmentsApiResponse, + GetEnvironmentsApiArg + >({ + query: ({ getEnvironmentParams }) => ({ + url: `/environments`, + params: getEnvironmentParams, + }), + }), + }), +}); // Adds tag handling for cache management -export const sessionLaunchersV2Api = - sessionLaunchersV2GeneratedApi.enhanceEndpoints({ - addTagTypes: ["Environment", "Launcher"], - endpoints: { - getEnvironments: { - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ - id, - type: "Environment" as const, - })), - "Environment", - ] - : ["Environment"], - }, - getSessionLaunchersByLauncherId: { - providesTags: (result) => - result - ? [{ id: result.id, type: "Launcher" }, "Launcher"] - : ["Launcher"], - }, - postSessionLaunchers: { - invalidatesTags: ["Launcher"], - }, - patchSessionLaunchersByLauncherId: { - invalidatesTags: (result) => - result ? [{ id: result.id, type: "Launcher" }] : ["Launcher"], - }, - deleteSessionLaunchersByLauncherId: { - invalidatesTags: ["Launcher"], - }, - getProjectsByProjectIdSessionLaunchers: { - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ id, type: "Launcher" as const })), - "Launcher", - ] - : ["Launcher"], - }, +export const sessionLaunchersV2Api = withFixedEndpoints.enhanceEndpoints({ + addTagTypes: ["Environment", "Launcher"], + endpoints: { + getEnvironments: { + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ + id, + type: "Environment" as const, + })), + "Environment", + ] + : ["Environment"], + }, + getSessionLaunchersByLauncherId: { + providesTags: (result) => + result + ? [{ id: result.id, type: "Launcher" }, "Launcher"] + : ["Launcher"], + }, + postSessionLaunchers: { + invalidatesTags: ["Launcher"], + }, + patchSessionLaunchersByLauncherId: { + invalidatesTags: (result) => + result ? [{ id: result.id, type: "Launcher" }] : ["Launcher"], + }, + deleteSessionLaunchersByLauncherId: { + invalidatesTags: ["Launcher"], + }, + getProjectsByProjectIdSessionLaunchers: { + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ id, type: "Launcher" as const })), + "Launcher", + ] + : ["Launcher"], }, - }); + }, +}); export const { // "environments" hooks diff --git a/tests/cypress/support/renkulab-fixtures/newSession.ts b/tests/cypress/support/renkulab-fixtures/newSession.ts index 92fda5d718..072a24a125 100644 --- a/tests/cypress/support/renkulab-fixtures/newSession.ts +++ b/tests/cypress/support/renkulab-fixtures/newSession.ts @@ -128,7 +128,7 @@ export function NewSession(Parent: T) { name = "getEnvironments", } = args ?? {}; const response = { fixture }; - cy.intercept("GET", `/api/data/environments*`, response).as(name); + cy.intercept("GET", `/api/data/environments`, response).as(name); return this; } };