diff --git a/workspaces/frontend/src/app/context/useNotebookAPIState.tsx b/workspaces/frontend/src/app/context/useNotebookAPIState.tsx index 983f918..6347618 100644 --- a/workspaces/frontend/src/app/context/useNotebookAPIState.tsx +++ b/workspaces/frontend/src/app/context/useNotebookAPIState.tsx @@ -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; @@ -13,6 +13,7 @@ const useNotebookAPIState = ( (path: string) => ({ getNamespaces: getNamespaces(path), getWorkspaceKinds: getWorkspaceKinds(path), + createWorkspace: createWorkspace(path), }), [], ); diff --git a/workspaces/frontend/src/app/hooks/useCreateWorkspace.ts b/workspaces/frontend/src/app/hooks/useCreateWorkspace.ts new file mode 100644 index 0000000..0e2cfc1 --- /dev/null +++ b/workspaces/frontend/src/app/hooks/useCreateWorkspace.ts @@ -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 => { + const { api, apiAvailable } = useNotebookAPI(); + + const call = React.useCallback>( + (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; diff --git a/workspaces/frontend/src/app/pages/Workspaces/utils.ts b/workspaces/frontend/src/app/pages/Workspaces/utils.ts new file mode 100644 index 0000000..a87fb3a --- /dev/null +++ b/workspaces/frontend/src/app/pages/Workspaces/utils.ts @@ -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 => { + const workspace = await api.createWorkspace(opts, formData, namespace); + return { workspace }; +}; diff --git a/workspaces/frontend/src/app/types.ts b/workspaces/frontend/src/app/types.ts index 26ba3e1..d3c0817 100644 --- a/workspaces/frontend/src/app/types.ts +++ b/workspaces/frontend/src/app/types.ts @@ -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 = { data: T; @@ -67,7 +67,24 @@ export type GetNamespaces = (opts: APIOptions) => Promise; export type GetWorkspaceKinds = (opts: APIOptions) => Promise; +export type CreateWorkspace = ( + opts: APIOptions, + data: CreateWorkspaceData, + namespace: string, +) => Promise; + export type NotebookAPIs = { getNamespaces: GetNamespaces; getWorkspaceKinds: GetWorkspaceKinds; + createWorkspace: CreateWorkspace; +}; + +export type CreateWorkspaceData = { + data: { + name: string; + kind: string; + paused: boolean; + deferUpdates: boolean; + podTemplate: WorkspacePodTemplateMutate; + }; }; diff --git a/workspaces/frontend/src/shared/api/notebookService.ts b/workspaces/frontend/src/shared/api/notebookService.ts index e676c22..8101736 100644 --- a/workspaces/frontend/src/shared/api/notebookService.ts +++ b/workspaces/frontend/src/shared/api/notebookService.ts @@ -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 => + handleRestFailures(restCREATE(hostPath, `/workspaces/${namespace}`, data, opts)).then( + (response) => { + if (isNotebookResponse(response)) { + return response.data; + } + throw new Error('Invalid response format'); + }, + ); diff --git a/workspaces/frontend/src/shared/types.ts b/workspaces/frontend/src/shared/types.ts index 8adc53a..7fdf8ab 100644 --- a/workspaces/frontend/src/shared/types.ts +++ b/workspaces/frontend/src/shared/types.ts @@ -93,6 +93,33 @@ export interface WorkspaceStatus { stateMessage: string; } +export interface WorkspacePodMetadataMutate { + labels: Record; + annotations: Record; +} + +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;