diff --git a/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap b/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
index a7fcf85592..6417d5c987 100644
--- a/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
+++ b/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
@@ -434,14 +434,8 @@ exports[`opening dock tab for installing helm chart given application is started
-
- Item list is empty
-
-
+ class="Spinner singleColor center"
+ />
+ class="UpgradeChart flex column"
+ >
+
+
+
+
+ Release
+
+
+
+ some-name
+
+
+ Namespace
+
+
+
+ some-namespace
+
+
+ Version
+
+
+
+
+ Upgrade version
+
+
+
+
+
+
+
+ Select...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/helm-releases/showing-details-for-helm-release.test.ts b/src/features/helm-releases/showing-details-for-helm-release.test.ts
index 840296e25d..5a83a3fbc0 100644
--- a/src/features/helm-releases/showing-details-for-helm-release.test.ts
+++ b/src/features/helm-releases/showing-details-for-helm-release.test.ts
@@ -23,6 +23,14 @@ import requestHelmReleaseInjectable from "../../renderer/components/+helm-releas
import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable";
import showCheckedErrorInjectable from "../../renderer/components/notifications/show-checked-error.injectable";
import getRandomUpgradeChartTabIdInjectable from "../../renderer/components/dock/upgrade-chart/get-random-upgrade-chart-tab-id.injectable";
+import type { RequestHelmCharts } from "../../common/k8s-api/endpoints/helm-charts.api/list.injectable";
+import type { RequestHelmChartVersions } from "../../common/k8s-api/endpoints/helm-charts.api/get-versions.injectable";
+import type { RequestHelmChartReadme } from "../../common/k8s-api/endpoints/helm-charts.api/get-readme.injectable";
+import type { RequestHelmChartValues } from "../../common/k8s-api/endpoints/helm-charts.api/get-values.injectable";
+import requestHelmChartsInjectable from "../../common/k8s-api/endpoints/helm-charts.api/list.injectable";
+import requestHelmChartVersionsInjectable from "../../common/k8s-api/endpoints/helm-charts.api/get-versions.injectable";
+import requestHelmChartReadmeInjectable from "../../common/k8s-api/endpoints/helm-charts.api/get-readme.injectable";
+import requestHelmChartValuesInjectable from "../../common/k8s-api/endpoints/helm-charts.api/get-values.injectable";
describe("showing details for helm release", () => {
let builder: ApplicationBuilder;
@@ -30,6 +38,10 @@ describe("showing details for helm release", () => {
let requestHelmReleaseMock: AsyncFnMock;
let requestHelmReleaseConfigurationMock: AsyncFnMock;
let requestHelmReleaseUpdateMock: AsyncFnMock;
+ let requestHelmChartsMock: AsyncFnMock;
+ let requestHelmChartVersionsMock: AsyncFnMock;
+ let requestHelmChartReadmeMock: AsyncFnMock;
+ let requestHelmChartValuesMock: AsyncFnMock;
let showSuccessNotificationMock: jest.Mock;
let showCheckedErrorNotificationMock: jest.Mock;
@@ -44,6 +56,10 @@ describe("showing details for helm release", () => {
requestHelmReleaseMock = asyncFn();
requestHelmReleaseConfigurationMock = asyncFn();
requestHelmReleaseUpdateMock = asyncFn();
+ requestHelmChartsMock = asyncFn();
+ requestHelmChartVersionsMock = asyncFn();
+ requestHelmChartReadmeMock = asyncFn();
+ requestHelmChartValuesMock = asyncFn();
showSuccessNotificationMock = jest.fn();
showCheckedErrorNotificationMock = jest.fn();
@@ -56,6 +72,10 @@ describe("showing details for helm release", () => {
windowDi.override(requestHelmReleaseInjectable, () => requestHelmReleaseMock);
windowDi.override(requestHelmReleaseConfigurationInjectable, () => requestHelmReleaseConfigurationMock);
windowDi.override(requestHelmReleaseUpdateInjectable, () => requestHelmReleaseUpdateMock);
+ windowDi.override(requestHelmChartsInjectable, () => requestHelmChartsMock);
+ windowDi.override(requestHelmChartVersionsInjectable, () => requestHelmChartVersionsMock);
+ windowDi.override(requestHelmChartReadmeInjectable, () => requestHelmChartReadmeMock);
+ windowDi.override(requestHelmChartValuesInjectable, () => requestHelmChartValuesMock);
windowDi.override(
namespaceStoreInjectable,
() =>
@@ -449,6 +469,16 @@ describe("showing details for helm release", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
+ it("shows spinner", () => {
+ const saveButton = rendered.getByTestId(
+ "helm-release-configuration-save-button",
+ );
+
+ expect(saveButton).toHaveClass("waiting");
+ });
+
+
+
it("calls for update", () => {
expect(requestHelmReleaseUpdateMock).toHaveBeenCalledWith(
"some-name",
@@ -463,14 +493,6 @@ describe("showing details for helm release", () => {
);
});
- it("shows spinner", () => {
- const saveButton = rendered.getByTestId(
- "helm-release-configuration-save-button",
- );
-
- expect(saveButton).toHaveClass("waiting");
- });
-
describe("when update resolves with success", () => {
beforeEach(async () => {
requestHelmReleasesMock.mockClear();
diff --git a/src/main/helm/helm-service/get-helm-chart-readme.global-override-for-injectable.ts b/src/main/helm/helm-service/get-helm-chart-readme.global-override-for-injectable.ts
new file mode 100644
index 0000000000..6d2c016251
--- /dev/null
+++ b/src/main/helm/helm-service/get-helm-chart-readme.global-override-for-injectable.ts
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import { getGlobalOverride } from "../../../common/test-utils/get-global-override";
+import getHelmChartReadmeInjectable from "./get-helm-chart-readme.injectable";
+
+export default getGlobalOverride(getHelmChartReadmeInjectable, () => () => {
+ throw new Error("tried to get a helm chart's readme without overriding");
+});
diff --git a/src/renderer/components/+helm-charts/helm-charts.tsx b/src/renderer/components/+helm-charts/helm-charts.tsx
index ede7b87bdc..a8367aac23 100644
--- a/src/renderer/components/+helm-charts/helm-charts.tsx
+++ b/src/renderer/components/+helm-charts/helm-charts.tsx
@@ -20,8 +20,7 @@ import navigateToHelmChartsInjectable from "../../../common/front-end-routing/ro
import { HelmChartIcon } from "./icon";
import helmChartsInjectable from "./helm-charts/helm-charts.injectable";
import selectedHelmChartInjectable from "./helm-charts/selected-helm-chart.injectable";
-import type { HelmChartStore } from "./store";
-import helmChartStoreInjectable from "./store.injectable";
+import { noop } from "lodash";
enum columnId {
name = "name",
@@ -39,7 +38,6 @@ interface Dependencies {
navigateToHelmCharts: NavigateToHelmCharts;
charts: IAsyncComputed;
selectedChart: IComputedValue;
- helmChartStore: HelmChartStore;
}
@observer
@@ -69,17 +67,31 @@ class NonInjectedHelmCharts extends Component {
};
render() {
+ const { charts } = this.props;
const selectedChart = this.props.selectedChart.get();
return (
-
isConfigurable
tableId="helm_charts"
className="HelmCharts"
- store={this.props.helmChartStore}
+ store={{
+ get isLoaded() {
+ return !charts.pending.get();
+ },
+ failedLoading: false,
+ getTotalCount: () => charts.value.get().length,
+ isSelected: (item) => item === selectedChart,
+ toggleSelection: noop,
+ isSelectedAll: () => false,
+ toggleSelectionAll: () => false,
+ pickOnlySelected: () => [],
+ removeSelectedItems: async () => {},
+ }}
+ preloadStores={false}
getItems={() => this.props.charts.value.get()}
isSelectable={false}
sortingCallbacks={{
@@ -138,8 +150,6 @@ export const HelmCharts = withInjectables(NonInjectedHelmCharts, {
navigateToHelmCharts: di.inject(navigateToHelmChartsInjectable),
charts: di.inject(helmChartsInjectable),
selectedChart: di.inject(selectedHelmChartInjectable),
- helmChartStore: di.inject(helmChartStoreInjectable),
}),
-},
-);
+});
diff --git a/src/renderer/components/+helm-charts/helm-charts/versions.injectable.ts b/src/renderer/components/+helm-charts/helm-charts/versions.injectable.ts
new file mode 100644
index 0000000000..18ae1ac05e
--- /dev/null
+++ b/src/renderer/components/+helm-charts/helm-charts/versions.injectable.ts
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
+import { asyncComputed } from "@ogre-tools/injectable-react";
+import { coerce } from "semver";
+import requestHelmChartVersionsInjectable from "../../../../common/k8s-api/endpoints/helm-charts.api/get-versions.injectable";
+import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
+import { sortCompareChartVersions } from "../../../utils";
+import helmChartsInjectable from "./helm-charts.injectable";
+
+export interface ChartVersion {
+ repo: string;
+ version: string;
+}
+
+const sortChartVersions = (versions: ChartVersion[]) => (
+ versions
+ .map(chartVersion => ({ ...chartVersion, __version: coerce(chartVersion.version, { loose: true }) }))
+ .sort(sortCompareChartVersions)
+ .map(({ __version, ...chartVersion }) => chartVersion)
+);
+
+const helmChartVersionsInjectable = getInjectable({
+ id: "helm-chart-versions-loader",
+ instantiate: (di, release) => {
+ const requestHelmChartVersions = di.inject(requestHelmChartVersionsInjectable);
+ const helmCharts = di.inject(helmChartsInjectable);
+
+ return asyncComputed(async () => {
+ const rawVersions = await Promise.all((
+ helmCharts.value.get()
+ .filter(chart => chart.getName() === release.getChart())
+ .map(chart => chart.getRepository())
+ .map(repo => requestHelmChartVersions(repo, release.getChart()))
+ ));
+
+ return sortChartVersions(rawVersions.flat());
+ }, []);
+ },
+ lifecycle: lifecycleEnum.keyedSingleton({
+ getInstanceKey: (di, release: HelmRelease) => release.getName(),
+ }),
+});
+
+export default helmChartVersionsInjectable;
diff --git a/src/renderer/components/+helm-charts/store.injectable.ts b/src/renderer/components/+helm-charts/store.injectable.ts
deleted file mode 100644
index 99764c38c8..0000000000
--- a/src/renderer/components/+helm-charts/store.injectable.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import requestHelmChartVersionsInjectable from "../../../common/k8s-api/endpoints/helm-charts.api/get-versions.injectable";
-import requestHelmChartsInjectable from "../../../common/k8s-api/endpoints/helm-charts.api/list.injectable";
-import { HelmChartStore } from "./store";
-
-const helmChartStoreInjectable = getInjectable({
- id: "helm-chart-store",
- instantiate: (di) => new HelmChartStore({
- requestHelmCharts: di.inject(requestHelmChartsInjectable),
- requestHelmChartVersions: di.inject(requestHelmChartVersionsInjectable),
- }),
-});
-
-export default helmChartStoreInjectable;
diff --git a/src/renderer/components/+helm-charts/store.ts b/src/renderer/components/+helm-charts/store.ts
deleted file mode 100644
index fee2a381de..0000000000
--- a/src/renderer/components/+helm-charts/store.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import semver from "semver";
-import { observable, makeObservable } from "mobx";
-import { autoBind, sortCompareChartVersions } from "../../utils";
-import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api";
-import { ItemStore } from "../../../common/item.store";
-import flatten from "lodash/flatten";
-import type { RequestHelmCharts } from "../../../common/k8s-api/endpoints/helm-charts.api/list.injectable";
-import type { RequestHelmChartVersions } from "../../../common/k8s-api/endpoints/helm-charts.api/get-versions.injectable";
-
-export interface ChartVersion {
- repo: string;
- version: string;
-}
-
-interface Dependencies {
- requestHelmCharts: RequestHelmCharts;
- requestHelmChartVersions: RequestHelmChartVersions;
-}
-
-export class HelmChartStore extends ItemStore {
- @observable versions = observable.map();
-
- constructor(protected readonly dependencies: Dependencies) {
- super();
-
- makeObservable(this);
- autoBind(this);
- }
-
- async loadAll() {
- try {
- const res = await this.loadItems(() => this.dependencies.requestHelmCharts());
-
- this.failedLoading = false;
-
- return res;
- } catch (error) {
- this.failedLoading = true;
-
- throw error;
- }
- }
-
- getByName(name: string, repo?: string) {
- if (typeof repo !== "string") {
- /**
- * FIXME:
- * This is here because in strict mode `getByName` MUST be 100% compatiable if called in the
- * situation where it is only a "ItemStore"
- */
- throw new TypeError("repo must be provided");
- }
-
- return this.items.find(chart => chart.getName() === name && chart.getRepository() === repo);
- }
-
- protected sortVersions = (versions: ChartVersion[]) => {
- return versions
- .map(chartVersion => ({ ...chartVersion, __version: semver.coerce(chartVersion.version, { loose: true }) }))
- .sort(sortCompareChartVersions)
- .map(({ __version, ...chartVersion }) => chartVersion);
- };
-
- async getVersions(chartName: string, force?: boolean): Promise {
- const versions = this.versions.get(chartName);
-
- if (versions && !force) {
- return versions;
- }
-
- const loadVersions = async (repo: string) => {
- const versions = await this.dependencies.requestHelmChartVersions(repo, chartName);
-
- return versions.map(chart => ({
- repo,
- version: chart.getVersion(),
- }));
- };
-
- if (!this.isLoaded) {
- await this.loadAll();
- }
- const repos = this.items
- .filter(chart => chart.getName() === chartName)
- .map(chart => chart.getRepository());
-
- const newVersions = await Promise.all(repos.map(loadVersions))
- .then(flatten)
- .then(this.sortVersions);
-
- this.versions.set(chartName, newVersions);
-
- return newVersions;
- }
-
- reset() {
- super.reset();
- this.versions.clear();
- }
-
- /**
- * @deprecated Not supported
- */
- removeItems(): Promise {
- throw new Error("removeItems is not supported");
- }
-}
diff --git a/src/renderer/components/+helm-releases/to-helm-release.injectable.ts b/src/renderer/components/+helm-releases/to-helm-release.injectable.ts
index a7a1aca5c8..2e79f9d102 100644
--- a/src/renderer/components/+helm-releases/to-helm-release.injectable.ts
+++ b/src/renderer/components/+helm-releases/to-helm-release.injectable.ts
@@ -4,7 +4,8 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { capitalize } from "lodash";
-import helmChartStoreInjectable from "../+helm-charts/store.injectable";
+import { when } from "mobx";
+import helmChartVersionsInjectable from "../+helm-charts/helm-charts/versions.injectable";
import type { HelmRelease, HelmReleaseDto } from "../../../common/k8s-api/endpoints/helm-releases.api";
import { formatDuration } from "../../utils";
@@ -13,7 +14,7 @@ export type ToHelmRelease = (release: HelmReleaseDto) => HelmRelease;
const toHelmReleaseInjectable = getInjectable({
id: "to-helm-release",
instantiate: (di): ToHelmRelease => {
- const helmChartStore = di.inject(helmChartStoreInjectable);
+ const helmChartVersions = (release: HelmRelease) => di.inject(helmChartVersionsInjectable, release);
return (release) => ({
...release,
@@ -71,14 +72,14 @@ const toHelmReleaseInjectable = getInjectable({
// Helm does not store from what repository the release is installed,
// so we have to try to guess it by searching charts
async getRepo() {
- const chartName = this.getChart();
- const version = this.getVersion();
- const versions = await helmChartStore.getVersions(chartName);
- const chartVersion = versions.find(
- (chartVersion) => chartVersion.version === version,
- );
+ const versionsComputed = helmChartVersions(this);
- return chartVersion ? chartVersion.repo : "";
+ await when(() => !versionsComputed.pending.get());
+
+ const version = this.getVersion();
+ const versions = versionsComputed.value.get();
+
+ return versions.find((chartVersion) => chartVersion.version === version)?.repo ?? "";
},
});
},
diff --git a/src/renderer/components/dock/upgrade-chart/create-upgrade-chart-tab.injectable.ts b/src/renderer/components/dock/upgrade-chart/create-upgrade-chart-tab.injectable.ts
index 3233d08cb5..7bf390da02 100644
--- a/src/renderer/components/dock/upgrade-chart/create-upgrade-chart-tab.injectable.ts
+++ b/src/renderer/components/dock/upgrade-chart/create-upgrade-chart-tab.injectable.ts
@@ -3,23 +3,27 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
+import type { IChartUpgradeData } from "./store.injectable";
import upgradeChartTabStoreInjectable from "./store.injectable";
import dockStoreInjectable from "../dock/store.injectable";
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import type { DockStore, DockTabCreateSpecific, TabId } from "../dock/store";
import { TabKind } from "../dock/store";
-import type { UpgradeChartTabStore } from "./store";
import { runInAction } from "mobx";
import getRandomUpgradeChartTabIdInjectable from "./get-random-upgrade-chart-tab-id.injectable";
+import type { DockTabStore } from "../dock-tab-store/dock-tab.store";
interface Dependencies {
- upgradeChartStore: UpgradeChartTabStore;
+ upgradeChartStore: DockTabStore;
dockStore: DockStore;
getRandomId: () => string;
}
const createUpgradeChartTab = ({ upgradeChartStore, dockStore, getRandomId }: Dependencies) => (release: HelmRelease, tabParams: DockTabCreateSpecific = {}): TabId => {
- const tabId = upgradeChartStore.getTabIdByRelease(release.getName());
+ const tabId = upgradeChartStore.findTabIdFromData(val => (
+ val.releaseName === release.getName()
+ && val.releaseNamespace === release.getNs()
+ ));
if (tabId) {
dockStore.open();
diff --git a/src/renderer/components/dock/upgrade-chart/store.injectable.ts b/src/renderer/components/dock/upgrade-chart/store.injectable.ts
index 2dd3612e56..8884f5935c 100644
--- a/src/renderer/components/dock/upgrade-chart/store.injectable.ts
+++ b/src/renderer/components/dock/upgrade-chart/store.injectable.ts
@@ -3,10 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { UpgradeChartTabStore } from "./store";
import createDockTabStoreInjectable from "../dock-tab-store/create-dock-tab-store.injectable";
-import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
-import requestHelmReleaseConfigurationInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/get-configuration.injectable";
+
+export interface IChartUpgradeData {
+ releaseName: string;
+ releaseNamespace: string;
+}
const upgradeChartTabStoreInjectable = getInjectable({
id: "upgrade-chart-tab-store",
@@ -14,10 +16,8 @@ const upgradeChartTabStoreInjectable = getInjectable({
instantiate: (di) => {
const createDockTabStore = di.inject(createDockTabStoreInjectable);
- return new UpgradeChartTabStore({
- createStorage: di.inject(createStorageInjectable),
- valuesStore: createDockTabStore(),
- requestHelmReleaseConfiguration: di.inject(requestHelmReleaseConfigurationInjectable),
+ return createDockTabStore({
+ storageKey: "chart_releases",
});
},
});
diff --git a/src/renderer/components/dock/upgrade-chart/store.ts b/src/renderer/components/dock/upgrade-chart/store.ts
deleted file mode 100644
index c5e4ea774e..0000000000
--- a/src/renderer/components/dock/upgrade-chart/store.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { action, computed, makeObservable } from "mobx";
-import type { TabId } from "../dock/store";
-import type { DockTabStoreDependencies } from "../dock-tab-store/dock-tab.store";
-import { DockTabStore } from "../dock-tab-store/dock-tab.store";
-import assert from "assert";
-import type { RequestHelmReleaseConfiguration } from "../../../../common/k8s-api/endpoints/helm-releases.api/get-configuration.injectable";
-
-export interface IChartUpgradeData {
- releaseName: string;
- releaseNamespace: string;
-}
-
-export interface UpgradeChartTabStoreDependencies extends DockTabStoreDependencies {
- valuesStore: DockTabStore;
- requestHelmReleaseConfiguration: RequestHelmReleaseConfiguration;
-}
-
-export class UpgradeChartTabStore extends DockTabStore {
- @computed private get releaseNameReverseLookup(): Map {
- return new Map(this.getAllData().map(([id, { releaseName }]) => [releaseName, id]));
- }
-
- get values() {
- return this.dependencies.valuesStore;
- }
-
- constructor(protected readonly dependencies: UpgradeChartTabStoreDependencies) {
- super(dependencies, {
- storageKey: "chart_releases",
- });
-
- makeObservable(this);
- }
-
- @action
- async reloadValues(tabId: TabId) {
- this.values.clearData(tabId); // reset
- const data = this.getData(tabId);
-
- assert(data, "cannot reload values if no data");
-
- const { releaseName, releaseNamespace } = data;
- const values = await this.dependencies.requestHelmReleaseConfiguration(releaseName, releaseNamespace, true);
-
- this.values.setData(tabId, values);
- }
-
- getTabIdByRelease(releaseName: string) {
- return this.releaseNameReverseLookup.get(releaseName);
- }
-}
diff --git a/src/renderer/components/dock/upgrade-chart/upgrade-chart-model.injectable.ts b/src/renderer/components/dock/upgrade-chart/upgrade-chart-model.injectable.ts
new file mode 100644
index 0000000000..deb8a4aedb
--- /dev/null
+++ b/src/renderer/components/dock/upgrade-chart/upgrade-chart-model.injectable.ts
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
+import { asyncComputed } from "@ogre-tools/injectable-react";
+import type { IComputedValue } from "mobx";
+import { action, computed, observable, when } from "mobx";
+import type { SingleValue } from "react-select";
+import type { ChartVersion } from "../../+helm-charts/helm-charts/versions.injectable";
+import helmChartVersionsInjectable from "../../+helm-charts/helm-charts/versions.injectable";
+import releasesInjectable from "../../+helm-releases/releases.injectable";
+import updateReleaseInjectable from "../../+helm-releases/update-release/update-release.injectable";
+import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
+import requestHelmReleaseConfigurationInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/get-configuration.injectable";
+import { waitUntilDefined } from "../../../utils";
+import type { SelectOption } from "../../select";
+import type { DockTab } from "../dock/store";
+import upgradeChartTabStoreInjectable from "./store.injectable";
+
+export interface UpgradeChartModel {
+ readonly release: HelmRelease;
+ readonly versionOptions: IComputedValue[]>;
+ readonly configration: {
+ readonly value: IComputedValue;
+ set: (value: string) => void;
+ readonly error: IComputedValue;
+ setError: (error: unknown) => void;
+ };
+ readonly version: {
+ readonly value: IComputedValue;
+ set: (value: SingleValue>) => void;
+ };
+ submit: () => Promise;
+}
+
+export interface UpgradeChartSubmitResult {
+ completedSuccessfully: boolean;
+}
+
+const upgradeChartModelInjectable = getInjectable({
+ id: "upgrade-chart-model",
+ instantiate: async (di, tab): Promise => {
+ const upgradeChartTabStore = di.inject(upgradeChartTabStoreInjectable);
+ const releases = di.inject(releasesInjectable);
+ const requestHelmReleaseConfiguration = di.inject(requestHelmReleaseConfigurationInjectable);
+ const updateRelease = di.inject(updateReleaseInjectable);
+
+ const tabData = await waitUntilDefined(() => upgradeChartTabStore.getData(tab.id));
+ const release = await waitUntilDefined(() => releases.value.get().find(release => release.getName() === tabData.releaseName));
+ const versions = di.inject(helmChartVersionsInjectable, release);
+ const storedConfigration = asyncComputed(() => requestHelmReleaseConfiguration(
+ release.getName(),
+ release.getNs(),
+ true,
+ ), "");
+
+ await when(() => !versions.pending.get());
+
+ const configrationValue = observable.box();
+ const configrationEditError = observable.box();
+ const configration: UpgradeChartModel["configration"] = {
+ value: computed(() => configrationValue.get() ?? storedConfigration.value.get()),
+ set: action((value) => {
+ configrationValue.set(value);
+ configrationEditError.set(undefined);
+ }),
+ error: computed(() => configrationEditError.get()),
+ setError: action((error) => configrationEditError.set(String(error))),
+ };
+ const versionValue = observable.box(versions.value.get()[0]);
+ const version: UpgradeChartModel["version"] = {
+ value: computed(() => versionValue.get()),
+ set: action((option) => versionValue.set(option?.value)),
+ };
+ const versionOptions = computed(() => (
+ versions.value
+ .get()
+ .map(version => ({
+ value: version,
+ label: `${version.repo}/${release.getChart()}-${version.version}`,
+ }))
+ ));
+
+ return {
+ release,
+ versionOptions,
+ configration,
+ version,
+ submit: async () => {
+ const version = versionValue.get();
+
+ if (!version || configrationEditError.get()) {
+ return {
+ completedSuccessfully: false,
+ };
+ }
+
+ await updateRelease(
+ release.getName(),
+ release.getNs(),
+ {
+ chart: release.getChart(),
+ values: configration.value.get(),
+ ...version,
+ },
+ );
+ storedConfigration.invalidate();
+
+ return {
+ completedSuccessfully: true,
+ };
+ },
+ };
+ },
+ lifecycle: lifecycleEnum.keyedSingleton({
+ getInstanceKey: (di, tab: DockTab) => tab.id,
+ }),
+});
+
+export default upgradeChartModelInjectable;
diff --git a/src/renderer/components/dock/upgrade-chart/view.tsx b/src/renderer/components/dock/upgrade-chart/view.tsx
index 4955d55b3b..a333b4c9c1 100644
--- a/src/renderer/components/dock/upgrade-chart/view.tsx
+++ b/src/renderer/components/dock/upgrade-chart/view.tsx
@@ -6,27 +6,20 @@
import "./upgrade-chart.scss";
import React from "react";
-import { action, makeObservable, observable, reaction } from "mobx";
-import { disposeOnUnmount, observer } from "mobx-react";
+import { makeObservable, observable } from "mobx";
+import { observer } from "mobx-react";
import { cssNames } from "../../../utils";
import type { DockTab } from "../dock/store";
import { InfoPanel } from "../info-panel";
-import type { UpgradeChartTabStore } from "./store";
import { Spinner } from "../../spinner";
import { Badge } from "../../badge";
import { EditorPanel } from "../editor-panel";
-import type { HelmChartStore, ChartVersion } from "../../+helm-charts/store";
-import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import type { SelectOption } from "../../select";
import { Select } from "../../select";
-import type { IAsyncComputed } from "@ogre-tools/injectable-react";
import { withInjectables } from "@ogre-tools/injectable-react";
-import upgradeChartTabStoreInjectable from "./store.injectable";
-import updateReleaseInjectable from "../../+helm-releases/update-release/update-release.injectable";
-import releasesInjectable from "../../+helm-releases/releases.injectable";
-import type { RequestHelmReleaseUpdate } from "../../../../common/k8s-api/endpoints/helm-releases.api/update.injectable";
-import { first } from "lodash/fp";
-import helmChartStoreInjectable from "../../+helm-charts/store.injectable";
+import type { ChartVersion } from "../../+helm-charts/helm-charts/versions.injectable";
+import type { UpgradeChartModel } from "./upgrade-chart-model.injectable";
+import upgradeChartModelInjectable from "./upgrade-chart-model.injectable";
export interface UpgradeChartProps {
className?: string;
@@ -34,160 +27,78 @@ export interface UpgradeChartProps {
}
interface Dependencies {
- releases: IAsyncComputed;
- upgradeChartTabStore: UpgradeChartTabStore;
- updateRelease: RequestHelmReleaseUpdate;
- helmChartStore: HelmChartStore;
+ model: UpgradeChartModel;
}
@observer
export class NonInjectedUpgradeChart extends React.Component {
@observable error?: string;
- @observable versions = observable.array();
- @observable version: ChartVersion | undefined = undefined;
constructor(props: UpgradeChartProps & Dependencies) {
super(props);
makeObservable(this);
}
- componentDidMount() {
- disposeOnUnmount(this, [
- reaction(
- () => this.release,
- release => this.reloadVersions(release),
- {
- fireImmediately: true,
- },
- ),
- reaction(
- () => this.release?.getRevision(),
- () => this.reloadValues(),
- {
- fireImmediately: true,
- },
- ),
- ]);
- }
-
- get tabId() {
- return this.props.tab.id;
- }
-
- get release() {
- const tabData = this.props.upgradeChartTabStore.getData(this.tabId);
-
- if (!tabData) return null;
-
- return this.props.releases.value.get().find(release => release.getName() === tabData.releaseName);
- }
-
- get value() {
- return this.props.upgradeChartTabStore.values.getData(this.tabId);
- }
-
- async reloadValues() {
- this.props.upgradeChartTabStore.reloadValues(this.props.tab.id);
- }
-
- async reloadVersions(release: HelmRelease | null | undefined) {
- if (!release) {
- return;
- }
-
- this.version = undefined;
- this.versions.clear();
- const versions = await this.props.helmChartStore.getVersions(release.getChart());
-
- this.versions.replace(versions);
- this.version = first(this.versions);
- }
-
- onChange = action((value: string) => {
- this.error = "";
- this.props.upgradeChartTabStore.values.setData(this.tabId, value);
- });
-
- onError = action((error: Error | string) => {
- this.error = error.toString();
- });
-
upgrade = async () => {
- if (this.error || !this.release || !this.version || !this.value) {
- return null;
+ const { model } = this.props;
+ const { completedSuccessfully } = await model.submit();
+
+ if (completedSuccessfully) {
+ return (
+