From 9cc59548fcdcc46366e18c84e9092cbadbab4d2d Mon Sep 17 00:00:00 2001 From: Oleksii Orel Date: Wed, 13 Sep 2023 16:29:14 +0300 Subject: [PATCH] feat: added PR time check for dashboard required for creating a workspace Signed-off-by: Oleksii Orel --- .github/workflows/pr.yml | 19 ++ devfile.yaml | 3 + .../dashboard-frontend/src/__tests__/const.ts | 199 ++++++++++++ .../workspaceCreationTimeCheck.spec.tsx | 305 ++++++++++++++++++ .../clusterConfigApi.ts | 4 +- .../clusterInfoApi.ts | 4 +- .../dashboard-backend-client/const.ts | 4 +- .../devWorkspaceApi.ts | 33 +- .../devWorkspaceTemplateApi.ts | 8 +- .../devworkspaceResourcesApi.ts | 4 +- .../dashboard-backend-client/eventsApi.ts | 4 +- .../factoryResolverApi.ts | 34 ++ .../personalAccessTokenApi.ts | 12 +- .../dashboard-backend-client/podsApi.ts | 4 +- .../serverConfigApi.ts | 4 +- .../userProfileApi.ts | 4 +- .../websocketClient/index.ts | 4 +- .../yamlResolverApi.ts | 8 +- .../devWorkspaceClient.editorUpdate.spec.ts | 10 +- .../FactoryResolver/__tests__/index.spec.ts | 7 +- .../src/store/FactoryResolver/index.ts | 10 +- .../store/Workspaces/devWorkspaces/index.ts | 2 +- yarn.lock | 8 +- 23 files changed, 630 insertions(+), 64 deletions(-) create mode 100644 packages/dashboard-frontend/src/__tests__/const.ts create mode 100644 packages/dashboard-frontend/src/__tests__/workspaceCreationTimeCheck.spec.tsx create mode 100644 packages/dashboard-frontend/src/services/dashboard-backend-client/factoryResolverApi.ts diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8b47493711..4074bf3166 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -40,6 +40,25 @@ jobs: name: "Check dependencies usage restrictions" run: yarn license:check + time-check: + runs-on: ubuntu-22.04 + if: ${{ github.base_ref == 'main' }} + steps: + - + name: "Checkout Che Dashboard source code" + uses: actions/checkout@v3 + - + name: "Use Node 16" + uses: actions/setup-node@v3 + with: + node-version: 16 + - + name: "Install dependencies" + run: yarn + - + name: "Workspace creation time check" + run: yarn --cwd packages/dashboard-frontend jest 'src/__tests__/workspaceCreationTimeCheck.spec.tsx' + build-and-test: runs-on: ubuntu-22.04 strategy: diff --git a/devfile.yaml b/devfile.yaml index 49e0f03769..165b9b244c 100644 --- a/devfile.yaml +++ b/devfile.yaml @@ -22,6 +22,9 @@ components: path: / protocol: http targetPort: 8888 + env: + - name: KUBEDOCK_ENABLED + value: true commands: - id: installdependencies exec: diff --git a/packages/dashboard-frontend/src/__tests__/const.ts b/packages/dashboard-frontend/src/__tests__/const.ts new file mode 100644 index 0000000000..1384195871 --- /dev/null +++ b/packages/dashboard-frontend/src/__tests__/const.ts @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import { FactoryResolver } from '../services/helpers/types'; +import devfileApi from '../services/devfileApi'; +import normalizeDevfileV2 from '../store/FactoryResolver/normalizeDevfileV2'; +import { dump } from 'js-yaml'; + +export const FACTORY_RESOLVER_DELAY = 600; +export const DEVWORKSPACE_RESOURSES_DELAY = 600; +export const CREATE_DEVWORKSPACE_DELAY = 200; +export const CREATE_DEVWORKSPACETEMPLATE_DELAY = 200; +export const PATCH_DEVWORKSPACE_DELAY = 100; + +export const TIME_LIMIT = 1850; + +// mock objects +export const timestampNew = '2023-09-04T14:09:42.560Z'; +export const namespace = { name: 'user-che', attributes: { phase: 'Active' } }; +export const url = 'https://github.com/eclipse-che/che-dashboard'; +export const devfile = { + schemaVersion: '2.2.0', + metadata: { + name: 'che-dashboard', + namespace: namespace.name, + }, + components: [ + { + name: 'universal-developer-image', + container: { + image: 'quay.io/devfile/universal-developer-image:ubi8-latest', + }, + }, + ], + commands: [], +}; +export const factoryResolver: FactoryResolver = { + v: '4.0', + source: 'devfile.yaml', + scm_info: { + clone_url: 'https://github.com/eclipse-che/che-dashboard.git', + scm_provider: 'github', + }, + devfile: devfile, + links: [], +}; +export const devfileV2 = normalizeDevfileV2( + devfile as devfileApi.DevfileLike, + factoryResolver, + 'https://github.com/eclipse-che/che-dashboard', + [], + namespace.name, + { factoryUrl: url }, +); +const sampleResourceUrl = + 'http://localhost/plugin-registry/v3/plugins/che-incubator/che-code/insiders/devfile.yaml'; +export const plugins = { + [sampleResourceUrl]: { + url: sampleResourceUrl, + plugin: { + schemaVersion: '2.2.0', + metadata: { + name: 'che-code', + }, + } as devfileApi.Devfile, + }, +}; +export const devfileContent = dump({ + schemaVersion: '2.2.0', + metadata: { + name: 'che-dashboard', + namespace: namespace.name, + }, + components: [ + { + name: 'universal-developer-image', + container: { + image: 'quay.io/devfile/universal-developer-image:ubi8-latest', + }, + }, + ], + commands: [], + projects: [ + { + name: 'che-dashboard', + git: { + remotes: { + origin: 'https://github.com/eclipse-che/che-dashboard.git', + }, + }, + }, + ], + attributes: { + 'dw.metadata.annotations': { + 'che.eclipse.org/devfile-source': dump({ + scm: { + repo: 'https://github.com/eclipse-che/che-dashboard.git', + fileName: 'devfile.yaml', + }, + factory: { + params: 'url=https://github.com/eclipse-che/che-dashboard', + }, + }), + }, + }, +}); +export const editorContent = dump({ + schemaVersion: '2.2.0', + metadata: { + name: 'che-code', + }, +} as devfileApi.Devfile); +export const devworkspaceResources = ` +apiVersion: workspace.devfile.io/v1alpha2 +kind: DevWorkspaceTemplate +metadata: + name: che-code +--- +apiVersion: workspace.devfile.io/v1alpha2 +kind: DevWorkspace +metadata: + name: che-dashboard +spec: + started: false + template: + components: + - name: universal-developer-image + container: + image: quay.io/devfile/universal-developer-image:ubi8-latest + projects: + - name: che-dashboard + git: + remotes: + origin: https://github.com/eclipse-che/che-dashboard.git +`; +export const targetDevWorkspace = { + apiVersion: 'workspace.devfile.io/v1alpha2', + kind: 'DevWorkspace', + metadata: { + annotations: { + 'che.eclipse.org/che-editor': 'che-incubator/che-code/insiders', + 'che.eclipse.org/last-updated-timestamp': `${timestampNew}`, + }, + name: 'che-dashboard', + namespace: namespace.name, + }, + spec: { + routingClass: 'che', + started: false, + template: { + components: [ + { + name: 'universal-developer-image', + container: { + image: 'quay.io/devfile/universal-developer-image:ubi8-latest', + }, + }, + ], + projects: [ + { + git: { + remotes: { origin: 'https://github.com/eclipse-che/che-dashboard.git' }, + }, + name: 'che-dashboard', + }, + ], + }, + }, +}; +export const targetDevWorkspaceTemplate: devfileApi.DevWorkspaceTemplate = { + apiVersion: 'workspace.devfile.io/v1alpha2', + kind: 'DevWorkspaceTemplate', + metadata: { + annotations: { + 'che.eclipse.org/components-update-policy': 'managed', + 'che.eclipse.org/plugin-registry-url': + 'http://localhost/plugin-registry/v3/plugins/che-incubator/che-code/insiders/devfile.yaml', + }, + name: 'che-code', + namespace: namespace.name, + ownerReferences: [ + { + apiVersion: 'workspace.devfile.io/v1alpha2', + kind: 'devworkspace', + name: 'che-dashboard', + uid: 'che-dashboard-test-uid', + }, + ], + }, +}; diff --git a/packages/dashboard-frontend/src/__tests__/workspaceCreationTimeCheck.spec.tsx b/packages/dashboard-frontend/src/__tests__/workspaceCreationTimeCheck.spec.tsx new file mode 100644 index 0000000000..f11b7fdcd6 --- /dev/null +++ b/packages/dashboard-frontend/src/__tests__/workspaceCreationTimeCheck.spec.tsx @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import React, { Suspense } from 'react'; +import { Location } from 'history'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router'; +import { render, screen, waitFor } from '@testing-library/react'; +import Routes from '../Routes'; +import { FakeStoreBuilder } from '../store/__mocks__/storeBuilder'; +import Fallback from '../components/Fallback'; +import mockAxios from 'axios'; +import { MockStoreEnhanced } from 'redux-mock-store'; +import { AppState } from '../store'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; +import devfileApi from '../services/devfileApi'; +import { ConvertedState } from '../store/FactoryResolver'; +import { + url, + devfileV2, + factoryResolver, + devworkspaceResources, + plugins, + targetDevWorkspace, + devfileContent, + editorContent, + namespace, + targetDevWorkspaceTemplate, + TIME_LIMIT, + FACTORY_RESOLVER_DELAY, + DEVWORKSPACE_RESOURSES_DELAY, + CREATE_DEVWORKSPACE_DELAY, + CREATE_DEVWORKSPACETEMPLATE_DELAY, + PATCH_DEVWORKSPACE_DELAY, +} from './const'; +import { api } from '@eclipse-che/common'; +import { dump } from 'js-yaml'; +import { cloneDeep } from 'lodash'; + +// mute the outputs +console.error = jest.fn(); +console.warn = jest.fn(); +console.debug = jest.fn(); + +describe('Workspace creation time', () => { + const mockGet = mockAxios.get as jest.Mock; + const mockPatch = mockAxios.patch as jest.Mock; + const mockPost = mockAxios.post as jest.Mock; + + const dateConstructor = window.Date; + const timestampNew = '2023-09-04T14:09:42.560Z'; + let execTime: number; + let execTimer: number | undefined = undefined; + beforeEach(() => { + class MockDate extends Date { + constructor() { + super(timestampNew); + } + } + window.Date = MockDate as DateConstructor; + + execTime = 0; + execTimer = window.setInterval(() => (execTime += 100), 100); + }); + + afterEach(() => { + jest.resetAllMocks(); + window.Date = dateConstructor; + if (execTimer) { + clearInterval(execTimer); + } + }); + + it('should be less then the TIME_LIMIT', async () => { + mockPost.mockResolvedValueOnce( + new Promise(resolve => + setTimeout( + () => + resolve({ + data: factoryResolver, + }), + FACTORY_RESOLVER_DELAY, + ), + ), + ); + + const { rerender } = render( + getComponent( + `/load-factory?url=${url}`, + new FakeStoreBuilder().withInfrastructureNamespace([namespace]).build(), + ), + ); + + await waitFor( + () => + expect(mockPost).toBeCalledWith('/api/factory/resolver', { error_code: undefined, url }), + { timeout: 6000 }, + ); + expect(mockPost).toHaveBeenCalledTimes(1); + expect(mockGet).not.toHaveBeenCalled(); + expect(mockPatch).not.toHaveBeenCalled(); + expect(screen.queryByTestId('fallback-spinner')).not.toBeInTheDocument(); + + mockPost.mockClear(); + mockPost.mockResolvedValueOnce( + new Promise(resolve => + setTimeout( + () => + resolve({ + data: devworkspaceResources, + }), + DEVWORKSPACE_RESOURSES_DELAY, + ), + ), + ); + mockPost.mockResolvedValueOnce( + new Promise(resolve => + setTimeout( + () => + resolve({ + data: Object.assign(cloneDeep(targetDevWorkspace), { + metadata: { + annotations: { + 'che.eclipse.org/che-editor': 'che-incubator/che-code/insiders', + 'che.eclipse.org/last-updated-timestamp': `${timestampNew}`, + }, + name: 'che-dashboard', + namespace: namespace.name, + uid: 'che-dashboard-test-uid', + labels: {}, + }, + }), + headers: {}, + }), + CREATE_DEVWORKSPACE_DELAY, + ), + ), + ); + mockPost.mockResolvedValueOnce( + new Promise(resolve => + setTimeout( + () => + resolve({ + data: cloneDeep(targetDevWorkspaceTemplate), + headers: {}, + }), + CREATE_DEVWORKSPACETEMPLATE_DELAY, + ), + ), + ); + + mockPatch.mockResolvedValueOnce( + new Promise(resolve => + setTimeout( + () => + resolve({ + data: cloneDeep(targetDevWorkspace), + }), + PATCH_DEVWORKSPACE_DELAY, + ), + ), + ); + + rerender( + getComponent( + `/load-factory?url=${url}`, + new FakeStoreBuilder() + .withInfrastructureNamespace([namespace]) + .withFactoryResolver({ + resolver: Object.assign( + { location: 'https://github.com/eclipse-che/che-dashboard' }, + factoryResolver, + ), + converted: { + isConverted: false, + devfileV2: devfileV2, + } as ConvertedState, + }) + .withDwServerConfig({ + defaults: { + editor: 'che-incubator/che-code/insiders', + }, + pluginRegistryURL: 'http://localhost/plugin-registry/v3', + } as api.IServerConfig) + .withDwPlugins(plugins) + .withDevfileRegistries({ + devfiles: { + ['http://localhost/plugin-registry/v3/plugins/che-incubator/che-code/insiders/devfile.yaml']: + { + content: dump({ + schemaVersion: '2.2.0', + metadata: { + name: 'che-code', + }, + } as devfileApi.Devfile), + }, + }, + }) + .build(), + ), + ); + + await waitFor( + () => + expect(mockPost.mock.calls).toEqual([ + [ + '/dashboard/api/devworkspace-resources', + { + devfileContent: devfileContent, + editorContent: editorContent, + editorId: undefined, + editorPath: undefined, + pluginRegistryUrl: 'http://localhost/plugin-registry/v3', + }, + ], + [ + `/dashboard/api/namespace/${namespace.name}/devworkspaces`, + { + devworkspace: targetDevWorkspace, + }, + ], + [ + `/dashboard/api/namespace/${namespace.name}/devworkspacetemplates`, + { + template: cloneDeep(targetDevWorkspaceTemplate), + }, + ], + ]), + { timeout: 1500 }, + ); + expect(mockPost).toBeCalledTimes(3); + + await waitFor( + () => + expect(mockPatch.mock.calls).toEqual([ + [ + `/dashboard/api/namespace/${namespace.name}/devworkspaces/${targetDevWorkspace.metadata.name}`, + [ + { + op: 'replace', + path: '/spec/template/components', + value: [ + { + container: { + env: [ + { + name: 'CHE_DASHBOARD_URL', + value: 'http://localhost', + }, + { + name: 'CHE_PLUGIN_REGISTRY_URL', + value: 'http://localhost/plugin-registry/v3', + }, + { + name: 'CHE_PLUGIN_REGISTRY_INTERNAL_URL', + value: '', + }, + { + name: 'OPENVSX_REGISTRY_URL', + value: '', + }, + ], + image: 'quay.io/devfile/universal-developer-image:ubi8-latest', + }, + name: 'universal-developer-image', + }, + ], + }, + ], + ], + ]), + { timeout: 1500 }, + ); + expect(mockPatch).toBeCalledTimes(1); + expect(mockGet).not.toBeCalled(); + expect(screen.queryByTestId('fallback-spinner')).not.toBeInTheDocument(); + + expect(execTime).toBeLessThan(TIME_LIMIT); + }, 5000); +}); + +function getComponent( + locationOrPath: Location | string, + store: MockStoreEnhanced>, +): React.ReactElement { + return ( + + + + + + + + ); +} diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterConfigApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterConfigApi.ts index 08d10fbe02..1530b223be 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterConfigApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterConfigApi.ts @@ -12,11 +12,11 @@ import axios from 'axios'; import common, { ClusterConfig } from '@eclipse-che/common'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchClusterConfig(): Promise { try { - const response = await axios.get(`${prefix}/cluster-config`); + const response = await axios.get(`${dashboardBackendPrefix}/cluster-config`); return response.data; } catch (e) { throw new Error( diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterInfoApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterInfoApi.ts index 95812eb73e..0acee7af85 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterInfoApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/clusterInfoApi.ts @@ -12,11 +12,11 @@ import axios from 'axios'; import common, { ClusterInfo } from '@eclipse-che/common'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchClusterInfo(): Promise { try { - const response = await axios.get(`${prefix}/cluster-info`); + const response = await axios.get(`${dashboardBackendPrefix}/cluster-info`); return response.data; } catch (e) { throw new Error(`Failed to fetch cluster information. ${common.helpers.errors.getMessage(e)}`); diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/const.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/const.ts index 0f4b7b0675..c1e5100432 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/const.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/const.ts @@ -10,4 +10,6 @@ * Red Hat, Inc. - initial API and implementation */ -export const prefix = '/dashboard/api'; +export const dashboardBackendPrefix = '/dashboard/api'; + +export const cheServerBaseUrl = '/api'; diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceApi.ts index 34a2a752a4..c33a370c81 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceApi.ts @@ -14,7 +14,7 @@ import { api, helpers } from '@eclipse-che/common'; import axios from 'axios'; import { JSONSchema7 } from 'json-schema'; import devfileApi, { IDevWorkspacesList } from '../devfileApi'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export type Headers = { [key: string]: string }; @@ -23,7 +23,7 @@ export async function createWorkspace( ): Promise<{ devWorkspace: devfileApi.DevWorkspace; headers: Headers }> { try { const response = await axios.post( - `${prefix}/namespace/${devworkspace.metadata.namespace}/devworkspaces`, + `${dashboardBackendPrefix}/namespace/${devworkspace.metadata.namespace}/devworkspaces`, { devworkspace }, ); return { devWorkspace: response.data, headers: response.headers }; @@ -40,7 +40,9 @@ export async function listWorkspacesInNamespace( defaultNamespace: string, ): Promise { try { - const response = await axios.get(`${prefix}/namespace/${defaultNamespace}/devworkspaces`); + const response = await axios.get( + `${dashboardBackendPrefix}/namespace/${defaultNamespace}/devworkspaces`, + ); return response.data; } catch (e) { throw new Error(`Failed to fetch the list of devWorkspaces. ${helpers.errors.getMessage(e)}`); @@ -53,7 +55,7 @@ export async function getWorkspaceByName( ): Promise { try { const response = await axios.get( - `${prefix}/namespace/${namespace}/devworkspaces/${workspaceName}`, + `${dashboardBackendPrefix}/namespace/${namespace}/devworkspaces/${workspaceName}`, ); return response.data; } catch (e) { @@ -70,7 +72,7 @@ export async function patchWorkspace( ): Promise<{ devWorkspace: devfileApi.DevWorkspace; headers: Headers }> { try { const response = await axios.patch( - `${prefix}/namespace/${namespace}/devworkspaces/${workspaceName}`, + `${dashboardBackendPrefix}/namespace/${namespace}/devworkspaces/${workspaceName}`, patch, ); return { devWorkspace: response.data, headers: response.headers }; @@ -83,7 +85,9 @@ export async function patchWorkspace( export async function deleteWorkspace(namespace: string, workspaceName: string): Promise { try { - await axios.delete(`${prefix}/namespace/${namespace}/devworkspaces/${workspaceName}`); + await axios.delete( + `${dashboardBackendPrefix}/namespace/${namespace}/devworkspaces/${workspaceName}`, + ); } catch (e) { throw new Error( `Failed to delete workspace '${workspaceName}'. ${helpers.errors.getMessage(e)}`, @@ -93,7 +97,9 @@ export async function deleteWorkspace(namespace: string, workspaceName: string): export async function getDockerConfig(namespace: string): Promise { try { - const response = await axios.get(`${prefix}/namespace/${namespace}/dockerconfig`); + const response = await axios.get( + `${dashboardBackendPrefix}/namespace/${namespace}/dockerconfig`, + ); return response.data; } catch (e) { throw new Error(`Failed to fetch dockerconfig. ${helpers.errors.getMessage(e)}`); @@ -105,7 +111,10 @@ export async function putDockerConfig( dockerconfig: api.IDockerConfig, ): Promise { try { - const response = await axios.put(`${prefix}/namespace/${namespace}/dockerconfig`, dockerconfig); + const response = await axios.put( + `${dashboardBackendPrefix}/namespace/${namespace}/dockerconfig`, + dockerconfig, + ); return response.data; } catch (e) { throw new Error(`Failed to put dockerconfig. ${helpers.errors.getMessage(e)}`); @@ -118,7 +127,7 @@ export async function addPersonalAccessToken( ): Promise { try { const response = await axios.post( - `${prefix}/namespace/${namespace}/personal-access-token`, + `${dashboardBackendPrefix}/namespace/${namespace}/personal-access-token`, personalAccessToken, ); return response.data; @@ -130,7 +139,7 @@ export async function addPersonalAccessToken( export async function injectKubeConfig(namespace: string, devworkspaceId: string): Promise { try { await axios.post( - `${prefix}/namespace/${namespace}/devworkspaceId/${devworkspaceId}/kubeconfig`, + `${dashboardBackendPrefix}/namespace/${namespace}/devworkspaceId/${devworkspaceId}/kubeconfig`, ); } catch (e) { throw new Error(`Failed to inject kubeconfig. ${helpers.errors.getMessage(e)}`); @@ -140,7 +149,7 @@ export async function injectKubeConfig(namespace: string, devworkspaceId: string export async function podmanLogin(namespace: string, devworkspaceId: string): Promise { try { await axios.post( - `${prefix}/namespace/${namespace}/devworkspaceId/${devworkspaceId}/podmanlogin`, + `${dashboardBackendPrefix}/namespace/${namespace}/devworkspaceId/${devworkspaceId}/podmanlogin`, ); } catch (e) { throw new Error(`Failed to podman login. ${helpers.errors.getMessage(e)}`); @@ -151,7 +160,7 @@ export async function getDevfileSchema( schemaVersion: string, ): Promise { try { - const response = await axios.get(`${prefix}/devfile?version=${schemaVersion}`); + const response = await axios.get(`${dashboardBackendPrefix}/devfile?version=${schemaVersion}`); return response.data; } catch (e) { throw new Error( diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceTemplateApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceTemplateApi.ts index 7bbf2b5950..45caf423b3 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceTemplateApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/devWorkspaceTemplateApi.ts @@ -13,12 +13,12 @@ import axios from 'axios'; import common, { api } from '@eclipse-che/common'; import devfileApi from '../devfileApi'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function createTemplate( template: devfileApi.DevWorkspaceTemplate, ): Promise { - const url = `${prefix}/namespace/${template.metadata.namespace}/devworkspacetemplates`; + const url = `${dashboardBackendPrefix}/namespace/${template.metadata.namespace}/devworkspacetemplates`; try { const response = await axios.post(url, { template }); return response.data; @@ -30,7 +30,7 @@ export async function createTemplate( } export async function getTemplates(namespace: string): Promise { - const url = `${prefix}/namespace/${namespace}/devworkspacetemplates`; + const url = `${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates`; try { const response = await axios.get(url); return response.data; @@ -46,7 +46,7 @@ export async function patchTemplate( templateName: string, patch: api.IPatch[], ): Promise { - const url = `${prefix}/namespace/${namespace}/devworkspacetemplates/${templateName}`; + const url = `${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates/${templateName}`; try { const response = await axios.patch(url, patch); return response.data; diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/devworkspaceResourcesApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/devworkspaceResourcesApi.ts index 955544ab5a..2713dc0c2a 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/devworkspaceResourcesApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/devworkspaceResourcesApi.ts @@ -12,11 +12,11 @@ import { api, helpers } from '@eclipse-che/common'; import axios from 'axios'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchResources(params: api.IDevWorkspaceResources): Promise { try { - const response = await axios.post(`${prefix}/devworkspace-resources`, params); + const response = await axios.post(`${dashboardBackendPrefix}/devworkspace-resources`, params); return response.data; } catch (e) { const errorMessage = helpers.errors.getMessage(e); diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/eventsApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/eventsApi.ts index a03e5e8ecc..88d352f7df 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/eventsApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/eventsApi.ts @@ -12,11 +12,11 @@ import axios from 'axios'; import { api, helpers } from '@eclipse-che/common'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchEvents(namespace: string): Promise { try { - const response = await axios.get(`${prefix}/namespace/${namespace}/events`); + const response = await axios.get(`${dashboardBackendPrefix}/namespace/${namespace}/events`); return response.data; } catch (e) { throw new Error(`Failed to fetch events. ${helpers.errors.getMessage(e)}`); diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/factoryResolverApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/factoryResolverApi.ts new file mode 100644 index 0000000000..945f805575 --- /dev/null +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/factoryResolverApi.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import axios from 'axios'; +import { helpers } from '@eclipse-che/common'; +import { cheServerBaseUrl } from './const'; +import { FactoryResolver } from '../helpers/types'; + +const factoryUrl = '/factory'; + +export async function getFactoryResolver( + url: string, + overrideParams: { [params: string]: string } = {}, +): Promise { + try { + const response = await axios.post( + `${cheServerBaseUrl}${factoryUrl}/resolver`, + Object.assign({}, overrideParams, { url }), + ); + + return response.data; + } catch (e) { + throw new Error(`Failed to fetch factory'. ${helpers.errors.getMessage(e)}`); + } +} diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/personalAccessTokenApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/personalAccessTokenApi.ts index 8fe77677be..08368feaab 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/personalAccessTokenApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/personalAccessTokenApi.ts @@ -12,11 +12,13 @@ import { api, helpers } from '@eclipse-che/common'; import axios from 'axios'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchTokens(namespace: string): Promise { try { - const response = await axios.get(`${prefix}/namespace/${namespace}/personal-access-token`); + const response = await axios.get( + `${dashboardBackendPrefix}/namespace/${namespace}/personal-access-token`, + ); return response.data; } catch (e) { throw new Error(`Failed to fetch personal access tokens. ${helpers.errors.getMessage(e)}`); @@ -29,7 +31,7 @@ export async function addToken( ): Promise { try { const response = await axios.post( - `${prefix}/namespace/${namespace}/personal-access-token`, + `${dashboardBackendPrefix}/namespace/${namespace}/personal-access-token`, token, ); return response.data; @@ -44,7 +46,7 @@ export async function updateToken( ): Promise { try { const response = await axios.patch( - `${prefix}/namespace/${namespace}/personal-access-token`, + `${dashboardBackendPrefix}/namespace/${namespace}/personal-access-token`, token, ); return response.data; @@ -59,7 +61,7 @@ export async function removeToken( ): Promise { try { const response = await axios.delete( - `${prefix}/namespace/${namespace}/personal-access-token/${token.tokenName}`, + `${dashboardBackendPrefix}/namespace/${namespace}/personal-access-token/${token.tokenName}`, ); return response.data; } catch (e) { diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/podsApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/podsApi.ts index 40f68b2000..8a9b1a2c44 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/podsApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/podsApi.ts @@ -12,11 +12,11 @@ import axios from 'axios'; import { api, helpers } from '@eclipse-che/common'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; export async function fetchPods(namespace: string): Promise { try { - const response = await axios.get(`${prefix}/namespace/${namespace}/pods`); + const response = await axios.get(`${dashboardBackendPrefix}/namespace/${namespace}/pods`); return response.data; } catch (e) { throw new Error(`Failed to fetch pods. ${helpers.errors.getMessage(e)}`); diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/serverConfigApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/serverConfigApi.ts index 8090da277c..a17239637c 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/serverConfigApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/serverConfigApi.ts @@ -12,7 +12,7 @@ import { api } from '@eclipse-che/common'; import axios from 'axios'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; /** * Returns an array of default plug-ins per editor @@ -22,7 +22,7 @@ import { prefix } from './const'; * default editor and default components */ export async function fetchServerConfig(): Promise { - const url = `${prefix}/server-config`; + const url = `${dashboardBackendPrefix}/server-config`; const response = await axios.get(url); return response.data ? response.data : []; } diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/userProfileApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/userProfileApi.ts index a0210997c8..dfa872d9a0 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/userProfileApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/userProfileApi.ts @@ -12,13 +12,13 @@ import common, { api } from '@eclipse-che/common'; import axios from 'axios'; -import { prefix } from './const'; +import { dashboardBackendPrefix } from './const'; /** * Returns object with user profile data. */ export async function fetchUserProfile(namespace: string): Promise { - const url = `${prefix}/userprofile/${namespace}`; + const url = `${dashboardBackendPrefix}/userprofile/${namespace}`; try { const response = await axios.get(url); return response.data; diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/websocketClient/index.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/websocketClient/index.ts index 16dab10f0f..f446fef34c 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/websocketClient/index.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/websocketClient/index.ts @@ -14,7 +14,7 @@ import { api } from '@eclipse-che/common'; import { injectable } from 'inversify'; import ReconnectingWebSocket from 'reconnecting-websocket'; import { getDefer, IDeferred } from '../../helpers/deferred'; -import { prefix } from '../const'; +import { dashboardBackendPrefix } from '../const'; import { ChannelListener, WebSocketMessageHandler } from './messageHandler'; import { SubscriptionArgs, WebSocketSubscriptionsManager } from './subscriptionsManager'; @@ -33,7 +33,7 @@ export class WebsocketClient { private readonly messageHandler: WebSocketMessageHandler; private readonly subscriptionsManager: WebSocketSubscriptionsManager; private websocketStream: ReconnectingWebSocket | undefined; - public readonly websocketContext = `${prefix}/websocket`; + public readonly websocketContext = `${dashboardBackendPrefix}/websocket`; constructor() { this.messageHandler = new WebSocketMessageHandler(); diff --git a/packages/dashboard-frontend/src/services/dashboard-backend-client/yamlResolverApi.ts b/packages/dashboard-frontend/src/services/dashboard-backend-client/yamlResolverApi.ts index 0952d01551..39e277729a 100644 --- a/packages/dashboard-frontend/src/services/dashboard-backend-client/yamlResolverApi.ts +++ b/packages/dashboard-frontend/src/services/dashboard-backend-client/yamlResolverApi.ts @@ -10,12 +10,12 @@ * Red Hat, Inc. - initial API and implementation */ -import { helpers } from '@eclipse-che/common'; import axios from 'axios'; +import { helpers } from '@eclipse-che/common'; +import { dashboardBackendPrefix } from './const'; import * as yaml from 'js-yaml'; import devfileApi from '../devfileApi'; import { FactoryResolver } from '../helpers/types'; -import { prefix } from './const'; export async function getYamlResolver( namespace: string, @@ -26,7 +26,9 @@ export async function getYamlResolver( const response = url.origin === window.location.origin ? await axios.get(url.href) - : await axios.post(`${prefix}/namespace/${namespace}/yaml/resolver`, { url: url.href }); + : await axios.post(`${dashboardBackendPrefix}/namespace/${namespace}/yaml/resolver`, { + url: url.href, + }); return { v: 'yaml-resolver', diff --git a/packages/dashboard-frontend/src/services/workspace-client/devworkspace/__tests__/devWorkspaceClient.editorUpdate.spec.ts b/packages/dashboard-frontend/src/services/workspace-client/devworkspace/__tests__/devWorkspaceClient.editorUpdate.spec.ts index bdffe53426..555362c378 100644 --- a/packages/dashboard-frontend/src/services/workspace-client/devworkspace/__tests__/devWorkspaceClient.editorUpdate.spec.ts +++ b/packages/dashboard-frontend/src/services/workspace-client/devworkspace/__tests__/devWorkspaceClient.editorUpdate.spec.ts @@ -13,7 +13,7 @@ import { container } from '../../../../inversify.config'; import { DevWorkspaceClient } from '../devWorkspaceClient'; import mockAxios from 'axios'; -import { prefix } from '../../../dashboard-backend-client/const'; +import { dashboardBackendPrefix } from '../../../dashboard-backend-client/const'; import getDevWorkspaceTemplate from './__mocks__/devWorkspaceSpecTemplates'; import devfileApi from '../../../devfileApi'; import * as DwtApi from '../../../dashboard-backend-client/devWorkspaceTemplateApi'; @@ -52,7 +52,7 @@ describe('DevWorkspace client editor update', () => { ); expect(mockPatch.mock.calls).toEqual([ - [`${prefix}/namespace/${namespace}/devworkspacetemplates`], + [`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates`], ]); expect(patch).toEqual({ @@ -89,7 +89,7 @@ describe('DevWorkspace client editor update', () => { ); expect(mockPatch.mock.calls).toEqual([ - [`${prefix}/namespace/${namespace}/devworkspacetemplates`], + [`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates`], ]); expect(patch).toEqual({}); @@ -119,7 +119,7 @@ describe('DevWorkspace client editor update', () => { ); expect(mockPatch.mock.calls).toEqual([ - [`${prefix}/namespace/${namespace}/devworkspacetemplates`], + [`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates`], [url], ]); @@ -156,7 +156,7 @@ describe('DevWorkspace client editor update', () => { ); expect(mockPatch.mock.calls).toEqual([ - [`${prefix}/namespace/${namespace}/devworkspacetemplates`], + [`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates`], [url], ]); diff --git a/packages/dashboard-frontend/src/store/FactoryResolver/__tests__/index.spec.ts b/packages/dashboard-frontend/src/store/FactoryResolver/__tests__/index.spec.ts index c5d987d179..3e60c7fc4f 100644 --- a/packages/dashboard-frontend/src/store/FactoryResolver/__tests__/index.spec.ts +++ b/packages/dashboard-frontend/src/store/FactoryResolver/__tests__/index.spec.ts @@ -16,8 +16,7 @@ import common from '@eclipse-che/common'; import { AppState } from '../..'; import { FakeStoreBuilder } from '../../__mocks__/storeBuilder'; import devfileApi from '../../../services/devfileApi'; -import { container } from '../../../inversify.config'; -import { CheWorkspaceClient } from '../../../services/workspace-client/cheworkspace/cheWorkspaceClient'; +import * as factoryResolver from '../../../services/dashboard-backend-client/factoryResolverApi'; import * as factoryResolverStore from '..'; import { AxiosError } from 'axios'; import normalizeDevfileV1 from '../normalizeDevfileV1'; @@ -64,9 +63,7 @@ jest.mock('../../../services/devfileApi/typeguards.ts', () => { // mute the error outputs console.error = jest.fn(); -const cheWorkspaceClient = container.get(CheWorkspaceClient); - -const getFactoryResolverSpy = jest.spyOn(cheWorkspaceClient.restApiClient, 'getFactoryResolver'); +const getFactoryResolverSpy = jest.spyOn(factoryResolver, 'getFactoryResolver'); const getYamlResolverSpy = jest.spyOn(yamlResolver, 'getYamlResolver'); describe('FactoryResolver store', () => { diff --git a/packages/dashboard-frontend/src/store/FactoryResolver/index.ts b/packages/dashboard-frontend/src/store/FactoryResolver/index.ts index e7b97407a0..f390dab1ef 100644 --- a/packages/dashboard-frontend/src/store/FactoryResolver/index.ts +++ b/packages/dashboard-frontend/src/store/FactoryResolver/index.ts @@ -14,8 +14,6 @@ import { Action, Reducer } from 'redux'; import axios from 'axios'; import common from '@eclipse-che/common'; import { FactoryResolver } from '../../services/helpers/types'; -import { container } from '../../inversify.config'; -import { CheWorkspaceClient } from '../../services/workspace-client/cheworkspace/cheWorkspaceClient'; import { AppThunk } from '../index'; import { createObject } from '../helpers'; import { selectDefaultComponents, selectPvcStrategy } from '../ServerConfig/selectors'; @@ -30,8 +28,7 @@ import { isOAuthResponse } from '../../services/oauth'; import { AUTHORIZED, SanityCheckAction } from '../sanityCheckMiddleware'; import { CHE_EDITOR_YAML_PATH } from '../../services/workspace-client'; import { FactoryParams } from '../../services/helpers/factoryFlow/buildFactoryParams'; - -const WorkspaceClient = container.get(CheWorkspaceClient); +import { getFactoryResolver } from '../../services/dashboard-backend-client/factoryResolverApi'; export type OAuthResponse = { attributes: { @@ -164,10 +161,7 @@ export const actionCreators: ActionCreators = { if (isDevfileRegistryLocation(location)) { data = await getYamlResolver(namespace, location); } else { - data = await WorkspaceClient.restApiClient.getFactoryResolver( - location, - overrideParams, - ); + data = await getFactoryResolver(location, overrideParams); const cheEditor = await grabLink(data.links, CHE_EDITOR_YAML_PATH); if (cheEditor) { optionalFilesContent[CHE_EDITOR_YAML_PATH] = cheEditor; diff --git a/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts b/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts index 170cb1e15b..b5ef535459 100644 --- a/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts +++ b/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts @@ -520,7 +520,7 @@ export const actionCreators: ActionCreators = { const openVSXUrl = selectOpenVSXUrl(state); const pluginRegistryUrl = selectPluginRegistryUrl(state); const pluginRegistryInternalUrl = selectPluginRegistryInternalUrl(state); - const cheEditor = editorId ? editorId : selectDefaultEditor(state); + const cheEditor = editorId ? editorId : state.dwServerConfig.config.defaults.editor; const defaultNamespace = defaultKubernetesNamespace.name; try { /* create a new DevWorkspace */ diff --git a/yarn.lock b/yarn.lock index f26eb502b2..2d8dd28dc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -374,10 +374,10 @@ resolved "https://registry.yarnpkg.com/@eclipse-che/api/-/api-7.72.0.tgz#f16a19f2628c307783203aa5c205b6098b7e57df" integrity sha512-baah1TSYAmCOuiFCHssb7mBoO5BrTAAz8tLV8Y1nqXvDIYMXXyHOnbBpl8/rVeplHGEZIDpFyFN1OGLoy6mcJA== -"@eclipse-che/che-devworkspace-generator@0.0.1-99986b8": - version "0.0.1-99986b8" - resolved "https://registry.yarnpkg.com/@eclipse-che/che-devworkspace-generator/-/che-devworkspace-generator-0.0.1-99986b8.tgz#64ba99846e07ba1dcb608a74a7a7a5bc57cf2e25" - integrity sha512-ajBZpXxZ+DZ+jyL19Yf6M/fykddxaywHB4UBffj7alfAxXpyIW/YNnB1Y0ImzQQoMTvGu+mA2aMfrq6aoJ4LPw== +"@eclipse-che/che-devworkspace-generator@next": + version "7.75.0-next-50585f6" + resolved "https://registry.yarnpkg.com/@eclipse-che/che-devworkspace-generator/-/che-devworkspace-generator-7.75.0-next-50585f6.tgz#928bc25b055f556fd500baba43c7793d4738c486" + integrity sha512-n3NpN8xpT8DqbVG+9qcVWglUP744XnfU76w++/n0JsuV/7WdZWawb8Avi+vCHLCoHXHjeCfz8AwffFZ6/HKB9w== dependencies: "@devfile/api" latest axios "0.21.2"