feat(ws): add frontend hooks to call create WS backend (#217)

* feat(ws): Notebooks 2.0 // Frontend // Call To Create Workspace

Signed-off-by: Yossi Elias (EXT-Nokia) <yossi.elias.ext@nokia.com>

* Create types for WorkspacePodTemplate and adjust camelCase leftovers

Signed-off-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com>

---------

Signed-off-by: Yossi Elias (EXT-Nokia) <yossi.elias.ext@nokia.com>
Signed-off-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com>
Co-authored-by: Yossi Elias (EXT-Nokia) <yossi.elias.ext@nokia.com>
Co-authored-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com>
Co-authored-by: senanz <33982987+senanz@users.noreply.github.com>
This commit is contained in:
Yossi Elias 2025-05-06 11:13:38 -04:00 committed by GitHub
parent 6123650b86
commit 744c3bb900
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 114 additions and 5 deletions

View File

@ -1,7 +1,7 @@
import React from 'react';
import { APIState } from '~/shared/api/types';
import { NotebookAPIs } from '~/app/types';
import { getNamespaces, getWorkspaceKinds } from '~/shared/api/notebookService';
import { getNamespaces, getWorkspaceKinds, createWorkspace } from '~/shared/api/notebookService';
import useAPIState from '~/shared/api/useAPIState';
export type NotebookAPIState = APIState<NotebookAPIs>;
@ -13,6 +13,7 @@ const useNotebookAPIState = (
(path: string) => ({
getNamespaces: getNamespaces(path),
getWorkspaceKinds: getWorkspaceKinds(path),
createWorkspace: createWorkspace(path),
}),
[],
);

View File

@ -0,0 +1,34 @@
import * as React from 'react';
import useFetchState, {
FetchState,
FetchStateCallbackPromise,
} from '~/shared/utilities/useFetchState';
import { CreateWorkspaceData } from '~/app/types';
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
import { Workspace } from '~/shared/types';
import { createWorkspaceCall } from '~/app/pages/Workspaces/utils';
import { APIOptions } from '~/shared/api/types';
const useCreateWorkspace = (
namespace: string,
formData: CreateWorkspaceData,
): FetchState<Workspace | null> => {
const { api, apiAvailable } = useNotebookAPI();
const call = React.useCallback<FetchStateCallbackPromise<Workspace | null>>(
(opts: APIOptions) => {
if (!apiAvailable) {
return Promise.reject(new Error('API not yet available'));
}
if (!namespace) {
return Promise.reject(new Error('namespace is not available yet'));
}
return createWorkspaceCall(opts, api, formData, namespace).then((result) => result.workspace);
},
[api, apiAvailable, namespace, formData],
);
return useFetchState(call, null);
};
export default useCreateWorkspace;

View File

@ -0,0 +1,18 @@
import { Workspace } from '~/shared/types';
import { CreateWorkspaceData } from '~/app/types';
import { NotebookAPIState } from '~/app/context/useNotebookAPIState';
import { APIOptions } from '~/shared/api/types';
export type RegisterWorkspaceCreatedResources = {
workspace: Workspace;
};
export const createWorkspaceCall = async (
opts: APIOptions,
api: NotebookAPIState['api'],
formData: CreateWorkspaceData,
namespace: string,
): Promise<RegisterWorkspaceCreatedResources> => {
const workspace = await api.createWorkspace(opts, formData, namespace);
return { workspace };
};

View File

@ -1,5 +1,5 @@
import { APIOptions } from '~/shared/api/types';
import { WorkspaceKind } from '~/shared/types';
import { Workspace, WorkspaceKind, WorkspacePodTemplateMutate } from '~/shared/types';
export type ResponseBody<T> = {
data: T;
@ -67,7 +67,24 @@ export type GetNamespaces = (opts: APIOptions) => Promise<NamespacesList>;
export type GetWorkspaceKinds = (opts: APIOptions) => Promise<WorkspaceKind[]>;
export type CreateWorkspace = (
opts: APIOptions,
data: CreateWorkspaceData,
namespace: string,
) => Promise<Workspace>;
export type NotebookAPIs = {
getNamespaces: GetNamespaces;
getWorkspaceKinds: GetWorkspaceKinds;
createWorkspace: CreateWorkspace;
};
export type CreateWorkspaceData = {
data: {
name: string;
kind: string;
paused: boolean;
deferUpdates: boolean;
podTemplate: WorkspacePodTemplateMutate;
};
};

View File

@ -1,8 +1,8 @@
import { NamespacesList } from '~/app/types';
import { isNotebookResponse, restGET } from '~/shared/api/apiUtils';
import { NamespacesList, CreateWorkspaceData } from '~/app/types';
import { isNotebookResponse, restGET, restCREATE } from '~/shared/api/apiUtils';
import { APIOptions } from '~/shared/api/types';
import { handleRestFailures } from '~/shared/api/errorUtils';
import { WorkspaceKind } from '~/shared/types';
import { Workspace, WorkspaceKind } from '~/shared/types';
export const getNamespaces =
(hostPath: string) =>
@ -23,3 +23,15 @@ export const getWorkspaceKinds =
}
throw new Error('Invalid response format');
});
export const createWorkspace =
(hostPath: string) =>
(opts: APIOptions, data: CreateWorkspaceData, namespace = ''): Promise<Workspace> =>
handleRestFailures(restCREATE(hostPath, `/workspaces/${namespace}`, data, opts)).then(
(response) => {
if (isNotebookResponse<Workspace>(response)) {
return response.data;
}
throw new Error('Invalid response format');
},
);

View File

@ -93,6 +93,33 @@ export interface WorkspaceStatus {
stateMessage: string;
}
export interface WorkspacePodMetadataMutate {
labels: Record<string, string>;
annotations: Record<string, string>;
}
export interface WorkspacePodVolumeMount {
pvcName: string;
mountPath: string;
readOnly?: boolean;
}
export interface WorkspacePodVolumesMutate {
home?: string;
data: WorkspacePodVolumeMount[];
}
export interface WorkspacePodTemplateOptionsMutate {
imageConfig: string;
podConfig: string;
}
export interface WorkspacePodTemplateMutate {
podMetadata: WorkspacePodMetadataMutate;
volumes: WorkspacePodVolumesMutate;
options: WorkspacePodTemplateOptionsMutate;
}
export interface Workspace {
name: string;
namespace: string;