From e9a6b9cdd2310a4d603a4611f215eeb3a4ab90d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20G=C3=A1mez?= Date: Mon, 11 Oct 2021 11:19:31 +0200 Subject: [PATCH] Improve upgradeView, reduce API calls (#3540) * Initial working version. Now just 3 calls are required Signed-off-by: Antonio Gamez Diaz * Remove many params in UpgradeForm Signed-off-by: Antonio Gamez Diaz * Simplify contexts and refs Signed-off-by: Antonio Gamez Diaz * Use the state in the upgradeForm Signed-off-by: Antonio Gamez Diaz * Use elements instead of strings in diff view Signed-off-by: Antonio Gamez Diaz * Fix AppUpgrade tests Signed-off-by: Antonio Gamez Diaz * Use new api operation Signed-off-by: Antonio Gamez Diaz * Remove unused action Signed-off-by: Antonio Gamez Diaz * Fix pkg loading name Signed-off-by: Antonio Gamez Diaz * Avoid re-requesting the pkg versions Signed-off-by: Antonio Gamez Diaz * Fix tests after the refactor Signed-off-by: Antonio Gamez Diaz --- dashboard/src/actions/packages.test.tsx | 43 -- dashboard/src/actions/packages.ts | 40 -- .../components/AppUpgrade/AppUpgrade.test.tsx | 127 ++-- .../src/components/AppUpgrade/AppUpgrade.tsx | 86 +-- .../src/components/Catalog/Catalog.test.tsx | 1 - .../DeploymentFormBody/Differential.test.tsx | 12 +- .../DeploymentFormBody/Differential.tsx | 6 +- .../DifferentialSelector.tsx | 21 +- .../OperatorInstanceFormBody.tsx | 10 +- .../PackageHeader/PackageView.test.tsx | 1 - .../UpgradeForm/UpgradeForm.test.tsx | 592 ++++++++++++------ .../components/UpgradeForm/UpgradeForm.tsx | 257 ++++---- dashboard/src/reducers/packages.test.ts | 1 - dashboard/src/reducers/packages.ts | 16 - dashboard/src/shared/types.ts | 5 - 15 files changed, 607 insertions(+), 611 deletions(-) diff --git a/dashboard/src/actions/packages.test.tsx b/dashboard/src/actions/packages.test.tsx index 39c560de33f..481919ae987 100644 --- a/dashboard/src/actions/packages.test.tsx +++ b/dashboard/src/actions/packages.test.tsx @@ -481,46 +481,3 @@ describe("fetchAndSelectAvailablePackageDetail", () => { expect(store.getActions()).toEqual(expectedActions); }); }); - -describe("fetchDeployedAvailablePackageDetail", () => { - it("should request a deployed package", async () => { - const response: GetAvailablePackageDetailResponse = { - availablePackageDetail: defaultAvailablePackageDetail, - }; - const mockGetAvailablePackageDetail = jest - .fn() - .mockImplementation(() => Promise.resolve(response)); - jest - .spyOn(PackagesService, "getAvailablePackageDetail") - .mockImplementation(mockGetAvailablePackageDetail); - - const expectedActions = [ - { type: getType(actions.packages.requestDeployedAvailablePackageDetail) }, - { - type: getType(actions.packages.receiveDeployedAvailablePackageDetail), - payload: { availablePackageDetail: defaultAvailablePackageDetail }, - }, - ]; - - await store.dispatch( - actions.packages.fetchDeployedAvailablePackageDetail( - { - context: { cluster: cluster, namespace: namespace }, - identifier: "foo", - plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, - } as AvailablePackageReference, - "1.0.0", - ), - ); - - expect(store.getActions()).toEqual(expectedActions); - expect(mockGetAvailablePackageDetail.mock.calls[0]).toEqual([ - { - context: { cluster: cluster, namespace: namespace }, - identifier: "foo", - plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, - } as AvailablePackageReference, - "1.0.0", - ]); - }); -}); diff --git a/dashboard/src/actions/packages.ts b/dashboard/src/actions/packages.ts index 0b3ff5c29d2..672ee456be0 100644 --- a/dashboard/src/actions/packages.ts +++ b/dashboard/src/actions/packages.ts @@ -70,24 +70,6 @@ export const receiveSelectedAvailablePackageVersions = createAction( // No reset action -// ** DeployedAvailablePackage actions ** -// related to the already deployed package in the state - -// Request action -export const requestDeployedAvailablePackageDetail = createAction( - "REQUEST_DEPLOYED_AVAILABLE_PACKAGE_DETAIL", -); - -// Receive action -export const receiveDeployedAvailablePackageDetail = createAction( - "RECEIVE_DEPLOYED_AVAILABLE_PACKAGE_DETAIL", - resolve => { - return (availablePackageDetail: AvailablePackageDetail) => resolve({ availablePackageDetail }); - }, -); - -// No reset action - // ** Error actions ** // for handling the erros thrown by the rest of the actions @@ -108,8 +90,6 @@ const allActions = [ resetSelectedAvailablePackageDetail, requestSelectedAvailablePackageVersions, receiveSelectedAvailablePackageVersions, - requestDeployedAvailablePackageDetail, - receiveDeployedAvailablePackageDetail, createErrorPackage, clearErrorPackage, ]; @@ -177,23 +157,3 @@ export function fetchAndSelectAvailablePackageDetail( } }; } - -export function fetchDeployedAvailablePackageDetail( - availablePackageReference?: AvailablePackageReference, - version?: string, -): ThunkAction, IStoreState, null, PackagesAction> { - return async dispatch => { - try { - dispatch(requestDeployedAvailablePackageDetail()); - const response = await PackagesService.getAvailablePackageDetail( - availablePackageReference, - version, - ); - if (response.availablePackageDetail) { - dispatch(receiveDeployedAvailablePackageDetail(response.availablePackageDetail)); - } - } catch (e: any) { - dispatch(createErrorPackage(new FetchError(e.message))); - } - }; -} diff --git a/dashboard/src/components/AppUpgrade/AppUpgrade.test.tsx b/dashboard/src/components/AppUpgrade/AppUpgrade.test.tsx index 773aee7fd00..319444a2fdc 100644 --- a/dashboard/src/components/AppUpgrade/AppUpgrade.test.tsx +++ b/dashboard/src/components/AppUpgrade/AppUpgrade.test.tsx @@ -1,9 +1,10 @@ -import actions from "actions"; import Alert from "components/js/Alert"; import LoadingWrapper from "components/LoadingWrapper"; import { + AvailablePackageDetail, AvailablePackageReference, Context, + InstalledPackageDetail, InstalledPackageReference, InstalledPackageStatus, InstalledPackageStatus_StatusReason, @@ -21,8 +22,10 @@ import { FetchError, IAppRepository, IAppState, + IPackageState, UpgradeError, } from "shared/types"; +import { PluginNames } from "shared/utils"; import SelectRepoForm from "../SelectRepoForm/SelectRepoForm"; import UpgradeForm from "../UpgradeForm/UpgradeForm"; import AppUpgrade from "./AppUpgrade"; @@ -64,6 +67,20 @@ const installedPackage1 = { } as InstalledPackageStatus, } as CustomInstalledPackageDetail; +const installedPackageDetail = { + availablePackageRef: { + context: { cluster: "default", namespace: "my-ns" }, + identifier: "test", + plugin: { name: PluginNames.PACKAGES_HELM, version: "0.0.1" } as Plugin, + }, + currentVersion: { appVersion: "4.5.6", pkgVersion: "1.2.3" }, +} as InstalledPackageDetail; + +const selectedPackage = { + versions: [{ appVersion: "10.0.0", pkgVersion: "1.2.3" }], + availablePackageDetail: { name: "test" } as AvailablePackageDetail, +} as IPackageState["selected"]; + const repo1 = { metadata: { name: defaultProps.repo, @@ -98,7 +115,7 @@ it("renders the repo selection form if not introduced", () => { } as IAppState, }; const wrapper = mountWrapper( - getStore({ ...defaultStore, apps: { ...state.apps } }), + getStore({ ...defaultStore, ...state }), , @@ -118,8 +135,7 @@ it("renders the repo selection form if not introduced when the app is loaded", ( const wrapper = mountWrapper( getStore({ ...defaultStore, - repos: { ...state.repos }, - apps: { ...state.apps }, + ...state, }), @@ -142,7 +158,7 @@ describe("when an error exists", () => { const wrapper = mountWrapper( getStore({ ...defaultStore, - apps: { ...state.apps }, + ...state, }), @@ -168,8 +184,7 @@ describe("when an error exists", () => { const wrapper = mountWrapper( getStore({ ...defaultStore, - repos: { ...state.repos }, - apps: { ...state.apps }, + ...state, }), @@ -189,12 +204,15 @@ describe("when an error exists", () => { apps: { error: upgradeError, selected: installedPackage1, - } as IAppState, + selectedDetails: installedPackageDetail, + } as unknown as IAppState, + packages: { selected: selectedPackage } as IPackageState, }; + const wrapper = mountWrapper( getStore({ ...defaultStore, - apps: { ...state.apps }, + ...state, }), @@ -203,7 +221,7 @@ describe("when an error exists", () => { , ); expect(wrapper.find(UpgradeForm)).toExist(); - expect(wrapper.find(UpgradeForm).prop("error")).toEqual(upgradeError); + expect(wrapper.find(UpgradeForm).find(Alert)).toIncludeText(upgradeError.message); }); }); @@ -211,18 +229,19 @@ it("renders the upgrade form when the repo is available", () => { const state = { apps: { selected: installedPackage1, - } as IAppState, + selectedDetails: installedPackageDetail, + } as unknown as IAppState, repos: { repo: repo1, repos: [repo1], isFetching: false, } as IAppRepositoryState, + packages: { selected: selectedPackage } as IPackageState, }; const wrapper = mountWrapper( getStore({ ...defaultStore, - apps: { ...state.apps }, - repos: { ...state.repos }, + ...state, }), @@ -239,18 +258,19 @@ it("skips the repo selection form if the app contains upgrade info", () => { const state = { apps: { selected: installedPackage1, - } as IAppState, + selectedDetails: installedPackageDetail, + } as unknown as IAppState, repos: { repo: repo1, repos: [repo1], isFetching: false, } as IAppRepositoryState, + packages: { selected: selectedPackage } as IPackageState, }; const wrapper = mountWrapper( getStore({ ...defaultStore, - apps: { ...state.apps }, - repos: { ...state.repos }, + ...state, }), @@ -262,78 +282,3 @@ it("skips the repo selection form if the app contains upgrade info", () => { expect(wrapper.find(Alert)).not.toExist(); expect(wrapper.find(SelectRepoForm)).not.toExist(); }); - -describe("when receiving new props", () => { - it("should request the deployed package when the app and repo are populated", () => { - const fetchDeployedAvailablePackageDetail = jest.fn(); - actions.packages.fetchDeployedAvailablePackageDetail = fetchDeployedAvailablePackageDetail; - - const state = { - apps: { - selected: installedPackage1, - } as IAppState, - repos: { - repo: repo1, - repos: [repo1], - isFetching: false, - } as IAppRepositoryState, - }; - mountWrapper( - getStore({ - ...defaultStore, - apps: { ...state.apps }, - repos: { ...state.repos }, - }), - - - , - - , - ); - - expect(fetchDeployedAvailablePackageDetail).toHaveBeenCalledWith( - { - context: { cluster: defaultProps.cluster, namespace: defaultProps.repoNamespace }, - identifier: "stable/bar", - plugin: defaultProps.plugin, - } as AvailablePackageReference, - "1.0.0", - ); - }); - - it("should request the deployed package when the repo is populated later", () => { - const fetchDeployedAvailablePackageDetail = jest.fn(); - actions.packages.fetchDeployedAvailablePackageDetail = fetchDeployedAvailablePackageDetail; - - const state = { - apps: { - selected: installedPackage1, - } as IAppState, - repos: { - repo: repo1, - repos: [repo1], - isFetching: false, - } as IAppRepositoryState, - }; - mountWrapper( - getStore({ - ...defaultStore, - apps: { ...state.apps }, - repos: { ...state.repos }, - }), - - - , - - , - ); - expect(fetchDeployedAvailablePackageDetail).toHaveBeenCalledWith( - { - context: { cluster: defaultProps.cluster, namespace: defaultProps.repoNamespace }, - identifier: "stable/bar", - plugin: defaultProps.plugin, - } as AvailablePackageReference, - "1.0.0", - ); - }); -}); diff --git a/dashboard/src/components/AppUpgrade/AppUpgrade.tsx b/dashboard/src/components/AppUpgrade/AppUpgrade.tsx index d20e56fe544..dbca17a7d2d 100644 --- a/dashboard/src/components/AppUpgrade/AppUpgrade.tsx +++ b/dashboard/src/components/AppUpgrade/AppUpgrade.tsx @@ -1,9 +1,6 @@ import actions from "actions"; import Alert from "components/js/Alert"; -import { - AvailablePackageReference, - InstalledPackageReference, -} from "gen/kubeappsapis/core/packages/v1alpha1/packages"; +import { InstalledPackageReference } from "gen/kubeappsapis/core/packages/v1alpha1/packages"; import { Plugin } from "gen/kubeappsapis/core/plugins/v1alpha1/plugins"; import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -27,20 +24,22 @@ function AppUpgrade() { const dispatch: ThunkDispatch = useDispatch(); const { cluster, namespace, releaseName, pluginName, pluginVersion } = ReactRouter.useParams() as IRouteParams; + const { - apps: { selected: app, isFetching: appsIsFetching, error }, - packages: { isFetching: packagesIsFetching, selected, deployed }, - repos: { repo }, + apps: { + selected: installedAppInstalledPackageDetail, + isFetching: appsIsFetching, + error, + selectedDetails: installedAppAvailablePackageDetail, + }, + packages: { isFetching: chartsIsFetching, selected: selectedPackage }, } = useSelector((state: IStoreState) => state); - const repoName = repo?.metadata?.name || app?.availablePackageRef?.context?.namespace; - const repoNamespace = repo?.metadata?.namespace || app?.availablePackageRef?.context?.namespace; + const isFetching = appsIsFetching || chartsIsFetching; - const [pluginObj] = useState( - selected.availablePackageDetail?.availablePackageRef?.plugin ?? - ({ name: pluginName, version: pluginVersion } as Plugin), - ); + const [pluginObj] = useState({ name: pluginName, version: pluginVersion } as Plugin); + // Initial fetch using the params in the URL useEffect(() => { dispatch( actions.apps.getApp({ @@ -49,68 +48,39 @@ function AppUpgrade() { plugin: pluginObj, } as InstalledPackageReference), ); - }, [dispatch, cluster, namespace, releaseName, pluginObj]); - - useEffect(() => { - dispatch( - actions.packages.fetchDeployedAvailablePackageDetail( - { - context: { - cluster: app?.availablePackageRef?.context?.cluster ?? cluster, - namespace: repoNamespace ?? "", - }, - identifier: app?.availablePackageRef?.identifier ?? "", - plugin: app?.availablePackageRef?.plugin, - } as AvailablePackageReference, - app?.currentVersion?.pkgVersion, - ), - ); - }, [dispatch, app, repoName, repoNamespace, cluster]); + }, [dispatch, cluster, namespace, pluginObj, releaseName]); if (error && error.constructor === FetchError) { return Unable to retrieve the current app: {error.message}; } - if (appsIsFetching || !app) { + if (isFetching || !installedAppInstalledPackageDetail) { + const loadingPkgName = + selectedPackage.availablePackageDetail?.availablePackageRef?.identifier ?? + installedAppInstalledPackageDetail?.installedPackageRef?.identifier ?? + "package"; return ( ); } - if ( - app?.currentVersion?.pkgVersion && - app?.valuesApplied && - app?.availablePackageRef?.identifier && - repoNamespace && - namespace && - cluster && - releaseName && - selected && - deployed - ) { + if (installedAppAvailablePackageDetail && installedAppInstalledPackageDetail && selectedPackage) { return (
- +
); } - return ; + return ( + + ); } export default AppUpgrade; diff --git a/dashboard/src/components/Catalog/Catalog.test.tsx b/dashboard/src/components/Catalog/Catalog.test.tsx index 07737e858cd..be118293391 100644 --- a/dashboard/src/components/Catalog/Catalog.test.tsx +++ b/dashboard/src/components/Catalog/Catalog.test.tsx @@ -30,7 +30,6 @@ const defaultPackageState = { isFetching: false, hasFinishedFetching: false, selected: {} as IPackageState["selected"], - deployed: {} as IPackageState["deployed"], items: [], categories: [], size: 20, diff --git a/dashboard/src/components/DeploymentFormBody/Differential.test.tsx b/dashboard/src/components/DeploymentFormBody/Differential.test.tsx index 9ca2888a03a..f6f8f9feaa8 100644 --- a/dashboard/src/components/DeploymentFormBody/Differential.test.tsx +++ b/dashboard/src/components/DeploymentFormBody/Differential.test.tsx @@ -6,7 +6,7 @@ import Differential from "./Differential"; it("should render a diff between two strings", () => { const wrapper = mountWrapper( defaultStore, - , + empty} />, ); expect(wrapper.find(ReactDiffViewer).props()).toMatchObject({ oldValue: "foo", newValue: "bar" }); }); @@ -14,7 +14,11 @@ it("should render a diff between two strings", () => { it("should print the emptyDiffText if there are no changes", () => { const wrapper = mountWrapper( defaultStore, - , + No differences!} + />, ); expect(wrapper.text()).toMatch("No differences!"); expect(wrapper.text()).not.toMatch("foo"); @@ -23,7 +27,7 @@ it("should print the emptyDiffText if there are no changes", () => { it("sets light theme by default", () => { const wrapper = mountWrapper( defaultStore, - , + empty} />, ); expect(wrapper.find(ReactDiffViewer).prop("useDarkTheme")).toBe(false); }); @@ -31,7 +35,7 @@ it("sets light theme by default", () => { it("changes theme", () => { const wrapper = mountWrapper( getStore({ config: { theme: SupportedThemes.dark } }), - , + empty} />, ); expect(wrapper.find(ReactDiffViewer).prop("useDarkTheme")).toBe(true); }); diff --git a/dashboard/src/components/DeploymentFormBody/Differential.tsx b/dashboard/src/components/DeploymentFormBody/Differential.tsx index 6051a17db38..83d9c536118 100644 --- a/dashboard/src/components/DeploymentFormBody/Differential.tsx +++ b/dashboard/src/components/DeploymentFormBody/Differential.tsx @@ -7,11 +7,11 @@ import "./Differential.css"; export interface IDifferentialProps { oldValues: string; newValues: string; - emptyDiffText: string; + emptyDiffElement: JSX.Element; } function Differential(props: IDifferentialProps) { - const { oldValues, newValues, emptyDiffText } = props; + const { oldValues, newValues, emptyDiffElement } = props; const { config: { theme }, } = useSelector((state: IStoreState) => state); @@ -38,7 +38,7 @@ function Differential(props: IDifferentialProps) { return (
{oldValues === newValues ? ( - {emptyDiffText} + emptyDiffElement ) : ( ; if (deploymentEvent === "upgrade") { // If there are already some deployed values (upgrade scenario) // We compare the values from the old release and the new one oldValues = deployedValues; - emptyDiffText = "The values for the new release are identical to the deployed version."; + emptyDiffElement = ( + +

+ The values you have entered to upgrade this package with are identical to the currently + deployed ones. +

+

+ If you want to restore the default values provided by the package, click on the{" "} + Restore defaults button below. +

+
+ ); } else { // If it's a new deployment, we show the different from the default // values for the selected version oldValues = defaultValues || ""; - emptyDiffText = "No changes detected from the package defaults."; + emptyDiffElement = No changes detected from the package defaults.; } - return ; + return ( + + ); } diff --git a/dashboard/src/components/OperatorInstanceFormBody/OperatorInstanceFormBody.tsx b/dashboard/src/components/OperatorInstanceFormBody/OperatorInstanceFormBody.tsx index ac58eadb97a..7532eea4201 100644 --- a/dashboard/src/components/OperatorInstanceFormBody/OperatorInstanceFormBody.tsx +++ b/dashboard/src/components/OperatorInstanceFormBody/OperatorInstanceFormBody.tsx @@ -114,10 +114,12 @@ function DeploymentFormBody({ No changes detected from deployed values + ) : ( + No changes detected from example defaults + ) } key="differential-selector" />, diff --git a/dashboard/src/components/PackageHeader/PackageView.test.tsx b/dashboard/src/components/PackageHeader/PackageView.test.tsx index 676d5cea2f5..fd39620a2a7 100644 --- a/dashboard/src/components/PackageHeader/PackageView.test.tsx +++ b/dashboard/src/components/PackageHeader/PackageView.test.tsx @@ -74,7 +74,6 @@ const defaultPackageState = { values: "values", versions: [testVersion], } as IPackageState["selected"], - deployed: {} as IPackageState["deployed"], items: [], categories: [], size: 20, diff --git a/dashboard/src/components/UpgradeForm/UpgradeForm.test.tsx b/dashboard/src/components/UpgradeForm/UpgradeForm.test.tsx index 95d5a1c9eac..5684125262e 100644 --- a/dashboard/src/components/UpgradeForm/UpgradeForm.test.tsx +++ b/dashboard/src/components/UpgradeForm/UpgradeForm.test.tsx @@ -1,4 +1,5 @@ import actions from "actions"; +import DeploymentFormBody from "components/DeploymentFormBody/DeploymentFormBody"; import Alert from "components/js/Alert"; import LoadingWrapper from "components/LoadingWrapper/LoadingWrapper"; import { @@ -6,18 +7,21 @@ import { AvailablePackageReference, Context, InstalledPackageReference, + InstalledPackageStatus, + InstalledPackageStatus_StatusReason, Maintainer, PackageAppVersion, + VersionReference, } from "gen/kubeappsapis/core/packages/v1alpha1/packages"; import { Plugin } from "gen/kubeappsapis/core/plugins/v1alpha1/plugins"; import { act } from "react-dom/test-utils"; import * as ReactRedux from "react-redux"; +import { MemoryRouter, Route } from "react-router"; import PackagesService from "shared/PackagesService"; import { defaultStore, getStore, mountWrapper } from "shared/specs/mountWrapper"; -import { FetchError, IPackageState, IStoreState } from "shared/types"; +import { CustomInstalledPackageDetail, FetchError, IAppState, IPackageState } from "shared/types"; import * as url from "shared/url"; -import DeploymentFormBody from "../DeploymentFormBody/DeploymentFormBody"; -import UpgradeForm, { IUpgradeFormProps } from "./UpgradeForm"; +import UpgradeForm from "./UpgradeForm"; const testVersion: PackageAppVersion = { pkgVersion: "1.2.3", @@ -26,134 +30,157 @@ const testVersion: PackageAppVersion = { const schema = { properties: { foo: { type: "string" } } }; -const availablePkgDetails = [ - { - name: "foo", - categories: [""], - displayName: "foo", - iconUrl: "https://icon.com", - repoUrl: "https://repo.com", - homeUrl: "https://example.com", - sourceUrls: ["test"], - shortDescription: "test", - longDescription: "test", - availablePackageRef: { - identifier: "foo/foo", - context: { cluster: "", namespace: "package-namespace" } as Context, - plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, - }, - valuesSchema: "test", - defaultValues: "test", - maintainers: [{ name: "test", email: "test" }] as Maintainer[], - readme: "test", - version: { - appVersion: testVersion.appVersion, - pkgVersion: testVersion.pkgVersion, - } as PackageAppVersion, - }, - { - name: "foo", - categories: [""], - displayName: "foo", - iconUrl: "https://icon.com", - repoUrl: "https://repo.com", - homeUrl: "https://example.com", - sourceUrls: ["test"], - shortDescription: "test", - longDescription: "test", - availablePackageRef: { - identifier: "foo/foo", - context: { cluster: "", namespace: "package-namespace" } as Context, - plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, - }, - valuesSchema: "test", - defaultValues: "test", - maintainers: [{ name: "test", email: "test" }] as Maintainer[], - readme: "test", - version: { - appVersion: testVersion.appVersion, - pkgVersion: testVersion.pkgVersion, - } as PackageAppVersion, - }, -] as AvailablePackageDetail[]; - const defaultProps = { - appCurrentVersion: "1.0.0", - appCurrentValues: "foo: bar", - packageId: "my-package", - packagesIsFetching: false, + packageId: "stable/bar", namespace: "default", cluster: "default", releaseName: "my-release", - repo: "my-repo", repoNamespace: "kubeapps", - error: undefined, - apps: { isFetching: false }, - packages: { isFetching: false }, - selected: { - versions: [{ appVersion: "10.0.0", pkgVersion: "1.2.3" }], - availablePackageDetail: { name: "test" } as AvailablePackageDetail, - } as IPackageState["selected"], - deployed: {} as IPackageState["deployed"], plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, -} as IUpgradeFormProps; +}; -const populatedProps = { - ...defaultProps, - selected: { - error: undefined, - availablePackageDetail: availablePkgDetails[0], - pkgVersion: testVersion.pkgVersion, +const availablePkgDetail = { + name: "foo", + categories: [""], + displayName: "foo", + iconUrl: "https://icon.com", + repoUrl: "https://repo.com", + homeUrl: "https://example.com", + sourceUrls: ["test"], + shortDescription: "test", + longDescription: "test", + availablePackageRef: { + identifier: "foo/foo", + context: { cluster: "", namespace: "package-namespace" } as Context, + plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, + }, + valuesSchema: '"$schema": "http://json-schema.org/schema#"', + defaultValues: "default: values", + maintainers: [{ name: "test", email: "test" }] as Maintainer[], + readme: "test", + version: { appVersion: testVersion.appVersion, - readme: "readme", - readmeError: undefined, - values: "initial: values", - versions: [testVersion], - schema: schema as any, - } as IPackageState["selected"], - deployed: { - availablePackageDetail: availablePkgDetails[0], - schema: schema as any, - values: "foo:", - } as IPackageState["deployed"], + pkgVersion: testVersion.pkgVersion, + } as PackageAppVersion, +} as AvailablePackageDetail; + +const installedPkgDetail = { + name: "test", + postInstallationNotes: "test", + valuesApplied: "foo:", + availablePackageRef: { + identifier: "stable/bar", + context: { cluster: defaultProps.cluster, namespace: defaultProps.repoNamespace } as Context, + plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, + } as AvailablePackageReference, + currentVersion: { appVersion: "10.0.0", pkgVersion: "1.0.0" } as PackageAppVersion, + installedPackageRef: { + identifier: "stable/bar", + pkgVersion: "1.0.0", + context: { cluster: defaultProps.cluster, namespace: defaultProps.repoNamespace } as Context, + plugin: { name: "my.plugin", version: "0.0.1" } as Plugin, + } as InstalledPackageReference, + latestMatchingVersion: { appVersion: "10.0.0", pkgVersion: "1.0.0" } as PackageAppVersion, + latestVersion: { appVersion: "10.0.0", pkgVersion: "1.0.0" } as PackageAppVersion, + pkgVersionReference: { version: "1" } as VersionReference, + reconciliationOptions: {}, + status: { + ready: true, + reason: InstalledPackageStatus_StatusReason.STATUS_REASON_INSTALLED, + userReason: "deployed", + } as InstalledPackageStatus, +} as CustomInstalledPackageDetail; + +const selectedPkg = { + availablePackageDetail: availablePkgDetail, + pkgVersion: testVersion.pkgVersion, + appVersion: testVersion.appVersion, + readme: "readme", + values: "initial: values", + versions: [testVersion], + schema: schema as any, }; +const routePathParam = `/c/${defaultProps.cluster}/ns/${defaultProps.namespace}/apps/${defaultProps.plugin.name}/${defaultProps.plugin.version}/${defaultProps.releaseName}/upgrade`; +const routePath = "/c/:cluster/ns/:namespace/apps/:pluginName/:pluginVersion/:releaseName/upgrade"; + describe("it behaves like a loading component", () => { it("if the app is being fetched", () => { + const state = { + ...defaultStore, + apps: { + isFetching: true, + } as IPackageState, + }; expect( mountWrapper( - getStore({ apps: { isFetching: true } } as IStoreState), - , + getStore({ ...state }), + + + , + + , ).find(LoadingWrapper), ).toExist(); }); it("if the package is being fetched", () => { + const state = { + ...defaultStore, + packages: { + isFetching: true, + } as IPackageState, + }; expect( mountWrapper( - getStore({ packages: { isFetching: true } } as IStoreState), - , + getStore({ ...state }), + + + , + + , ).find(LoadingWrapper), ).toExist(); }); - it("if there are no versions", () => { + const state = { + ...defaultStore, + packages: { + selected: { + versions: [] as PackageAppVersion[], + }, + } as IPackageState, + }; expect( mountWrapper( - defaultStore, - , + getStore({ ...state }), + + + , + + , ).find(LoadingWrapper), ).toExist(); }); it("if there is no version", () => { + const state = { + ...defaultStore, + packages: { + selected: { + availablePackageDetail: undefined, + }, + } as IPackageState, + }; + expect( mountWrapper( - defaultStore, - , + getStore({ ...state }), + + + , + + , ).find(LoadingWrapper), ).toExist(); }); @@ -162,7 +189,23 @@ describe("it behaves like a loading component", () => { it("fetches the available versions", () => { const getAvailablePackageVersions = jest.fn(); PackagesService.getAvailablePackageVersions = getAvailablePackageVersions; - mountWrapper(defaultStore, ); + + const state = { + ...defaultStore, + apps: { + selected: installedPkgDetail, + selectedDetails: availablePkgDetail, + isFetching: false, + } as IAppState, + }; + mountWrapper( + getStore({ ...state }), + + + , + + , + ); expect(getAvailablePackageVersions).toHaveBeenCalledWith({ context: { cluster: defaultProps.cluster, @@ -173,56 +216,64 @@ it("fetches the available versions", () => { } as AvailablePackageReference); }); -it("fetches the current package version even if there is already one in the state", () => { - const deployed = { - availablePackageDetail: availablePkgDetails[1], - }; - - const selected = { - availablePackageDetail: availablePkgDetails[0], - pkgVersion: testVersion.pkgVersion, - appVersion: testVersion.appVersion, - readme: "readme", - values: "values", - versions: [testVersion], - schema: schema as any, - }; - +it("does not fetch the current package version if there is already one in the state", () => { const getAvailablePackageDetail = jest.fn(); PackagesService.getAvailablePackageDetail = getAvailablePackageDetail; + const state = { + ...defaultStore, + apps: { + selected: installedPkgDetail, + selectedDetails: availablePkgDetail, + isFetching: false, + } as IAppState, + packages: { + selected: selectedPkg, + } as IPackageState, + }; mountWrapper( - defaultStore, - , - ); - expect(getAvailablePackageDetail).toHaveBeenCalledWith( - { - context: { - cluster: availablePkgDetails[0].availablePackageRef?.context?.cluster, - namespace: defaultProps.repoNamespace, - }, - identifier: defaultProps.packageId, - plugin: defaultProps.plugin, - } as AvailablePackageReference, - deployed.availablePackageDetail.version?.pkgVersion, + getStore({ ...state }), + + + , + + , ); + expect(getAvailablePackageDetail).not.toHaveBeenCalled(); }); describe("renders an error", () => { it("renders an alert if the deployment failed", () => { - const selected = { - availablePackageDetail: { name: "foo" } as AvailablePackageDetail, - pkgVersion: "10.0.0", - appVersion: "1.2.3", - versions: [{ appVersion: "10.0.0", pkgVersion: "1.2.3" }], - schema: schema as any, - error: new FetchError("wrong format!"), + const state = { + ...defaultStore, + apps: { + selected: installedPkgDetail, + selectedDetails: availablePkgDetail, + isFetching: false, + error: new FetchError("wrong format!"), + } as IAppState, + packages: { + selected: { + availablePackageDetail: availablePkgDetail, + pkgVersion: testVersion.pkgVersion, + appVersion: testVersion.appVersion, + readme: "readme", + values: "initial: values", + versions: [testVersion], + schema: schema as any, + }, + } as IPackageState, }; + const wrapper = mountWrapper( - defaultStore, - , + getStore({ ...state }), + + + , + + , ); expect(wrapper.find(Alert).exists()).toBe(true); - expect(wrapper.find(Alert).html()).toContain("wrong format!"); + expect(wrapper.find(Alert).first()).toIncludeText("wrong format!"); }); }); @@ -230,34 +281,89 @@ it("defaults the upgrade version to the current version", () => { // helm upgrade is the only way to update the values.yaml, so upgrade is // often used by users to update values only, so we can't default to the // latest version on the assumption that they always want to upgrade. - const wrapper = mountWrapper(defaultStore, ); + const state = { + ...defaultStore, + apps: { + selected: installedPkgDetail, + selectedDetails: availablePkgDetail, + isFetching: false, + } as IAppState, + packages: { + selected: selectedPkg, + } as IPackageState, + }; + + const wrapper = mountWrapper( + getStore({ ...state }), + + + , + + , + ); expect(wrapper.find(DeploymentFormBody).prop("packageVersion")).toBe("1.0.0"); }); it("forwards the appValues when modified", () => { - const wrapper = mountWrapper(defaultStore, ); + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: "foo: not-bar" }, + selectedDetails: { ...availablePkgDetail, defaultValues: "# A comment\nfoo: bar\n" }, + isFetching: false, + } as IAppState, + packages: { + selected: { ...selectedPkg, values: "initial: values" }, + } as IPackageState, + }; + const wrapper = mountWrapper( + getStore({ ...state }), + + + , + + , + ); + + const handleValuesChange: (v: string) => void = wrapper + .find(DeploymentFormBody) + .prop("setValues"); act(() => { - const handleValuesChange: (v: string) => void = wrapper - .find(DeploymentFormBody) - .prop("setValues"); handleValuesChange("foo: bar"); }); - expect(wrapper.find(DeploymentFormBody).prop("appValues")).toBe("initial: values\nfoo: bar\n"); + wrapper.update(); + + expect(wrapper.find(DeploymentFormBody).prop("appValues")).toBe("foo: bar"); }); it("triggers an upgrade when submitting the form", async () => { const mockDispatch = jest.fn().mockReturnValue(true); jest.spyOn(ReactRedux, "useDispatch").mockReturnValue(mockDispatch); - const { namespace, releaseName } = defaultProps; + const updateInstalledPackage = jest.fn(); + actions.apps.updateInstalledPackage = updateInstalledPackage; + const appValues = "initial: values\nfoo: bar\n"; - const updateInstalledPackage = jest - .spyOn(actions.apps, "updateInstalledPackage") - .mockImplementation(() => { - return jest.fn(); - }); + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: appValues }, + selectedDetails: availablePkgDetail, + isFetching: false, + } as IAppState, + packages: { + selected: selectedPkg, + } as IPackageState, + }; + const wrapper = mountWrapper( - defaultStore, - , + getStore({ + ...state, + }), + + + , + + , ); await act(async () => { @@ -271,24 +377,14 @@ it("triggers an upgrade when submitting the form", async () => { }); }); expect(updateInstalledPackage).toHaveBeenCalledWith( - { - context: { cluster: defaultProps.cluster, namespace: namespace }, - identifier: releaseName, - plugin: { name: "my.plugin", version: "0.0.1" }, - } as InstalledPackageReference, - availablePkgDetails[0], + installedPkgDetail.installedPackageRef, + availablePkgDetail, appValues, schema, ); expect(mockDispatch).toHaveBeenCalledWith({ payload: { - args: [ - url.app.apps.get({ - context: { cluster: defaultProps.cluster, namespace: namespace }, - identifier: releaseName, - plugin: { name: "my.plugin", version: "0.0.1" }, - } as InstalledPackageReference), - ], + args: [url.app.apps.get(installedPkgDetail.installedPackageRef)], method: "push", }, type: "@@router/CALL_HISTORY_METHOD", @@ -297,41 +393,91 @@ it("triggers an upgrade when submitting the form", async () => { describe("when receiving new props", () => { it("should calculate the modifications from the default and the current values", () => { + const defaultValues = "initial: values\na: b\n"; + const deployedValues = "a: b\n"; const currentValues = "a: b\nc: d\n"; - const defaultValues = "a: b\n"; + const expectedValues = "initial: values\na: b\nc: d\n"; + + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: currentValues }, + selectedDetails: { ...availablePkgDetail, defaultValues: deployedValues }, + isFetching: false, + } as IAppState, + packages: { + selected: { ...selectedPkg, values: defaultValues }, + } as IPackageState, + }; + const wrapper = mountWrapper( - defaultStore, - , + getStore({ + ...state, + }), + + + , + + , ); + wrapper.setProps({ deployed: { values: defaultValues } }); - expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual( - "initial: values\n" + currentValues, - ); + expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual(expectedValues); }); it("should apply modifications if a new version is selected", () => { const defaultValues = "a: b\n"; const deployedValues = "a: B\n"; const currentValues = "a: B\nc: d\n"; + const expectedValues = "a: b\nc: d\n"; + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: currentValues }, + selectedDetails: { ...availablePkgDetail, defaultValues: deployedValues }, + isFetching: false, + } as IAppState, + packages: { + selected: { ...selectedPkg, values: defaultValues }, + } as IPackageState, + }; const wrapper = mountWrapper( - defaultStore, - , + getStore({ + ...state, + }), + + + , + + , ); - expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual("a: b\nc: d\n"); + + expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual(expectedValues); }); it("won't apply changes if the values have been manually modified", () => { const userValues = "a: b\n"; - const wrapper = mountWrapper(defaultStore, ); + const state = { + ...defaultStore, + apps: { + selected: installedPkgDetail, + selectedDetails: availablePkgDetail, + isFetching: false, + } as IAppState, + packages: { + selected: selectedPkg, + } as IPackageState, + }; + const wrapper = mountWrapper( + getStore({ + ...state, + }), + + + , + + , + ); act(() => { const handleValuesChange: (v: string) => void = wrapper .find(DeploymentFormBody) @@ -342,7 +488,7 @@ describe("when receiving new props", () => { .prop("setValuesModified"); setValuesModified(); }); - wrapper.setProps({ selected: { versions: [testVersion], version: availablePkgDetails[1] } }); + wrapper.setProps({ selected: { versions: [testVersion], version: availablePkgDetail } }); wrapper.update(); expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual(userValues); }); @@ -418,23 +564,43 @@ describe("when receiving new props", () => { }, ].forEach(t => { it(t.description, () => { - const deployed = { - values: t.defaultValues, - requested: true, + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: t.deployedValues }, + selectedDetails: { ...availablePkgDetail, defaultValues: t.defaultValues }, + isFetching: false, + } as IAppState, + packages: { + selected: { ...selectedPkg, values: "initial: values" }, + } as IPackageState, }; - const newSelected = { - ...populatedProps.selected, - version: availablePkgDetails[1], - values: t.newDefaultValues, + const newState = { + ...state, + apps: { + ...state.apps, + selected: { + ...state.apps.selected, + values: t.deployedValues, + }, + } as IAppState, + packages: { + selected: { + ...state.packages.selected, + values: t.newDefaultValues, + }, + } as IPackageState, }; + const wrapper = mountWrapper( - defaultStore, - , + getStore({ + ...newState, + }), + + + , + + , ); expect(wrapper.find(DeploymentFormBody).prop("appValues")).toEqual(t.result); }); @@ -442,14 +608,32 @@ describe("when receiving new props", () => { }); it("shows, by default, the default values of the deployed package plus any modification", () => { + const defaultValues = "initial: values"; + const deployedValues = "# A comment\nfoo: bar\n"; + const currentValues = "foo: not-bar"; + const expectedValues = "# A comment\nfoo: not-bar\n"; + + const state = { + ...defaultStore, + apps: { + selected: { ...installedPkgDetail, valuesApplied: currentValues }, + selectedDetails: { ...availablePkgDetail, defaultValues: deployedValues }, + isFetching: false, + } as IAppState, + packages: { + selected: { ...selectedPkg, values: defaultValues }, + } as IPackageState, + }; const wrapper = mountWrapper( - defaultStore, - , + getStore({ + ...state, + }), + + + , + + , ); - const expectedValues = "# A comment\nfoo: not-bar\n"; + expect(wrapper.find(DeploymentFormBody).prop("deployedValues")).toBe(expectedValues); }); diff --git a/dashboard/src/components/UpgradeForm/UpgradeForm.tsx b/dashboard/src/components/UpgradeForm/UpgradeForm.tsx index f0cef25683f..acceb05fc86 100644 --- a/dashboard/src/components/UpgradeForm/UpgradeForm.tsx +++ b/dashboard/src/components/UpgradeForm/UpgradeForm.tsx @@ -8,10 +8,9 @@ import PackageVersionSelector from "components/PackageHeader/PackageVersionSelec import { push } from "connected-react-router"; import * as jsonpatch from "fast-json-patch"; import { - AvailablePackageReference, - InstalledPackageReference, + AvailablePackageDetail, + InstalledPackageDetail, } from "gen/kubeappsapis/core/packages/v1alpha1/packages"; -import { Plugin } from "gen/kubeappsapis/core/plugins/v1alpha1/plugins"; import * as yaml from "js-yaml"; import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -25,18 +24,11 @@ import LoadingWrapper from "../LoadingWrapper/LoadingWrapper"; import "./UpgradeForm.css"; export interface IUpgradeFormProps { - appCurrentVersion: string; - appCurrentValues?: string; - packageId: string; - packagesIsFetching: boolean; - namespace: string; - cluster: string; - releaseName: string; - repoNamespace: string; + installedAppAvailablePackageDetail: AvailablePackageDetail; + installedAppInstalledPackageDetail: InstalledPackageDetail; + selectedPackage: IPackageState["selected"]; + chartsIsFetching: boolean; error?: Error; - selected: IPackageState["selected"]; - deployed: IPackageState["deployed"]; - plugin: Plugin; } function applyModifications(mods: jsonpatch.Operation[], values: string) { @@ -55,93 +47,85 @@ function applyModifications(mods: jsonpatch.Operation[], values: string) { return values; } -function UpgradeForm({ - appCurrentVersion, - appCurrentValues, - packageId, - packagesIsFetching, - namespace, - cluster, - releaseName, - repoNamespace, - error, - selected, - deployed, - plugin, -}: IUpgradeFormProps) { - const [appValues, setAppValues] = useState(appCurrentValues || ""); - const [isDeploying, setIsDeploying] = useState(false); - const [valuesModified, setValuesModified] = useState(false); - const [modifications, setModifications] = useState( - undefined as undefined | jsonpatch.Operation[], - ); +function UpgradeForm() { const dispatch: ThunkDispatch = useDispatch(); - const [deployedValues, setDeployedValues] = useState(""); - - const { availablePackageDetail, versions, schema, values, pkgVersion } = selected; - - const packageCluster = availablePackageDetail?.availablePackageRef?.context?.cluster; - const { - apps: { isFetching: appsFetching }, - packages: { isFetching: packagesFetching }, + apps: { + selected: installedAppInstalledPackageDetail, + isFetching: appsIsFetching, + error, + selectedDetails: installedAppAvailablePackageDetail, + }, + packages: { isFetching: chartsIsFetching, selected: selectedPackage }, } = useSelector((state: IStoreState) => state); - const isFetching = appsFetching || packagesFetching; - const pluginObj = plugin ?? selected.availablePackageDetail?.availablePackageRef?.plugin; + + const isFetching = appsIsFetching || chartsIsFetching; + const { availablePackageDetail, versions, schema, values, pkgVersion } = selectedPackage; + + const [appValues, setAppValues] = useState(""); + const [modifications, setModifications] = useState( + undefined as undefined | jsonpatch.Operation[], + ); + const [deployedValues, setDeployedValues] = useState(""); + const [isDeploying, setIsDeploying] = useState(false); + const [valuesModified, setValuesModified] = useState(false); useEffect(() => { - dispatch( - actions.packages.fetchAvailablePackageVersions({ - context: { - cluster: packageCluster ?? cluster, - namespace: repoNamespace, - }, - plugin: pluginObj, - identifier: packageId, - } as AvailablePackageReference), - ); - }, [dispatch, packageCluster, repoNamespace, packageId, cluster, pluginObj]); + // This block just will be executed once, given that populating + // the list of versions does not depend on anything else + if (selectedPackage.versions.length === 0) { + dispatch( + actions.packages.fetchAvailablePackageVersions( + installedAppInstalledPackageDetail?.availablePackageRef, + ), + ); + if (installedAppAvailablePackageDetail) { + // Additionally, mark the current installed package version as the selected, + // next time, the selection will be handled by selectVersion() + dispatch( + actions.packages.receiveSelectedAvailablePackageDetail( + installedAppAvailablePackageDetail, + ), + ); + } + } + }, [ + dispatch, + installedAppInstalledPackageDetail?.availablePackageRef, + selectedPackage.versions.length, + installedAppAvailablePackageDetail, + ]); useEffect(() => { - if (deployed.values && !modifications) { + if (installedAppAvailablePackageDetail?.defaultValues && !modifications) { // Calculate modifications from the default values - const defaultValuesObj = yaml.load(deployed.values); - const deployedValuesObj = yaml.load(appCurrentValues || ""); + const defaultValuesObj = yaml.load(installedAppAvailablePackageDetail?.defaultValues); + const deployedValuesObj = yaml.load(installedAppInstalledPackageDetail?.valuesApplied || ""); const newModifications = jsonpatch.compare(defaultValuesObj as any, deployedValuesObj as any); - const values = applyModifications(newModifications, deployed.values); + const values = applyModifications( + newModifications, + installedAppAvailablePackageDetail?.defaultValues, + ); setModifications(newModifications); setAppValues(values); } - }, [deployed.values, appCurrentValues, modifications]); + }, [ + installedAppAvailablePackageDetail?.defaultValues, + installedAppInstalledPackageDetail?.valuesApplied, + modifications, + ]); useEffect(() => { - if (deployed.values) { + if (installedAppAvailablePackageDetail?.defaultValues) { // Apply modifications to deployed values - const values = applyModifications(modifications || [], deployed.values); + const values = applyModifications( + modifications || [], + installedAppAvailablePackageDetail?.defaultValues, + ); setDeployedValues(values); } - }, [deployed.values, modifications]); - - useEffect(() => { - dispatch( - actions.packages.fetchAndSelectAvailablePackageDetail( - { - context: { cluster: packageCluster, namespace: repoNamespace }, - plugin: pluginObj, - identifier: packageId, - } as AvailablePackageReference, - deployed.availablePackageDetail?.version?.pkgVersion, - ), - ); - }, [ - dispatch, - packageCluster, - repoNamespace, - packageId, - deployed.availablePackageDetail?.version?.pkgVersion, - pluginObj, - ]); + }, [installedAppAvailablePackageDetail?.defaultValues, modifications]); useEffect(() => { if (!valuesModified && values) { @@ -164,11 +148,7 @@ function UpgradeForm({ const selectVersion = (e: React.ChangeEvent) => { dispatch( actions.packages.fetchAndSelectAvailablePackageDetail( - { - context: { cluster: packageCluster, namespace: repoNamespace }, - plugin: pluginObj, - identifier: packageId, - } as AvailablePackageReference, + installedAppInstalledPackageDetail?.availablePackageRef, e.currentTarget.value, ), ); @@ -177,14 +157,14 @@ function UpgradeForm({ const handleDeploy = async (e: React.FormEvent) => { e.preventDefault(); setIsDeploying(true); - if (availablePackageDetail) { + if ( + availablePackageDetail && + installedAppInstalledPackageDetail?.installedPackageRef && + installedAppInstalledPackageDetail?.availablePackageRef?.context?.namespace + ) { const deployedSuccess = await dispatch( actions.apps.updateInstalledPackage( - { - context: { cluster: cluster, namespace: namespace }, - identifier: releaseName, - plugin: pluginObj, - } as InstalledPackageReference, + installedAppInstalledPackageDetail?.installedPackageRef, availablePackageDetail, appValues, schema, @@ -192,15 +172,7 @@ function UpgradeForm({ ); setIsDeploying(false); if (deployedSuccess) { - dispatch( - push( - url.app.apps.get({ - context: { cluster: cluster, namespace: namespace }, - plugin: pluginObj, - identifier: releaseName, - } as AvailablePackageReference), - ), - ); + dispatch(push(url.app.apps.get(installedAppInstalledPackageDetail?.installedPackageRef))); } } }; @@ -222,11 +194,11 @@ function UpgradeForm({ ) : ( <> 0 && !!availablePackageDetail } > - - - - - -
-
- - -
- - -
-
+ {!installedAppInstalledPackageDetail?.availablePackageRef?.identifier || + !installedAppInstalledPackageDetail?.currentVersion?.pkgVersion ? ( + <> + ) : ( + <> + + + + + +
+
+ + +
+ + +
+
+ + )}
)} diff --git a/dashboard/src/reducers/packages.test.ts b/dashboard/src/reducers/packages.test.ts index 9bf5b9cf400..7b56a539efc 100644 --- a/dashboard/src/reducers/packages.test.ts +++ b/dashboard/src/reducers/packages.test.ts @@ -44,7 +44,6 @@ describe("packageReducer", () => { selected: { versions: [], }, - deployed: {}, size: 20, }; }); diff --git a/dashboard/src/reducers/packages.ts b/dashboard/src/reducers/packages.ts index 1a8b9691360..de818eaa0f4 100644 --- a/dashboard/src/reducers/packages.ts +++ b/dashboard/src/reducers/packages.ts @@ -14,7 +14,6 @@ export const initialState: IPackageState = { selected: { versions: [], }, - deployed: {}, size: 20, }; @@ -97,21 +96,6 @@ const packageReducer = ( isFetching: false, selected: selectedPackageReducer(state.selected, action), }; - case getType(actions.packages.requestDeployedAvailablePackageDetail): - return { - ...state, - deployed: {}, - }; - case getType(actions.packages.receiveDeployedAvailablePackageDetail): - return { - ...state, - isFetching: false, - deployed: { - availablePackageDetail: action.payload.availablePackageDetail, - schema: action.payload.availablePackageDetail.valuesSchema as any, - values: action.payload.availablePackageDetail.defaultValues, - }, - }; case getType(actions.packages.resetAvailablePackageSummaries): return { ...state, diff --git a/dashboard/src/shared/types.ts b/dashboard/src/shared/types.ts index 6f7a96695b6..fec1dfe1415 100644 --- a/dashboard/src/shared/types.ts +++ b/dashboard/src/shared/types.ts @@ -73,11 +73,6 @@ export interface IPackageState { values?: string; schema?: JSONSchemaType; }; - deployed: { - availablePackageDetail?: AvailablePackageDetail; - values?: string; - schema?: JSONSchemaType; - }; items: AvailablePackageSummary[]; categories: string[]; size: number;