From 63bcd38ecf4aca01ee60affad0c345017faabcec Mon Sep 17 00:00:00 2001 From: Guilherme Caponetto <638737+caponetto@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:28:00 -0300 Subject: [PATCH] refactor: remove redundant code replaced by shared library Signed-off-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com> --- workspaces/frontend/src/__mocks__/utils.ts | 7 - .../cypress/tests/mocked/application.cy.ts | 6 +- .../workspaces/WorkspaceDetailsActivity.cy.ts | 8 +- .../tests/mocked/workspaces/Workspaces.cy.ts | 18 +- .../workspaces/filterWorkspacesTest.cy.ts | 12 +- workspaces/frontend/src/app/AppRoutes.tsx | 6 +- .../src/app/context/useNotebookAPIState.tsx | 3 +- .../hooks/__tests__/useNamespaces.spec.tsx | 52 ------ .../frontend/src/app/hooks/useNamespaces.ts | 23 --- .../src/app/hooks/useWorkspaceCountPerKind.ts | 5 +- .../WorkspaceKinds/Form/WorkspaceKindForm.tsx | 2 +- .../details/WorkspaceKindDetailsImages.tsx | 6 +- .../WorkspaceKindDetailsNamespaces.tsx | 28 ++- .../WorkspaceKindDetailsPodConfigs.tsx | 6 +- .../src/app/pages/notFound/NotFound.tsx | 2 + workspaces/frontend/src/app/routes.ts | 4 + .../frontend/src/shared/api/apiUtils.ts | 4 +- workspaces/frontend/src/shared/api/types.ts | 16 +- .../frontend/src/shared/api/useAPIState.ts | 32 ---- workspaces/frontend/src/shared/typeHelpers.ts | 160 ------------------ .../frontend/src/shared/utilities/types.ts | 11 -- 21 files changed, 53 insertions(+), 358 deletions(-) delete mode 100644 workspaces/frontend/src/__mocks__/utils.ts delete mode 100644 workspaces/frontend/src/app/hooks/__tests__/useNamespaces.spec.tsx delete mode 100644 workspaces/frontend/src/app/hooks/useNamespaces.ts delete mode 100644 workspaces/frontend/src/shared/api/useAPIState.ts delete mode 100644 workspaces/frontend/src/shared/typeHelpers.ts diff --git a/workspaces/frontend/src/__mocks__/utils.ts b/workspaces/frontend/src/__mocks__/utils.ts deleted file mode 100644 index 18538b431..000000000 --- a/workspaces/frontend/src/__mocks__/utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -interface Envelope { - data: T; -} - -export const mockBFFResponse = (data: T): Envelope => ({ - data, -}); diff --git a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/application.cy.ts b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/application.cy.ts index 52134087b..0f4a82ebd 100644 --- a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/application.cy.ts +++ b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/application.cy.ts @@ -1,7 +1,7 @@ +import { mockModArchResponse } from 'mod-arch-core'; import { pageNotfound } from '~/__tests__/cypress/cypress/pages/pageNotFound'; import { home } from '~/__tests__/cypress/cypress/pages/home'; import { mockNamespaces } from '~/__mocks__/mockNamespaces'; -import { mockBFFResponse } from '~/__mocks__/utils'; import { mockWorkspace1 } from '~/shared/mock/mockNotebookServiceData'; import { navBar } from '~/__tests__/cypress/cypress/pages/navBar'; @@ -12,10 +12,10 @@ describe('Application', () => { it('Home page should have primary button', () => { cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); cy.intercept('GET', `/api/v1/workspaces/${mockNamespaces[0].name}`, { - body: mockBFFResponse([mockWorkspace1]), + body: mockModArchResponse([mockWorkspace1]), }).as('getWorkspaces'); home.visit(); diff --git a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/WorkspaceDetailsActivity.cy.ts b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/WorkspaceDetailsActivity.cy.ts index f1a22546e..a0c1ac4c2 100644 --- a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/WorkspaceDetailsActivity.cy.ts +++ b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/WorkspaceDetailsActivity.cy.ts @@ -1,4 +1,4 @@ -import { mockBFFResponse } from '~/__mocks__/utils'; +import { mockModArchResponse } from 'mod-arch-core'; import { mockNamespaces } from '~/__mocks__/mockNamespaces'; import { mockWorkspaces } from '~/__tests__/cypress/cypress/tests/mocked/workspace.mock'; import { navBar } from '~/__tests__/cypress/cypress/pages/navBar'; @@ -6,13 +6,13 @@ import { navBar } from '~/__tests__/cypress/cypress/pages/navBar'; describe('WorkspaceDetailsActivity Component', () => { beforeEach(() => { cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); cy.intercept('GET', '/api/v1/workspaces', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getWorkspaces'); cy.intercept('GET', '/api/v1/workspaces/default', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getDefaultWorkspaces'); cy.visit('/'); cy.wait('@getNamespaces'); diff --git a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/Workspaces.cy.ts b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/Workspaces.cy.ts index 6da7b7e62..8439deee0 100644 --- a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/Workspaces.cy.ts +++ b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/Workspaces.cy.ts @@ -1,5 +1,5 @@ +import { mockModArchResponse } from 'mod-arch-core'; import { mockNamespaces } from '~/__mocks__/mockNamespaces'; -import { mockBFFResponse } from '~/__mocks__/utils'; import { home } from '~/__tests__/cypress/cypress/pages/home'; import { mockWorkspaces, @@ -26,11 +26,11 @@ describe('Workspaces Tests', () => { home.visit(); cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); cy.intercept('GET', '/api/v1/workspaces', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getWorkspaces'); cy.wait('@getWorkspaces'); }); @@ -65,15 +65,15 @@ describe('Workspace by namespace functionality', () => { home.visit(); cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); - cy.intercept('GET', '/api/v1/workspaces', { body: mockBFFResponse(mockWorkspaces) }).as( + cy.intercept('GET', '/api/v1/workspaces', { body: mockModArchResponse(mockWorkspaces) }).as( 'getWorkspaces', ); cy.intercept('GET', '/api/v1/workspaces/kubeflow', { - body: mockBFFResponse(mockWorkspacesByNS), + body: mockModArchResponse(mockWorkspacesByNS), }).as('getKubeflowWorkspaces'); cy.wait('@getNamespaces'); @@ -107,14 +107,14 @@ describe('Workspaces Component', () => { cy.visit('/'); cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); cy.wait('@getNamespaces'); cy.intercept('GET', '/api/v1/workspaces', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getWorkspaces'); cy.intercept('GET', '/api/v1/workspaces/kubeflow', { - body: mockBFFResponse(mockWorkspacesByNS), + body: mockModArchResponse(mockWorkspacesByNS), }); }); diff --git a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts index ded6c2277..4ff6f4ff0 100644 --- a/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts +++ b/workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspaces/filterWorkspacesTest.cy.ts @@ -1,6 +1,6 @@ +import { mockModArchResponse } from 'mod-arch-core'; import { mockNamespaces } from '~/__mocks__/mockNamespaces'; import { mockWorkspaces } from '~/__mocks__/mockWorkspaces'; -import { mockBFFResponse } from '~/__mocks__/utils'; import { home } from '~/__tests__/cypress/cypress/pages/home'; import { navBar } from '~/__tests__/cypress/cypress/pages/navBar'; import { mockWorkspaceKinds } from '~/shared/mock/mockNotebookServiceData'; @@ -16,19 +16,19 @@ const useFilter = (filterKey: string, filterName: string, searchValue: string) = describe('Application', () => { beforeEach(() => { cy.intercept('GET', '/api/v1/namespaces', { - body: mockBFFResponse(mockNamespaces), + body: mockModArchResponse(mockNamespaces), }).as('getNamespaces'); cy.intercept('GET', '/api/v1/workspaces', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getWorkspaces'); cy.intercept('GET', '/api/v1/workspaces/default', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }).as('getDefaultWorkspaces'); cy.intercept('GET', '/api/v1/workspaces/custom-namespace', { - body: mockBFFResponse(mockWorkspaces), + body: mockModArchResponse(mockWorkspaces), }); cy.intercept('GET', '/api/v1/workspacekinds', { - body: mockBFFResponse(mockWorkspaceKinds), + body: mockModArchResponse(mockWorkspaceKinds), }); home.visit(); cy.wait('@getNamespaces'); diff --git a/workspaces/frontend/src/app/AppRoutes.tsx b/workspaces/frontend/src/app/AppRoutes.tsx index 2de7b7c7b..918d74658 100644 --- a/workspaces/frontend/src/app/AppRoutes.tsx +++ b/workspaces/frontend/src/app/AppRoutes.tsx @@ -37,7 +37,7 @@ export const useAdminDebugSettings = (): NavDataItem[] => { return [ { label: 'Debug', - children: [{ label: 'Notebooks', path: '/notebookDebugSettings' }], + children: [{ label: 'Notebooks', path: AppRoutePaths.notebookDebugSettings }], }, { label: 'Workspace kinds', @@ -70,8 +70,10 @@ const AppRoutes: React.FC = () => { path={AppRoutePaths.root} element={} /> + {user?.clusterAdmin && ( + } /> + )} } /> - {user?.clusterAdmin && } />} ); }; diff --git a/workspaces/frontend/src/app/context/useNotebookAPIState.tsx b/workspaces/frontend/src/app/context/useNotebookAPIState.tsx index fa8db8b1e..319775f8d 100644 --- a/workspaces/frontend/src/app/context/useNotebookAPIState.tsx +++ b/workspaces/frontend/src/app/context/useNotebookAPIState.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react'; +import { useAPIState, APIState } from 'mod-arch-core'; import { NotebookApis, notebookApisImpl } from '~/shared/api/notebookApi'; -import { APIState } from '~/shared/api/types'; -import useAPIState from '~/shared/api/useAPIState'; import { mockNotebookApisImpl } from '~/shared/mock/mockNotebookApis'; import { MOCK_API_ENABLED } from '~/shared/utilities/const'; diff --git a/workspaces/frontend/src/app/hooks/__tests__/useNamespaces.spec.tsx b/workspaces/frontend/src/app/hooks/__tests__/useNamespaces.spec.tsx deleted file mode 100644 index 74c8618f7..000000000 --- a/workspaces/frontend/src/app/hooks/__tests__/useNamespaces.spec.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { renderHook } from '~/__tests__/unit/testUtils/hooks'; -import useNamespaces from '~/app/hooks/useNamespaces'; -import { useNotebookAPI } from '~/app/hooks/useNotebookAPI'; -import { NotebookApis } from '~/shared/api/notebookApi'; -import { APIState } from '~/shared/api/types'; - -jest.mock('~/app/hooks/useNotebookAPI', () => ({ - useNotebookAPI: jest.fn(), -})); - -const mockUseNotebookAPI = useNotebookAPI as jest.MockedFunction; - -describe('useNamespaces', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('rejects when API not available', async () => { - const unavailableState: APIState = { - apiAvailable: false, - api: {} as NotebookApis, - }; - mockUseNotebookAPI.mockReturnValue({ ...unavailableState, refreshAllAPI: jest.fn() }); - - const { result, waitForNextUpdate } = renderHook(() => useNamespaces()); - await waitForNextUpdate(); - - const [namespacesData, loaded, loadError] = result.current; - expect(namespacesData).toBeNull(); - expect(loaded).toBe(false); - expect(loadError).toBeDefined(); - }); - - it('returns data when API is available', async () => { - const listNamespaces = jest.fn().mockResolvedValue({ ok: true, data: [{ name: 'ns1' }] }); - const api = { namespaces: { listNamespaces } } as unknown as NotebookApis; - - const availableState: APIState = { - apiAvailable: true, - api, - }; - mockUseNotebookAPI.mockReturnValue({ ...availableState, refreshAllAPI: jest.fn() }); - - const { result, waitForNextUpdate } = renderHook(() => useNamespaces()); - await waitForNextUpdate(); - - const [namespacesData, loaded, loadError] = result.current; - expect(namespacesData).toEqual([{ name: 'ns1' }]); - expect(loaded).toBe(true); - expect(loadError).toBeUndefined(); - }); -}); diff --git a/workspaces/frontend/src/app/hooks/useNamespaces.ts b/workspaces/frontend/src/app/hooks/useNamespaces.ts deleted file mode 100644 index 1d8067d6a..000000000 --- a/workspaces/frontend/src/app/hooks/useNamespaces.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useCallback } from 'react'; -import { FetchState, FetchStateCallbackPromise, useFetchState } from 'mod-arch-core'; -import { useNotebookAPI } from '~/app/hooks/useNotebookAPI'; -import { ApiNamespaceListEnvelope } from '~/generated/data-contracts'; - -const useNamespaces = (): FetchState => { - const { api, apiAvailable } = useNotebookAPI(); - - const call = useCallback< - FetchStateCallbackPromise - >(async () => { - if (!apiAvailable) { - throw new Error('API not yet available'); - } - - const envelope = await api.namespaces.listNamespaces(); - return envelope.data; - }, [api, apiAvailable]); - - return useFetchState(call, null); -}; - -export default useNamespaces; diff --git a/workspaces/frontend/src/app/hooks/useWorkspaceCountPerKind.ts b/workspaces/frontend/src/app/hooks/useWorkspaceCountPerKind.ts index 22c06d331..00aeeaee2 100644 --- a/workspaces/frontend/src/app/hooks/useWorkspaceCountPerKind.ts +++ b/workspaces/frontend/src/app/hooks/useWorkspaceCountPerKind.ts @@ -4,9 +4,8 @@ import { WorkspaceCountPerOption } from '~/app/types'; import { WorkspacekindsWorkspaceKind, WorkspacesWorkspace } from '~/generated/data-contracts'; import { NotebookApis } from '~/shared/api/notebookApi'; -export type WorkspaceCountPerKind = Record< - WorkspacekindsWorkspaceKind['name'], - WorkspaceCountPerOption +export type WorkspaceCountPerKind = Partial< + Record >; export const useWorkspaceCountPerKind = (): WorkspaceCountPerKind => { diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/Form/WorkspaceKindForm.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/Form/WorkspaceKindForm.tsx index 7ca0a271d..c2955ee02 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/Form/WorkspaceKindForm.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/Form/WorkspaceKindForm.tsx @@ -88,7 +88,7 @@ export const WorkspaceKindForm: React.FC = () => { if (createResult.ok) { notification.success( - `Workspace kind '${createResult.data.data.name}' created successfully`, + `Workspace kind '${createResult.result.data.name}' created successfully`, ); navigate('workspaceKinds'); } else { diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsImages.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsImages.tsx index 1ed3e0106..a90f90ea0 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsImages.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsImages.tsx @@ -20,11 +20,7 @@ export const WorkspaceKindDetailsImages: React.FunctionComponent diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsNamespaces.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsNamespaces.tsx index 336bce1bf..cf8ef6b04 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsNamespaces.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsNamespaces.tsx @@ -12,23 +12,17 @@ export const WorkspaceKindDetailsNamespaces: React.FunctionComponent< WorkspaceDetailsNamespacesProps > = ({ workspaceKind, workspaceCountPerKind }) => ( ({ - id: String(rowIndex), - displayName: namespace, - kindName: workspaceKind.name, - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - workspaceCount: workspaceCountPerKind[workspaceKind.name] - ? workspaceCountPerKind[workspaceKind.name].countByNamespace[namespace] - : 0, - workspaceCountRouteState: { - namespace, - }, - }))} + rows={Object.keys(workspaceCountPerKind[workspaceKind.name]?.countByNamespace ?? {}).map( + (namespace, rowIndex) => ({ + id: String(rowIndex), + displayName: namespace, + kindName: workspaceKind.name, + workspaceCount: workspaceCountPerKind[workspaceKind.name]?.countByNamespace[namespace] ?? 0, + workspaceCountRouteState: { + namespace, + }, + }), + )} tableKind="namespace" /> ); diff --git a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsPodConfigs.tsx b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsPodConfigs.tsx index 0a3a5e5f4..4ee5ad4bc 100644 --- a/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsPodConfigs.tsx +++ b/workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsPodConfigs.tsx @@ -16,10 +16,8 @@ export const WorkspaceKindDetailsPodConfigs: React.FunctionComponent< id: podConfig.id, displayName: podConfig.displayName, kindName: workspaceKind.name, - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - workspaceCount: workspaceCountPerKind[workspaceKind.name] - ? (workspaceCountPerKind[workspaceKind.name].countByPodConfig[podConfig.id] ?? 0) - : 0, + workspaceCount: + workspaceCountPerKind[workspaceKind.name]?.countByPodConfig[podConfig.id] ?? 0, workspaceCountRouteState: { podConfigId: podConfig.id, }, diff --git a/workspaces/frontend/src/app/pages/notFound/NotFound.tsx b/workspaces/frontend/src/app/pages/notFound/NotFound.tsx index 60f61be7b..65cbf61ae 100644 --- a/workspaces/frontend/src/app/pages/notFound/NotFound.tsx +++ b/workspaces/frontend/src/app/pages/notFound/NotFound.tsx @@ -9,6 +9,8 @@ import { import { PageSection } from '@patternfly/react-core/dist/esm/components/Page'; import { useTypedNavigate } from '~/app/routerHelper'; +// TODO: replace with mod-arch-shared NotFound component +// Currently, not possible because mod-arch-shared version assumes home route is '/' const NotFound: React.FunctionComponent = () => { function GoHomeBtn() { const navigate = useTypedNavigate(); diff --git a/workspaces/frontend/src/app/routes.ts b/workspaces/frontend/src/app/routes.ts index 8379d179f..a51f44243 100644 --- a/workspaces/frontend/src/app/routes.ts +++ b/workspaces/frontend/src/app/routes.ts @@ -7,6 +7,7 @@ export const AppRoutePaths = { workspaceKindSummary: '/workspacekinds/:kind/summary', workspaceKindCreate: '/workspacekinds/create', workspaceKindEdit: '/workspacekinds/:kind/edit', + notebookDebugSettings: '/notebookDebugSettings', } satisfies Record; export type AppRoute = (typeof AppRoutePaths)[keyof typeof AppRoutePaths]; @@ -35,6 +36,7 @@ export type RouteParamsMap = { workspaceKindEdit: { kind: string; }; + notebookDebugSettings: undefined; }; /** @@ -69,6 +71,7 @@ export type RouteStateMap = { workspaceKindEdit: { workspaceKindName: string; }; + notebookDebugSettings: undefined; }; /** @@ -90,4 +93,5 @@ export type RouteSearchParamsMap = { workspaceKindSummary: undefined; workspaceKindCreate: undefined; workspaceKindEdit: undefined; + notebookDebugSettings: undefined; }; diff --git a/workspaces/frontend/src/shared/api/apiUtils.ts b/workspaces/frontend/src/shared/api/apiUtils.ts index 196f9ea9a..2aff10d14 100644 --- a/workspaces/frontend/src/shared/api/apiUtils.ts +++ b/workspaces/frontend/src/shared/api/apiUtils.ts @@ -20,8 +20,8 @@ function isApiErrorEnvelope(data: unknown): data is ApiErrorEnvelope { export async function safeApiCall(fn: () => Promise): Promise> { try { - const data = await fn(); - return { ok: true, data }; + const result = await fn(); + return { ok: true, result }; } catch (error: unknown) { if (axios.isAxiosError(error)) { const apiError = error.response?.data; diff --git a/workspaces/frontend/src/shared/api/types.ts b/workspaces/frontend/src/shared/api/types.ts index 6685d1773..b6c822f84 100644 --- a/workspaces/frontend/src/shared/api/types.ts +++ b/workspaces/frontend/src/shared/api/types.ts @@ -1,20 +1,6 @@ import { ApiErrorEnvelope } from '~/generated/data-contracts'; import { ApiConfig, HttpClient } from '~/generated/http-client'; -export type APIOptions = { - dryRun?: boolean; - signal?: AbortSignal; - parseJSON?: boolean; - headers?: Record; -}; - -export type APIState = { - /** If API will successfully call */ - apiAvailable: boolean; - /** The available API functions */ - api: T; -}; - export type RemoveHttpClient = Omit>; export type WithExperimental = TBase & { @@ -24,5 +10,5 @@ export type WithExperimental = TBase & { export type ApiClass = abstract new (config?: ApiConfig) => object; export type ApiInstance = RemoveHttpClient>; export type ApiCallResult = - | { ok: true; data: T } + | { ok: true; result: T } | { ok: false; errorEnvelope: ApiErrorEnvelope }; diff --git a/workspaces/frontend/src/shared/api/useAPIState.ts b/workspaces/frontend/src/shared/api/useAPIState.ts deleted file mode 100644 index 507ffa82e..000000000 --- a/workspaces/frontend/src/shared/api/useAPIState.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useCallback, useMemo, useState } from 'react'; -import { APIState } from '~/shared/api/types'; - -const useAPIState = ( - hostPath: string | null, - createAPI: (path: string) => T, -): [apiState: APIState, refreshAPIState: () => void] => { - const [internalAPIToggleState, setInternalAPIToggleState] = useState(false); - - const refreshAPIState = useCallback(() => { - setInternalAPIToggleState((v) => !v); - }, []); - - const apiState = useMemo>(() => { - let path = hostPath; - if (!path) { - // TODO: we need to figure out maybe a stopgap or something - path = ''; - } - const api = createAPI(path); - - return { - apiAvailable: !!path, - api, - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [createAPI, hostPath, internalAPIToggleState]); - - return [apiState, refreshAPIState]; -}; - -export default useAPIState; diff --git a/workspaces/frontend/src/shared/typeHelpers.ts b/workspaces/frontend/src/shared/typeHelpers.ts deleted file mode 100644 index a590b848b..000000000 --- a/workspaces/frontend/src/shared/typeHelpers.ts +++ /dev/null @@ -1,160 +0,0 @@ -/** - * The type `{}` doesn't mean "any empty object", it means "any non-nullish value". - * - * Use the `AnyObject` type for objects whose structure is unknown. - * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492 - */ -export type AnyObject = Record; - -/** - * Takes a type and makes all properties partial within it. - * - * TODO: Implement the SDK & Patch logic -- this should stop being needed as things will be defined as Patches - */ -export type RecursivePartial = T extends object - ? { - [P in keyof T]?: RecursivePartial; - } - : T; - -/** - * Partial only some properties. - * - * eg. PartialSome - */ -export type PartialSome = Pick, Keys> & - Omit; - -/** - * Unions all values of an object togethers -- antithesis to `keyof myObj`. - */ -export type ValueOf = T[keyof T]; - -/** - * Never allow any properties of `Type`. - * - * Utility type, probably never a reason to export. - */ -type Never = { - [K in keyof Type]?: never; -}; - -/** - * Either TypeA properties or TypeB properties -- never both. - * - * @example - * ```ts - * type MyType = EitherNotBoth<{ foo: boolean }, { bar: boolean }>; - * - * // Valid usages: - * const objA: MyType = { - * foo: true, - * }; - * const objB: MyType = { - * bar: true, - * }; - * - * // TS Error -- can't have both properties: - * const objBoth: MyType = { - * foo: true, - * bar: true, - * }; - * - * // TS Error -- must have at least one property: - * const objNeither: MyType = { - * }; - * ``` - */ -export type EitherNotBoth = (TypeA & Never) | (TypeB & Never); - -/** - * Either TypeA properties or TypeB properties or neither of the properties -- never both. - * - * @example - * ```ts - * type MyType = EitherOrBoth<{ foo: boolean }, { bar: boolean }>; - * - * // Valid usages: - * const objA: MyType = { - * foo: true, - * }; - * const objB: MyType = { - * bar: true, - * }; - * const objBoth: MyType = { - * foo: true, - * bar: true, - * }; - * - * // TS Error -- can't omit both properties: - * const objNeither: MyType = { - * }; - * ``` - */ -export type EitherOrBoth = EitherNotBoth | (TypeA & TypeB); - -/** - * Either TypeA properties or TypeB properties or neither of the properties -- never both. - * - * @example - * ```ts - * type MyType = EitherOrNone<{ foo: boolean }, { bar: boolean }>; - * - * // Valid usages: - * const objA: MyType = { - * foo: true, - * }; - * const objB: MyType = { - * bar: true, - * }; - * const objNeither: MyType = { - * }; - * - * // TS Error -- can't have both properties: - * const objBoth: MyType = { - * foo: true, - * bar: true, - * }; - * ``` - */ -export type EitherOrNone = - | EitherNotBoth - | (Never & Never); - -// support types for `ExactlyOne` -type Explode = keyof T extends infer K - ? K extends unknown - ? { [I in keyof T]: I extends K ? T[I] : never } - : never - : never; -type AtMostOne = Explode>; -type AtLeastOne }> = Partial & U[keyof U]; - -/** - * Create a type where exactly one of multiple properties must be supplied. - * - * @example - * ```ts - * type Foo = ExactlyOne<{ a: number, b: string, c: boolean}>; - * - * // Valid usages: - * const objA: Foo = { - * a: 1, - * }; - * const objB: Foo = { - * b: 'hi', - * }; - * const objC: Foo = { - * c: true, - * }; - * - * // TS Error -- can't have more than one property: - * const objAll: Foo = { - * a: 1, - * b: 'hi', - * c: true, - * }; - * ``` - */ -export type ExactlyOne = AtMostOne & AtLeastOne; diff --git a/workspaces/frontend/src/shared/utilities/types.ts b/workspaces/frontend/src/shared/utilities/types.ts index dab53f8a5..b1f6a6155 100644 --- a/workspaces/frontend/src/shared/utilities/types.ts +++ b/workspaces/frontend/src/shared/utilities/types.ts @@ -1,14 +1,3 @@ -export enum DeploymentMode { - Standalone = 'standalone', - Kubeflow = 'kubeflow', -} - -export enum Theme { - Default = 'default-theme', - MUI = 'mui-theme', - // Future themes can be added here -} - export enum ContentType { YAML = 'application/yaml', }