From 2b33d5eed69ecef4ce921eb73da4cc4afd07fcb5 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 9 Jan 2023 13:15:37 -0800 Subject: [PATCH] Remove a lot of usages of legacy globals (#6825) * Remove all usages of legacy global logger Signed-off-by: Sebastian Malton * Remove usages of legacy global storageClassApi Signed-off-by: Sebastian Malton * Remove usages of legacy global serviceApi Signed-off-by: Sebastian Malton * Remove usages of legacy global secretApi Signed-off-by: Sebastian Malton * Remove usages of legacy global resourceQuotaApi Signed-off-by: Sebastian Malton * Remove usages of legacy global persistentVolumeClaimApi Signed-off-by: Sebastian Malton * Remove usages of legacy global nodeApi Signed-off-by: Sebastian Malton * Remove usages of legacy global networkPolicyApi Signed-off-by: Sebastian Malton * Fix formatting Signed-off-by: Sebastian Malton * Remove usages of legacy global configMapApi Signed-off-by: Sebastian Malton * Remove usages of legacy global jobApi Signed-off-by: Sebastian Malton * Remove usages of legacy global cronJobApi Signed-off-by: Sebastian Malton * Remove usages of legacy global podApi Signed-off-by: Sebastian Malton * Remove usages of legacy global roleApi Signed-off-by: Sebastian Malton * Remove exports of NonInjected components - To improve auto-complete Signed-off-by: Sebastian Malton * Remove usages of legacy global catalogEntityRegistry Signed-off-by: Sebastian Malton * Add lint against using legacy global works Signed-off-by: Sebastian Malton * Fix lint in KubeObject file Signed-off-by: Sebastian Malton * Remove usages of legacy global catalogCategoryRegistry Signed-off-by: Sebastian Malton * Remove usages of legacy global navigation Signed-off-by: Sebastian Malton * Remove usages of legacy global navigate Signed-off-by: Sebastian Malton * Remove unneeded index file Signed-off-by: Sebastian Malton * Remove usages of legacy global customResourceDefinitionStore Signed-off-by: Sebastian Malton * Remove usages of legacy global eventStore Signed-off-by: Sebastian Malton * Remove usages of legacy global podSecurityPolicyStore Signed-off-by: Sebastian Malton * Remove usages of legacy global storageClassStore Signed-off-by: Sebastian Malton * Remove usages of legacy global persistentVolumeStore Signed-off-by: Sebastian Malton * Remove usages of legacy global clusterRoleBindingStore Signed-off-by: Sebastian Malton * Remove usages of legacy global clusterRoleStore Signed-off-by: Sebastian Malton * Remove usages of legacy global roleBindingStore Signed-off-by: Sebastian Malton * Move files around Signed-off-by: Sebastian Malton * Remove usages of legacy global roleStore Signed-off-by: Sebastian Malton * Remove usages of legacy global serviceAccountStore Signed-off-by: Sebastian Malton * Remove usages of legacy global podStore Signed-off-by: Sebastian Malton * Remove final uses of asLegacyGlobalForExtensionApi in application code Signed-off-by: Sebastian Malton * Fix lint rule and start fixing lint errors Signed-off-by: Sebastian Malton * Make listHelmReleases injectable Signed-off-by: Sebastian Malton * Make installHelmChart injectable Signed-off-by: Sebastian Malton * Make deleteHelmRelease injectable Signed-off-by: Sebastian Malton * Make getHelmReleaseValues injectable Signed-off-by: Sebastian Malton * Make getHelmReleaseHistory injectable Signed-off-by: Sebastian Malton * Make rollbackHelmRelease injectable Signed-off-by: Sebastian Malton * Remove unnecessary overrides from getDiForUnitTesting Signed-off-by: Sebastian Malton * Add global override for logger Signed-off-by: Sebastian Malton * Add global override for history Signed-off-by: Sebastian Malton * Cleanup typing around getting injectables within jest Signed-off-by: Sebastian Malton * Move some more overrides to global if needed Signed-off-by: Sebastian Malton * Add global override for forcusWindow Signed-off-by: Sebastian Malton * Fix lint in KubeApi Signed-off-by: Sebastian Malton * Fix lint in catalog/index.ts Signed-off-by: Sebastian Malton * Remove last usages of legacy global getDetailsUrl Signed-off-by: Sebastian Malton * Remove last usages of legacy global showDetails Signed-off-by: Sebastian Malton * Add functions back to extension API Signed-off-by: Sebastian Malton * Remove all uses of legacy global Notification functions Signed-off-by: Sebastian Malton * Remove no longer existing export Signed-off-by: Sebastian Malton * Remove unnecessary overrides Signed-off-by: Sebastian Malton * Fix injecting side effects by using more injectables Signed-off-by: Sebastian Malton * Fix cluster role binding dialog Signed-off-by: Sebastian Malton * Fix cluster role dialog Signed-off-by: Sebastian Malton * Fix create service account dialog Signed-off-by: Sebastian Malton * Fix history override Signed-off-by: Sebastian Malton * Make KubeApi consume its dependencies while not breaking the extension API Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton * Fix CephFs tests Signed-off-by: Sebastian Malton * Fix user store tests by using override of config Signed-off-by: Sebastian Malton * Remove use of mockFs in hotbar store tests Signed-off-by: Sebastian Malton * Add direct devDep of memfs Signed-off-by: Sebastian Malton * Remove use of mockFs in cluster store tests Signed-off-by: Sebastian Malton * Remove use of mockFs in router tests Signed-off-by: Sebastian Malton * Remove use of mockFs in kube auth proxy tests Signed-off-by: Sebastian Malton * Remove use of mockFs in log resource selector tests Signed-off-by: Sebastian Malton * Remove use of mockFs in hotbar remove command tests Signed-off-by: Sebastian Malton * Remove use of mockFs in the select component tests Signed-off-by: Sebastian Malton * Remove mock-fs as a dependency Signed-off-by: Sebastian Malton * Remove no longer necessary mocks Signed-off-by: Sebastian Malton * Fix namespace select filter tests Signed-off-by: Sebastian Malton * Remove use of mockFs in the extension loader tests Signed-off-by: Sebastian Malton * Fixing edit-namespace-from-new-tab test Signed-off-by: Sebastian Malton * Fix app paths tests by making the tests run in "production" mode Signed-off-by: Sebastian Malton * Remove last vestiges of isTestEnv and the is* globals Signed-off-by: Sebastian Malton * Fix delete-cluster-dialog tests Signed-off-by: Sebastian Malton * Fix install-helm-chart-from-previously-opened-tab tests - Split out storage initialization to a runnable Signed-off-by: Sebastian Malton * Remove unnecessary override of non side effect injectable Signed-off-by: Sebastian Malton * Fix disable-kube-object-detail-items-when-cluster-is-not-relevant tests Signed-off-by: Sebastian Malton * Fix reactively-hide-kube-object-detail-item tests Signed-off-by: Sebastian Malton * Fix lint Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton * Fix sidebar-and-tab-navigation-for-core tests Signed-off-by: Sebastian Malton * Remove need to override lensLocalStoragePath in tests Signed-off-by: Sebastian Malton * Replace more overrides with global ones Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton * Fix typings Signed-off-by: Sebastian Malton * Remove no longer needed technical test Signed-off-by: Sebastian Malton * Update snapshots Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- .eslintrc.js | 9 + package.json | 2 +- src/common/__tests__/cluster-store.test.ts | 351 +++---- src/common/__tests__/hotbar-store.test.ts | 134 ++- src/common/__tests__/user-store.test.ts | 94 +- src/common/app-paths/app-paths.test.ts | 112 +- ...custom-kube-config-directory.injectable.ts | 10 +- ...npm-cli.global-override-for-injectable.ts} | 6 +- .../app-paths/path-to-npm-cli.injectable.ts | 13 + src/common/catalog-entities/general.ts | 6 +- .../catalog-entities/kubernetes-cluster.ts | 10 +- src/common/fs/write-buffer-sync.injectable.ts | 29 + ...le-model.global-override-for-injectable.ts | 62 +- ...-message.global-override-for-injectable.ts | 9 + src/common/ipc/ipc.ts | 5 +- .../k8s-api/__tests__/api-manager.test.ts | 6 + .../kube-api-version-detection.test.ts | 27 +- src/common/k8s-api/__tests__/kube-api.test.ts | 61 +- .../__tests__/kube-object.store.test.ts | 8 + .../create-kube-api-for-cluster.injectable.ts | 54 +- ...-kube-api-for-remote-cluster.injectable.ts | 35 +- .../k8s-api/create-kube-api.injectable.ts | 26 + .../cluster-role-binding.api.injectable.ts | 7 +- .../endpoints/cluster-role-binding.api.ts | 6 +- .../endpoints/cluster-role.api.injectable.ts | 7 +- .../k8s-api/endpoints/cluster-role.api.ts | 6 +- .../endpoints/cluster.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/cluster.api.ts | 8 +- .../component-status.api.injectable.ts | 7 +- .../k8s-api/endpoints/component-status.api.ts | 6 +- .../endpoints/config-map.api.injectable.ts | 7 +- .../k8s-api/endpoints/config-map.api.ts | 6 +- .../endpoints/cron-job.api.injectable.ts | 5 + src/common/k8s-api/endpoints/cron-job.api.ts | 6 +- ...stom-resource-definition.api.injectable.ts | 7 +- .../custom-resource-definition.api.ts | 6 +- .../endpoints/daemon-set.api.injectable.ts | 7 +- .../k8s-api/endpoints/daemon-set.api.ts | 8 +- .../endpoints/deployment.api.injectable.ts | 7 +- .../k8s-api/endpoints/deployment.api.ts | 8 +- .../endpoints/endpoint.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/endpoint.api.ts | 6 +- .../endpoints/events.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/events.api.ts | 6 +- ...orizontal-pod-autoscaler.api.injectable.ts | 7 +- .../horizontal-pod-autoscaler.api.ts | 6 +- src/common/k8s-api/endpoints/index.ts | 1 - .../endpoints/ingress-class.api.injectable.ts | 9 +- .../k8s-api/endpoints/ingress-class.api.ts | 6 +- .../endpoints/ingress.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/ingress.api.ts | 8 +- .../k8s-api/endpoints/job.api.injectable.ts | 5 + src/common/k8s-api/endpoints/job.api.ts | 8 +- .../k8s-api/endpoints/lease.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/lease.api.ts | 8 +- .../k8s-api/endpoints/legacy-globals.ts | 78 -- .../endpoints/limit-range.api.injectable.ts | 7 +- .../k8s-api/endpoints/limit-range.api.ts | 6 +- .../endpoints/namespace.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/namespace.api.ts | 8 +- .../network-policy.api.injectable.ts | 7 +- .../k8s-api/endpoints/network-policy.api.ts | 6 +- .../k8s-api/endpoints/node.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/node.api.ts | 8 +- .../persistent-volume-claim.api.injectable.ts | 7 +- .../endpoints/persistent-volume-claim.api.ts | 8 +- .../persistent-volume.api.injectable.ts | 7 +- .../endpoints/persistent-volume.api.ts | 6 +- .../pod-disruption-budget.api.injectable.ts | 7 +- .../endpoints/pod-disruption-budget.api.ts | 6 +- .../endpoints/pod-metrics.api.injectable.ts | 7 +- .../k8s-api/endpoints/pod-metrics.api.ts | 6 +- .../pod-security-policy.api.injectable.ts | 7 +- .../endpoints/pod-security-policy.api.ts | 6 +- .../k8s-api/endpoints/pod.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/pod.api.ts | 8 +- .../priority-class.api.injectable.ts | 7 +- .../k8s-api/endpoints/priority-class.api.ts | 6 +- .../endpoints/replica-set.api.injectable.ts | 7 +- .../k8s-api/endpoints/replica-set.api.ts | 8 +- .../resource-quota.api.injectable.ts | 7 +- .../k8s-api/endpoints/resource-quota.api.ts | 6 +- .../endpoints/role-binding.api.injectable.ts | 7 +- .../k8s-api/endpoints/role-binding.api.ts | 6 +- .../k8s-api/endpoints/role.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/role.api.ts | 6 +- .../endpoints/runtime-class.api.injectable.ts | 7 +- .../k8s-api/endpoints/runtime-class.api.ts | 6 +- .../endpoints/secret.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/secret.api.ts | 6 +- ...lf-subject-rules-reviews.api.injectable.ts | 7 +- .../self-subject-rules-reviews.api.ts | 8 +- .../service-account.api.injectable.ts | 7 +- .../k8s-api/endpoints/service-account.api.ts | 6 +- .../endpoints/service.api.injectable.ts | 7 +- src/common/k8s-api/endpoints/service.api.ts | 6 +- .../endpoints/stateful-set.api.injectable.ts | 7 +- .../k8s-api/endpoints/stateful-set.api.ts | 8 +- .../endpoints/storage-class.api.injectable.ts | 7 +- .../k8s-api/endpoints/storage-class.api.ts | 6 +- src/common/k8s-api/kube-api.ts | 36 +- src/common/k8s-api/kube-object.store.ts | 7 +- src/common/k8s-api/kube-object.ts | 12 +- .../k8s-api/maybe-kube-api.injectable.ts | 19 + src/common/kube-helpers.ts | 3 - .../logger.global-override-for-injectable.ts | 16 + src/common/logger.ts | 9 - src/common/test-utils/use-fake-time.ts | 2 +- .../utils/environment-variables.injectable.ts | 27 - ...ndom-id.global-override-for-injectable.ts} | 6 +- .../utils/reactive-now/reactive-now.test.tsx | 4 +- src/common/vars.ts | 15 - ...ormation.global-override-for-injectable.ts | 4 +- src/common/vars/is-development.injectable.ts | 9 +- src/common/vars/is-test-env.injectable.ts | 18 - ...rces-dir.global-override-for-injectable.ts | 9 + ...alized-platform-architecture.injectable.ts | 10 +- ...platform.global-override-for-injectable.ts | 9 + src/common/vars/platform.injectable.ts | 1 - ...ess-arch.global-override-for-injectable.ts | 9 + src/common/vars/process-arch.injectable.ts | 13 + .../__tests__/extension-loader.test.ts | 11 - src/extensions/common-api/index.ts | 4 +- src/extensions/common-api/k8s-api.ts | 95 +- ...ion-installation-state-store.injectable.ts | 5 +- .../extension-installation-state-store.ts | 63 +- .../extension-installer.injectable.ts | 4 + .../extension-installer.ts | 20 +- src/extensions/ipc/ipc-main.ts | 11 +- src/extensions/ipc/ipc-registrar.ts | 2 +- src/extensions/lens-renderer-extension.ts | 3 +- src/extensions/renderer-api/components.ts | 41 +- src/extensions/renderer-api/navigation.ts | 2 +- .../application-menu/application-menu.test.ts | 4 +- ...g-of-orphan-application-menu-items.test.ts | 4 +- .../analytics-for-installing-update.test.ts | 4 +- ...alling-update-using-topbar-button.test.tsx | 4 +- .../update-button/update-button.tsx | 2 +- ...g-time-since-update-was-downloaded.test.ts | 4 +- .../periodical-checking-of-updates.test.ts | 4 +- .../installing-update.test.ts | 4 +- .../opening-entity-details.test.tsx.snap | 28 - ...ing-cluster-frame-components.test.tsx.snap | 38 + .../order-of-sidebar-items.test.tsx.snap | 76 ++ ...-and-tab-navigation-for-core.test.tsx.snap | 266 +++++ ...ab-navigation-for-extensions.test.tsx.snap | 304 ++++++ .../visibility-of-sidebar-items.test.tsx.snap | 76 ++ .../workload-overview.test.tsx.snap | 38 + .../delete-cluster-dialog.test.tsx | 29 +- ...when-cluster-is-not-relevant.test.tsx.snap | 114 ++ ...when-cluster-is-not-relevant.test.tsx.snap | 114 ++ ...ages-when-cluster-is-not-relevant.test.tsx | 5 - ...tems-when-cluster-is-not-relevant.test.tsx | 5 - ...when-cluster-is-not-relevant.test.tsx.snap | 985 ++++++++++++------ ...hide-kube-object-detail-item.test.tsx.snap | 660 ++++++++---- ...tems-when-cluster-is-not-relevant.test.tsx | 41 - ...vely-hide-kube-object-detail-item.test.tsx | 38 - ...when-cluster-is-not-relevant.test.tsx.snap | 114 ++ ...tems-when-cluster-is-not-relevant.test.tsx | 3 - ...how-status-for-a-kube-object.test.tsx.snap | 228 ++++ ...when-cluster-is-not-relevant.test.tsx.snap | 114 ++ ...uses-when-cluster-is-not-relevant.test.tsx | 3 - ...eactively-hide-kube-object-status.test.tsx | 3 - .../show-status-for-a-kube-object.test.tsx | 4 +- .../edit-namespace-from-new-tab.test.tsx.snap | 608 +++++++++++ ...e-from-previously-opened-tab.test.tsx.snap | 76 ++ .../edit-namespace-from-new-tab.test.tsx | 107 +- ...espace-from-previously-opened-tab.test.tsx | 9 - ...debar-and-tab-navigation-for-core.test.tsx | 29 +- ...and-tab-navigation-for-extensions.test.tsx | 4 +- ...when-cluster-is-not-relevant.test.tsx.snap | 114 ++ ...ails-when-cluster-is-not-relevant.test.tsx | 4 - ...elm-repository-in-preferences.test.ts.snap | 48 +- ...tory-from-list-in-preferences.test.ts.snap | 40 +- ...m-repositories-in-preferences.test.ts.snap | 40 +- ...ive-repository-in-preferences.test.ts.snap | 16 +- ...tom-helm-repository-in-preferences.test.ts | 4 +- ...sitories.global-override-for-injectable.ts | 9 + ...lling-helm-chart-from-new-tab.test.ts.snap | 811 +++++++++++++- ...rt-from-previously-opened-tab.test.ts.snap | 76 ++ ...tab-for-installing-helm-chart.test.ts.snap | 342 ++++++ ...m-chart-from-previously-opened-tab.test.ts | 27 +- .../upgrade-chart-new-tab.test.ts.snap | 114 ++ .../upgrade-chart-new-tab.test.ts | 4 +- ...wing-details-for-helm-release.test.ts.snap | 608 +++++++++++ .../showing-details-for-helm-release.test.ts | 4 +- .../__snapshots__/download-logs.test.tsx.snap | 76 ++ ...ion-to-kubernetes-preferences.test.ts.snap | 4 +- .../preferences/closing-preferences.test.tsx | 2 +- ...ing-the-app-using-application-menu.test.ts | 4 +- ...ironment.global-override-for-injectable.ts | 11 + .../shell-sync/main/setup-shell.injectable.ts | 1 - src/jest.setup.ts | 24 +- src/main/__test__/cluster.test.ts | 3 - src/main/__test__/kube-auth-proxy.test.ts | 90 +- src/main/__test__/static-file-route.test.ts | 16 - ...-testing.global-override-for-injectable.ts | 9 + ...tory-for-integration-testing.injectable.ts | 9 +- .../get-electron-app-path.injectable.ts | 22 +- .../get-electron-app-path.ts | 28 - .../set-electron-app-path.injectable.ts | 9 +- .../app-paths/setup-app-paths.injectable.ts | 6 +- src/main/catalog-pusher.ts | 32 - ...adcaster.global-override-for-injectable.ts | 10 + .../broadcaster.injectable.ts | 29 + .../catalog-sync-to-renderer.injectable.ts | 25 +- .../push-catalog-to-renderer.injectable.ts | 20 + src/main/catalog/catalog-entity-registry.ts | 12 - src/main/catalog/index.ts | 1 - src/main/cluster/manager.injectable.ts | 2 + src/main/cluster/manager.ts | 48 +- .../3.6.0-beta.1.injectable.ts | 9 +- .../5.0.0-beta.10.injectable.ts | 13 +- ...tron-app.global-override-for-injectable.ts | 49 +- ...n-dialog.global-override-for-injectable.ts | 26 + .../features/focus-application.injectable.ts | 2 - ...ve-theme.global-override-for-injectable.ts | 16 + .../setup-ipc-main-handlers.injectable.ts | 6 +- .../setup-ipc-main-handlers.ts | 9 +- src/main/getDiForUnitTesting.ts | 140 +-- .../helm/delete-helm-release.injectable.ts | 37 + .../get-helm-release-history.injectable.ts | 39 + .../get-helm-release-values.injectable.ts | 49 + src/main/helm/helm-release-manager.ts | 168 --- .../delete-helm-release.injectable.ts | 18 +- .../get-helm-chart-values.injectable.ts | 2 - .../get-helm-chart-versions.injectable.ts | 2 - .../get-helm-release-history.injectable.ts | 18 +- .../get-helm-release-values.injectable.ts | 31 +- .../install-helm-chart.injectable.ts | 23 +- .../list-helm-charts.injectable.ts | 2 - .../list-helm-releases.injectable.ts | 15 +- .../rollback-helm-release.injectable.ts | 24 +- .../helm/install-helm-chart.injectable.ts | 93 ++ .../helm/list-helm-releases.injectable.ts | 48 + .../helm/rollback-helm-release.injectable.ts | 37 + .../create-kube-auth-proxy.injectable.ts | 2 + src/main/kube-auth-proxy/kube-auth-proxy.ts | 9 +- src/main/kubectl/create-kubectl.injectable.ts | 2 + src/main/kubectl/kubectl.ts | 39 +- .../kubectl/normalized-arch.injectable.ts | 10 +- .../get-cluster-for-request.injectable.ts | 45 + src/main/lens-proxy/lens-proxy.injectable.ts | 6 +- src/main/lens-proxy/lens-proxy.ts | 8 +- .../shell-api-request.injectable.ts | 42 + .../shell-api-request.injectable.ts | 21 - .../shell-api-request/shell-api-request.ts | 36 - .../shell-request-authenticator.injectable.ts | 0 .../shell-request-authenticator.ts | 6 +- src/main/logger.ts | 8 - src/main/router/router.test.ts | 11 +- .../delete-release-route.injectable.ts | 10 +- .../get-release-history-route.injectable.ts | 12 +- .../get-release-values-route.injectable.ts | 12 +- .../install-chart-route.injectable.ts | 4 +- .../list-releases-route.injectable.ts | 4 +- .../rollback-release-route.injectable.ts | 10 +- .../create-port-forward.injectable.ts | 19 +- .../functionality/port-forward.ts | 14 +- .../node-shell-session/node-shell-session.ts | 7 +- .../node-shell-session/open.injectable.ts | 2 + .../start-kube-config-sync.injectable.ts | 2 - .../setup-hardware-acceleration.injectable.ts | 4 +- ...rguments.global-override-for-injectable.ts | 9 + .../utils/get-port-from-stream.injectable.ts | 77 ++ src/main/utils/get-port.ts | 71 -- ...ormation.global-override-for-injectable.ts | 24 - ...disabled.global-override-for-injectable.ts | 9 + ...eleration-should-be-disabled.injectable.ts | 13 + .../api/__tests__/websocket-api.test.ts | 8 +- src/renderer/api/catalog-category-registry.ts | 11 - .../api/create-terminal-api.injectable.ts | 9 +- ...default-websocket-api-params.injectable.ts | 22 + src/renderer/api/terminal-api.ts | 13 +- src/renderer/api/websocket-api.ts | 20 +- .../setup-auto-registration.injectable.ts | 14 +- ...es-cluster-context-menu-open.injectable.ts | 2 +- src/renderer/bootstrap.tsx | 2 +- .../components/+add-cluster/add-cluster.tsx | 16 +- .../+catalog/catalog-add-button.tsx | 37 +- .../components/+cluster/cluster-issues.tsx | 2 +- .../cluster-overview-store.injectable.ts | 2 + .../+config-autoscalers/hpa-details.tsx | 7 +- .../+config-autoscalers/store.injectable.ts | 2 + .../+config-leases/store.injectable.ts | 2 + .../limit-range-details.tsx | 19 +- .../+config-limit-ranges/store.injectable.ts | 2 + .../+config-maps/store.injectable.ts | 2 + .../pod-disruption-budgets-details.tsx | 19 +- .../store.injectable.ts | 2 + .../store.injectable.ts | 2 + .../add-dialog/close.injectable.ts | 18 + .../add-dialog/is-open.injectable.ts | 18 + .../add-dialog/open-state.injectable.ts | 13 + .../add-dialog/open.injectable.ts | 18 + .../view.scss} | 0 .../view.tsx} | 119 ++- .../resource-quota-details.tsx | 19 +- .../resource-quotas.tsx | 7 +- .../store.injectable.ts | 2 + .../store.injectable.ts | 2 + .../add-dialog/close.injectable.ts | 18 + .../add-dialog/is-open.injectable.ts | 18 + .../add-dialog/open.injectable.ts | 18 + .../add-dialog/state.injectable.ts | 13 + .../view.scss} | 0 .../view.tsx} | 83 +- .../components/+config-secrets/secrets.tsx | 7 +- .../+config-secrets/store.injectable.ts | 2 + .../+custom-resources/crd-details.tsx | 19 +- .../components/+custom-resources/crd-list.tsx | 5 +- .../crd-resource-details.tsx | 19 +- .../definition.store.injectable.ts | 2 + .../+custom-resources/legacy-store.ts | 12 - .../components/+events/event-details.tsx | 6 +- .../components/+events/kube-event-details.tsx | 7 +- .../components/+events/kube-event-icon.tsx | 28 +- .../components/+events/legacy-store.ts | 12 - .../components/+events/store.injectable.ts | 2 + .../attempt-install.injectable.tsx | 16 +- ...confirm-uninstall-extension.injectable.tsx | 2 +- .../uninstall-extension.injectable.tsx | 84 ++ .../uninstall-extension.injectable.ts | 24 - .../uninstall-extension.tsx | 79 -- .../+helm-releases/dialog/dialog.tsx | 7 +- .../+namespaces/add-dialog/dialog.tsx | 7 +- .../namespace-select-filter.test.tsx | 6 +- src/renderer/components/+namespaces/route.tsx | 2 +- .../+namespaces/store.injectable.ts | 2 + src/renderer/components/+namespaces/store.ts | 7 +- .../+network-endpoints/endpoint-details.tsx | 19 +- .../+network-endpoints/store.injectable.ts | 2 + .../ingress-class-store.injectable.ts | 15 +- .../ingress-store.injectable.ts | 2 + .../network-policy-details.tsx | 19 +- .../+network-policies/store.injectable.ts | 2 + .../port-forward-details.tsx | 33 +- .../port-forward-menu.tsx | 33 +- .../+network-services/service-details.tsx | 7 +- .../service-port-component.tsx | 40 +- .../+network-services/store.injectable.ts | 2 + .../components/+nodes/store.injectable.ts | 2 + .../+pod-security-policies/legacy-store.ts | 12 - .../pod-security-policies.tsx | 19 +- .../pod-security-policy-details.tsx | 19 +- .../store.injectable.ts | 2 + .../+storage-classes/legacy-store.ts | 12 - .../storage-class-details.tsx | 7 +- .../+storage-classes/storage-classes.tsx | 19 +- .../+storage-classes/store.injectable.ts | 2 + .../store.injectable.ts | 2 + .../+storage-volumes/legacy-store.ts | 12 - .../+storage-volumes/store.injectable.ts | 2 + .../+storage-volumes/volume-details-list.tsx | 26 +- .../+storage-volumes/volume-details.tsx | 43 +- .../components/+storage-volumes/volumes.tsx | 41 +- .../__tests__/dialog.test.tsx | 15 +- .../+cluster-role-bindings/details.tsx | 16 +- .../dialog/close.injectable.ts | 27 + .../dialog/edit-name-state.injectable.ts | 13 + .../dialog/open.injectable.ts | 29 + .../dialog/state.injectable.ts | 24 + .../{dialog.scss => dialog/view.scss} | 0 .../{dialog.tsx => dialog/view.tsx} | 161 +-- .../+cluster-role-bindings/index.ts | 2 +- .../+cluster-role-bindings/legacy-store.ts | 12 - .../store.injectable.ts | 2 + .../+cluster-role-bindings/view.tsx | 42 +- .../+cluster-roles/add-dialog.tsx | 91 -- .../add-dialog/close.injectable.ts | 21 + .../add-dialog/open.injectable.ts | 23 + .../add-dialog/state.injectable.ts | 22 + .../{add-dialog.scss => add-dialog/view.scss} | 0 .../+cluster-roles/add-dialog/view.tsx | 102 ++ .../+user-management/+cluster-roles/index.ts | 2 +- .../+cluster-roles/legacy-store.ts | 12 - .../+cluster-roles/store.injectable.ts | 2 + .../+user-management/+cluster-roles/view.tsx | 30 +- .../+role-bindings/__tests__/dialog.test.tsx | 14 +- .../+role-bindings/details.tsx | 16 +- .../+role-bindings/dialog/close.injectable.ts | 18 + .../+role-bindings/dialog/open.injectable.ts | 24 + .../+role-bindings/dialog/state.injectable.ts | 22 + .../{dialog.scss => dialog/view.scss} | 0 .../{dialog.tsx => dialog/view.tsx} | 139 ++- .../+user-management/+role-bindings/index.ts | 2 +- .../+role-bindings/legacy-store.ts | 12 - .../+role-bindings/store.injectable.ts | 2 + .../+user-management/+role-bindings/view.tsx | 9 +- .../+user-management/+roles/add-dialog.tsx | 100 -- .../+roles/add-dialog/close.injectable.ts | 22 + .../+roles/add-dialog/open.injectable.ts | 22 + .../+roles/add-dialog/state.injectable.ts | 24 + .../{add-dialog.scss => add-dialog/view.scss} | 0 .../+roles/add-dialog/view.tsx | 112 ++ .../+user-management/+roles/index.ts | 2 +- .../+user-management/+roles/legacy-store.ts | 12 - .../+roles/store.injectable.ts | 2 + .../+user-management/+roles/view.tsx | 8 +- .../+service-accounts/create-dialog.tsx | 96 -- .../create-dialog/close.injectable.ts | 22 + .../create-dialog/open.injectable.ts | 24 + .../create-dialog/state.injectable.ts | 24 + .../view.scss} | 0 .../+service-accounts/create-dialog/view.tsx | 112 ++ .../+service-accounts/index.ts | 2 +- .../+service-accounts/legacy-store.ts | 12 - .../+service-accounts/store.injectable.ts | 2 + .../+service-accounts/view.tsx | 9 +- .../+welcome/__test__/welcome.test.tsx | 9 - .../+workloads-cronjobs/cron-job-menu.tsx | 25 +- ...luster-frame-child-component.injectable.ts | 4 +- .../+workloads-cronjobs/cronjob-details.tsx | 14 +- .../cronjob-trigger-dialog.tsx | 133 --- .../+workloads-cronjobs/store.injectable.ts | 2 + .../trigger-dialog/close.injectable.ts | 18 + .../trigger-dialog/open.injectable.ts | 21 + .../trigger-dialog/state.injectable.ts | 14 + .../view.scss} | 0 .../trigger-dialog/view.tsx | 151 +++ .../+workloads-daemonsets/daemonset-menu.tsx | 8 +- .../+workloads-daemonsets/store.injectable.ts | 2 + .../deployment-menu.tsx | 8 +- ...luster-frame-child-component.injectable.ts | 2 - .../+workloads-deployments/scale/dialog.tsx | 7 +- .../store.injectable.ts | 2 + .../+workloads-jobs/store.injectable.ts | 2 + .../__tests__/pod-tolerations.test.tsx | 6 - .../details/volumes/variant-helpers.tsx | 31 +- .../variants/__tests__/ceph-fs.test.tsx | 15 +- .../details/volumes/variants/ceph-fs.tsx | 38 +- .../details/volumes/variants/config-map.tsx | 31 +- .../variants/container-storage-interface.tsx | 122 ++- .../details/volumes/variants/flex-volume.tsx | 37 +- .../variants/persistent-volume-claim.tsx | 31 +- .../volumes/variants/rados-block-device.tsx | 40 +- .../details/volumes/variants/scale-io.tsx | 42 +- .../details/volumes/variants/secret.tsx | 31 +- .../details/volumes/variants/storage-os.tsx | 31 +- .../+workloads-pods/legacy-store.ts | 12 - .../+workloads-pods/pod-container-port.tsx | 40 +- .../+workloads-pods/pod-details-list.tsx | 30 +- .../+workloads-pods/pod-details-secrets.tsx | 27 +- .../+workloads-pods/store.injectable.ts | 2 + .../scale-dialog/dialog.tsx | 7 +- ...luster-frame-child-component.injectable.ts | 2 - .../store.injectable.ts | 2 + .../+workloads-statefulsets/scale/dialog.tsx | 7 +- ...luster-frame-child-component.injectable.ts | 2 - .../store.injectable.ts | 2 + .../confirm-dialog/confirm-dialog.tsx | 9 +- .../components/countdown/countdown.test.tsx | 4 +- .../is-current-context.tsx | 10 + .../delete-cluster-dialog/open.injectable.ts | 13 +- .../delete-cluster-dialog/state.injectable.ts | 2 + .../components/delete-cluster-dialog/view.tsx | 87 +- .../components/dialog/logs-dialog.tsx | 28 +- .../dock/__test__/dock-tabs.test.tsx | 220 ---- .../dock/dock-tab-store/dock-tab.store.ts | 8 +- src/renderer/components/dock/dock/store.ts | 39 +- .../edit-resource-model.injectable.tsx | 171 ++- .../components/dock/edit-resource/view.tsx | 54 +- .../__test__/log-resource-selector.test.tsx | 37 +- .../components/dock/logs/tab-store.ts | 8 +- .../terminal/create-terminal.injectable.ts | 2 + .../dock/terminal/send-command.injectable.ts | 114 +- .../components/dock/terminal/terminal.ts | 7 +- .../error-boundary/error-boundary.tsx | 19 +- .../__tests__/hotbar-remove-command.test.tsx | 20 +- .../components/input/drop-file-input.tsx | 23 +- .../components/input/search-input-url.tsx | 2 +- .../item-object-list/page-filters/list.tsx | 2 +- .../item-object-list/page-filters/store.ts | 2 +- .../components/kube-detail-params/index.ts | 18 - ...luster-frame-child-component.injectable.ts | 2 - .../kube-object-list-layout.tsx | 2 +- .../kube-object-meta/kube-object-meta.tsx | 6 +- ...luster-frame-child-component.injectable.ts | 2 - src/renderer/components/layout/sidebar.tsx | 2 +- src/renderer/components/layout/tab-layout.tsx | 32 +- .../notifications/notifications.tsx | 34 +- .../__tests__/render-delay.test.tsx | 4 +- .../components/select/select.test.tsx | 19 +- src/renderer/components/table/table.tsx | 2 +- .../test-utils/get-application-builder.tsx | 11 +- .../__snapshots__/cluster-frame.test.tsx.snap | 126 +++ .../cluster-frame/cluster-frame.test.tsx | 4 +- .../frames/cluster-frame/cluster-frame.tsx | 2 +- .../root-frame/init-root-frame.injectable.ts | 69 ++ .../init-root-frame.injectable.ts | 26 - .../init-root-frame/init-root-frame.ts | 75 -- src/renderer/getDiForUnitTesting.tsx | 105 +- .../add-sync-entries.injectable.tsx | 5 +- ...amespaces-forbidden-handler.injectable.tsx | 8 +- .../ipc/register-ipc-listeners.injectable.ts | 21 +- src/renderer/ipc/register-listeners.tsx | 27 - .../kube-watch-api.injectable.ts | 2 + src/renderer/kube-watch-api/kube-watch-api.ts | 19 +- ...s-window.global-override-for-injectable.ts | 9 + .../history.global-override-for-injectable.ts | 10 + src/renderer/navigation/index.ts | 23 - .../about-port-forwarding.injectable.ts | 30 - .../about-port-forwarding.injectable.tsx | 48 + src/renderer/port-forward/index.ts | 1 - ...notify-error-port-forwarding.injectable.ts | 30 - ...otify-error-port-forwarding.injectable.tsx | 47 + ...luster-frame-child-component.injectable.ts | 2 - .../port-forward/port-forward-dialog.tsx | 31 +- .../port-forward/port-forward-notify.tsx | 92 -- .../port-forward-store.injectable.ts | 2 + .../port-forward-store/port-forward-store.ts | 23 +- ...-protocol-add-route-handlers.injectable.ts | 2 + .../bind-protocol-add-route-handlers.tsx | 13 +- ...ens-protocol-router-renderer.injectable.ts | 15 +- .../lens-protocol-router-renderer.tsx | 17 +- .../extension-page-parameters.injectable.ts | 2 +- ...et-extension-page-parameters.injectable.ts | 2 +- src/renderer/routes/page-registration.ts | 2 +- .../search-store/search-store.test.ts | 15 +- ...eHelper.test.ts => storage-helper.test.ts} | 72 +- .../utils/create-storage-helper.injectable.ts | 23 + .../create-storage.injectable.ts | 42 +- .../utils/create-storage/create-storage.ts | 98 -- .../initialize-state.injectable.ts | 75 ++ .../utils/create-storage/state.injectable.ts | 14 + ...ve-delay.global-override-for-injectable.ts | 9 + .../create-storage/storages-are-ready.ts | 31 - src/renderer/utils/index.ts | 2 +- .../{storageHelper.ts => storage-helper.ts} | 72 +- yarn.lock | 14 +- 530 files changed, 11320 insertions(+), 5399 deletions(-) rename src/common/{fs/ensure-dir.global-override-for-injectable.ts => app-paths/path-to-npm-cli.global-override-for-injectable.ts} (52%) create mode 100644 src/common/app-paths/path-to-npm-cli.injectable.ts create mode 100644 src/common/fs/write-buffer-sync.injectable.ts create mode 100644 src/common/ipc/broadcast-message.global-override-for-injectable.ts create mode 100644 src/common/k8s-api/create-kube-api.injectable.ts delete mode 100644 src/common/k8s-api/endpoints/legacy-globals.ts create mode 100644 src/common/k8s-api/maybe-kube-api.injectable.ts create mode 100644 src/common/logger.global-override-for-injectable.ts delete mode 100644 src/common/utils/environment-variables.injectable.ts rename src/common/{fs/access-path.global-override-for-injectable.ts => utils/get-random-id.global-override-for-injectable.ts} (51%) rename src/{renderer => common}/vars/application-information.global-override-for-injectable.ts (79%) delete mode 100644 src/common/vars/is-test-env.injectable.ts create mode 100644 src/common/vars/lens-resources-dir.global-override-for-injectable.ts create mode 100644 src/common/vars/platform.global-override-for-injectable.ts create mode 100644 src/common/vars/process-arch.global-override-for-injectable.ts create mode 100644 src/common/vars/process-arch.injectable.ts create mode 100644 src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.global-override-for-injectable.ts create mode 100644 src/features/shell-sync/main/compute-shell-environment.global-override-for-injectable.ts create mode 100644 src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.global-override-for-injectable.ts delete mode 100644 src/main/app-paths/get-electron-app-path/get-electron-app-path.ts delete mode 100644 src/main/catalog-pusher.ts create mode 100644 src/main/catalog-sync-to-renderer/broadcaster.global-override-for-injectable.ts create mode 100644 src/main/catalog-sync-to-renderer/broadcaster.injectable.ts create mode 100644 src/main/catalog-sync-to-renderer/push-catalog-to-renderer.injectable.ts delete mode 100644 src/main/catalog/catalog-entity-registry.ts create mode 100644 src/main/electron-app/features/electron-dialog.global-override-for-injectable.ts create mode 100644 src/main/electron-app/features/native-theme.global-override-for-injectable.ts create mode 100644 src/main/helm/delete-helm-release.injectable.ts create mode 100644 src/main/helm/get-helm-release-history.injectable.ts create mode 100644 src/main/helm/get-helm-release-values.injectable.ts delete mode 100644 src/main/helm/helm-release-manager.ts create mode 100644 src/main/helm/install-helm-chart.injectable.ts create mode 100644 src/main/helm/list-helm-releases.injectable.ts create mode 100644 src/main/helm/rollback-helm-release.injectable.ts create mode 100644 src/main/lens-proxy/get-cluster-for-request.injectable.ts create mode 100644 src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts delete mode 100644 src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts delete mode 100644 src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts rename src/main/lens-proxy/proxy-functions/{shell-api-request => }/shell-request-authenticator/shell-request-authenticator.injectable.ts (100%) rename src/main/lens-proxy/proxy-functions/{shell-api-request => }/shell-request-authenticator/shell-request-authenticator.ts (89%) delete mode 100644 src/main/logger.ts create mode 100644 src/main/utils/command-line-arguments.global-override-for-injectable.ts create mode 100644 src/main/utils/get-port-from-stream.injectable.ts delete mode 100644 src/main/utils/get-port.ts delete mode 100644 src/main/vars/application-information.global-override-for-injectable.ts create mode 100644 src/main/vars/hardware-acceleration-should-be-disabled.global-override-for-injectable.ts create mode 100644 src/main/vars/hardware-acceleration-should-be-disabled.injectable.ts delete mode 100644 src/renderer/api/catalog-category-registry.ts create mode 100644 src/renderer/api/default-websocket-api-params.injectable.ts create mode 100644 src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts create mode 100644 src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts create mode 100644 src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts create mode 100644 src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts rename src/renderer/components/+config-resource-quotas/{add-quota-dialog.scss => add-dialog/view.scss} (100%) rename src/renderer/components/+config-resource-quotas/{add-quota-dialog.tsx => add-dialog/view.tsx} (63%) create mode 100644 src/renderer/components/+config-secrets/add-dialog/close.injectable.ts create mode 100644 src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts create mode 100644 src/renderer/components/+config-secrets/add-dialog/open.injectable.ts create mode 100644 src/renderer/components/+config-secrets/add-dialog/state.injectable.ts rename src/renderer/components/+config-secrets/{add-secret-dialog.scss => add-dialog/view.scss} (100%) rename src/renderer/components/+config-secrets/{add-secret-dialog.tsx => add-dialog/view.tsx} (68%) delete mode 100644 src/renderer/components/+custom-resources/legacy-store.ts delete mode 100644 src/renderer/components/+events/legacy-store.ts create mode 100644 src/renderer/components/+extensions/uninstall-extension.injectable.tsx delete mode 100644 src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts delete mode 100644 src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx delete mode 100644 src/renderer/components/+pod-security-policies/legacy-store.ts delete mode 100644 src/renderer/components/+storage-classes/legacy-store.ts delete mode 100644 src/renderer/components/+storage-volumes/legacy-store.ts create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog/close.injectable.ts create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog/edit-name-state.injectable.ts create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog/open.injectable.ts create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog/state.injectable.ts rename src/renderer/components/+user-management/+cluster-role-bindings/{dialog.scss => dialog/view.scss} (100%) rename src/renderer/components/+user-management/+cluster-role-bindings/{dialog.tsx => dialog/view.tsx} (52%) delete mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/legacy-store.ts delete mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx create mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog/close.injectable.ts create mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog/open.injectable.ts create mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog/state.injectable.ts rename src/renderer/components/+user-management/+cluster-roles/{add-dialog.scss => add-dialog/view.scss} (100%) create mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog/view.tsx delete mode 100644 src/renderer/components/+user-management/+cluster-roles/legacy-store.ts create mode 100644 src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts create mode 100644 src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts create mode 100644 src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts rename src/renderer/components/+user-management/+role-bindings/{dialog.scss => dialog/view.scss} (100%) rename src/renderer/components/+user-management/+role-bindings/{dialog.tsx => dialog/view.tsx} (60%) delete mode 100644 src/renderer/components/+user-management/+role-bindings/legacy-store.ts delete mode 100644 src/renderer/components/+user-management/+roles/add-dialog.tsx create mode 100644 src/renderer/components/+user-management/+roles/add-dialog/close.injectable.ts create mode 100644 src/renderer/components/+user-management/+roles/add-dialog/open.injectable.ts create mode 100644 src/renderer/components/+user-management/+roles/add-dialog/state.injectable.ts rename src/renderer/components/+user-management/+roles/{add-dialog.scss => add-dialog/view.scss} (100%) create mode 100644 src/renderer/components/+user-management/+roles/add-dialog/view.tsx delete mode 100644 src/renderer/components/+user-management/+roles/legacy-store.ts delete mode 100644 src/renderer/components/+user-management/+service-accounts/create-dialog.tsx create mode 100644 src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts create mode 100644 src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts create mode 100644 src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts rename src/renderer/components/+user-management/+service-accounts/{create-dialog.scss => create-dialog/view.scss} (100%) create mode 100644 src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx delete mode 100644 src/renderer/components/+user-management/+service-accounts/legacy-store.ts delete mode 100644 src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx create mode 100644 src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts create mode 100644 src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts create mode 100644 src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts rename src/renderer/components/+workloads-cronjobs/{cronjob-trigger-dialog.scss => trigger-dialog/view.scss} (100%) create mode 100644 src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx delete mode 100644 src/renderer/components/+workloads-pods/legacy-store.ts create mode 100644 src/renderer/components/delete-cluster-dialog/is-current-context.tsx delete mode 100644 src/renderer/components/dock/__test__/dock-tabs.test.tsx delete mode 100644 src/renderer/components/kube-detail-params/index.ts create mode 100644 src/renderer/frames/root-frame/init-root-frame.injectable.ts delete mode 100644 src/renderer/frames/root-frame/init-root-frame/init-root-frame.injectable.ts delete mode 100644 src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts delete mode 100644 src/renderer/ipc/register-listeners.tsx create mode 100644 src/renderer/navigation/focus-window.global-override-for-injectable.ts create mode 100644 src/renderer/navigation/history.global-override-for-injectable.ts delete mode 100644 src/renderer/navigation/index.ts delete mode 100644 src/renderer/port-forward/about-port-forwarding.injectable.ts create mode 100644 src/renderer/port-forward/about-port-forwarding.injectable.tsx delete mode 100644 src/renderer/port-forward/notify-error-port-forwarding.injectable.ts create mode 100644 src/renderer/port-forward/notify-error-port-forwarding.injectable.tsx delete mode 100644 src/renderer/port-forward/port-forward-notify.tsx rename src/renderer/utils/__tests__/{storageHelper.test.ts => storage-helper.test.ts} (67%) create mode 100644 src/renderer/utils/create-storage-helper.injectable.ts delete mode 100755 src/renderer/utils/create-storage/create-storage.ts create mode 100644 src/renderer/utils/create-storage/initialize-state.injectable.ts create mode 100644 src/renderer/utils/create-storage/state.injectable.ts create mode 100644 src/renderer/utils/create-storage/storage-save-delay.global-override-for-injectable.ts delete mode 100644 src/renderer/utils/create-storage/storages-are-ready.ts rename src/renderer/utils/{storageHelper.ts => storage-helper.ts} (67%) diff --git a/.eslintrc.js b/.eslintrc.js index 09a06987c4..8e32a4dac8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -283,6 +283,15 @@ module.exports = { ], message: "No importing from the extension api definitions in application code", }, + { + "group": [ + "**/extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api", + "**/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications", + "**/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api", + "**/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api", + ], + message: "No importing the legacy global functions in non-ExtensionApi code", + }, ], }], }, diff --git a/package.json b/package.json index 5f2b5f1d77..1b11f47926 100644 --- a/package.json +++ b/package.json @@ -273,7 +273,6 @@ "mobx-observable-history": "^2.0.3", "mobx-react": "^7.6.0", "mobx-utils": "^6.0.4", - "mock-fs": "^5.2.0", "moment": "^2.29.4", "moment-timezone": "^0.5.40", "node-fetch": "^3.3.0", @@ -408,6 +407,7 @@ "jest-environment-jsdom": "^28.1.3", "jest-mock-extended": "^2.0.9", "make-plural": "^6.2.2", + "memfs": "^3.4.12", "memorystream": "^0.3.1", "mini-css-extract-plugin": "^2.7.2", "mock-http": "^1.1.0", diff --git a/src/common/__tests__/cluster-store.test.ts b/src/common/__tests__/cluster-store.test.ts index 28357ae5ff..9dd1a29a5b 100644 --- a/src/common/__tests__/cluster-store.test.ts +++ b/src/common/__tests__/cluster-store.test.ts @@ -3,34 +3,33 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import fs from "fs"; -import mockFs from "mock-fs"; -import path from "path"; -import fse from "fs-extra"; import type { ClusterStore } from "../cluster-store/cluster-store"; -import { Console } from "console"; -import { stdout, stderr } from "process"; -import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import type { GetCustomKubeConfigFilePath } from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import getCustomKubeConfigFilePathInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import type { CreateCluster } from "../cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import assert from "assert"; import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable"; import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable"; import normalizedPlatformInjectable from "../vars/normalized-platform.injectable"; -import fsInjectable from "../fs/fs.injectable"; import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; +import type { WriteJsonSync } from "../fs/write-json-sync.injectable"; +import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; +import type { ReadFileSync } from "../fs/read-file-sync.injectable"; +import readFileSyncInjectable from "../fs/read-file-sync.injectable"; +import { readFileSync } from "fs"; +import type { WriteFileSync } from "../fs/write-file-sync.injectable"; +import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; +import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable"; +import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable"; -console = new Console(stdout, stderr); - -const testDataIcon = fs.readFileSync( - "test-data/cluster-store-migration-icon.png", -); +// NOTE: this is intended to read the actual file system +const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png"); const clusterServerUrl = "https://localhost"; const kubeconfig = ` apiVersion: v1 @@ -56,75 +55,41 @@ users: token: kubeconfig-user-q4lm4:xxxyyyy `; -const embed = (directoryName: string, contents: any): string => { - fse.ensureDirSync(path.dirname(directoryName)); - fse.writeFileSync(directoryName, contents, { - encoding: "utf-8", - mode: 0o600, - }); - - return directoryName; -}; - -jest.mock("electron", () => ({ - ipcMain: { - handle: jest.fn(), - on: jest.fn(), - removeAllListeners: jest.fn(), - off: jest.fn(), - send: jest.fn(), - }, -})); - describe("cluster-store", () => { - let mainDi: DiContainer; + let di: DiContainer; let clusterStore: ClusterStore; let createCluster: CreateCluster; + let writeJsonSync: WriteJsonSync; + let writeFileSync: WriteFileSync; + let writeBufferSync: WriteBufferSync; + let readFileSync: ReadFileSync; + let getCustomKubeConfigFilePath: GetCustomKubeConfigFilePath; + let writeFileSyncAndReturnPath: (filePath: string, contents: string) => string; beforeEach(async () => { - mainDi = getDiForUnitTesting({ doGeneralOverrides: true }); + di = getDiForUnitTesting({ doGeneralOverrides: true }); - mockFs(); - - mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - mainDi.override(directoryForTempInjectable, () => "some-temp-directory"); - mainDi.override(kubectlBinaryNameInjectable, () => "kubectl"); - mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); - mainDi.override(normalizedPlatformInjectable, () => "darwin"); - - mainDi.permitSideEffects(getConfigurationFileModelInjectable); - mainDi.unoverride(getConfigurationFileModelInjectable); - - mainDi.permitSideEffects(fsInjectable); - }); - - afterEach(() => { - mockFs.restore(); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); + di.override(directoryForTempInjectable, () => "/some-temp-directory"); + di.override(kubectlBinaryNameInjectable, () => "kubectl"); + di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); + di.override(normalizedPlatformInjectable, () => "darwin"); + createCluster = di.inject(createClusterInjectionToken); + getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + writeJsonSync = di.inject(writeJsonSyncInjectable); + writeFileSync = di.inject(writeFileSyncInjectable); + writeBufferSync = di.inject(writeBufferSyncInjectable); + readFileSync = di.inject(readFileSyncInjectable); + writeFileSyncAndReturnPath = (filePath, contents) => (writeFileSync(filePath, contents), filePath); }); describe("empty config", () => { - let getCustomKubeConfigDirectory: (directoryName: string) => string; - beforeEach(async () => { - getCustomKubeConfigDirectory = mainDi.inject(getCustomKubeConfigDirectoryInjectable); - - mockFs({ - "some-directory-for-user-data": { - "lens-cluster-store.json": JSON.stringify({}), - }, - }); - - createCluster = mainDi.inject(createClusterInjectionToken); - - clusterStore = mainDi.inject(clusterStoreInjectable); - + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {}); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); - afterEach(() => { - mockFs.restore(); - }); - describe("with foo cluster added", () => { beforeEach(() => { const cluster = createCluster({ @@ -135,8 +100,8 @@ describe("cluster-store", () => { icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", clusterName: "minikube", }, - kubeConfigPath: embed( - getCustomKubeConfigDirectory("foo"), + kubeConfigPath: writeFileSyncAndReturnPath( + getCustomKubeConfigFilePath("foo"), kubeconfig, ), }, { @@ -169,8 +134,8 @@ describe("cluster-store", () => { preferences: { clusterName: "prod", }, - kubeConfigPath: embed( - getCustomKubeConfigDirectory("prod"), + kubeConfigPath: writeFileSyncAndReturnPath( + getCustomKubeConfigFilePath("prod"), kubeconfig, ), }); @@ -180,8 +145,8 @@ describe("cluster-store", () => { preferences: { clusterName: "dev", }, - kubeConfigPath: embed( - getCustomKubeConfigDirectory("dev"), + kubeConfigPath: writeFileSyncAndReturnPath( + getCustomKubeConfigFilePath("dev"), kubeconfig, ), }); @@ -193,61 +158,49 @@ describe("cluster-store", () => { }); it("check if cluster's kubeconfig file saved", () => { - const file = embed(getCustomKubeConfigDirectory("boo"), "kubeconfig"); + const file = writeFileSyncAndReturnPath(getCustomKubeConfigFilePath("boo"), "kubeconfig"); - expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig"); + expect(readFileSync(file)).toBe("kubeconfig"); }); }); }); describe("config with existing clusters", () => { beforeEach(() => { - mockFs({ - "temp-kube-config": kubeconfig, - "some-directory-for-user-data": { - "lens-cluster-store.json": JSON.stringify({ - __internal__: { - migrations: { - version: "99.99.99", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfigPath: "./temp-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "default", - }, - { - id: "cluster2", - kubeConfigPath: "./temp-kube-config", - contextName: "foo2", - preferences: { terminalCWD: "/foo2" }, - }, - { - id: "cluster3", - kubeConfigPath: "./temp-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "foo", - ownerRef: "foo", - }, - ], - }), + writeFileSync("/temp-kube-config", kubeconfig); + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { + __internal__: { + migrations: { + version: "99.99.99", + }, }, + clusters: [ + { + id: "cluster1", + kubeConfigPath: "/temp-kube-config", + contextName: "foo", + preferences: { terminalCWD: "/foo" }, + workspace: "default", + }, + { + id: "cluster2", + kubeConfigPath: "/temp-kube-config", + contextName: "foo2", + preferences: { terminalCWD: "/foo2" }, + }, + { + id: "cluster3", + kubeConfigPath: "/temp-kube-config", + contextName: "foo", + preferences: { terminalCWD: "/foo" }, + workspace: "foo", + ownerRef: "foo", + }, + ], }); - - createCluster = mainDi.inject(createClusterInjectionToken); - - clusterStore = mainDi.inject(clusterStoreInjectable); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); - - afterEach(() => { - mockFs.restore(); - }); - it("allows to retrieve a cluster", () => { const storedCluster = clusterStore.getById("cluster1"); @@ -271,66 +224,35 @@ describe("cluster-store", () => { describe("config with invalid cluster kubeconfig", () => { beforeEach(() => { - const invalidKubeconfig = ` -apiVersion: v1 -clusters: -- cluster: - server: https://localhost - name: test2 -contexts: -- context: - cluster: test - user: test - name: test -current-context: test -kind: Config -preferences: {} -users: -- name: test - user: - token: kubeconfig-user-q4lm4:xxxyyyy -`; - - mockFs({ - "invalid-kube-config": invalidKubeconfig, - "valid-kube-config": kubeconfig, - "some-directory-for-user-data": { - "lens-cluster-store.json": JSON.stringify({ - __internal__: { - migrations: { - version: "99.99.99", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfigPath: "./invalid-kube-config", - contextName: "test", - preferences: { terminalCWD: "/foo" }, - workspace: "foo", - }, - { - id: "cluster2", - kubeConfigPath: "./valid-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "default", - }, - ], - }), + writeFileSync("/invalid-kube-config", invalidKubeconfig); + writeFileSync("/valid-kube-config", kubeconfig); + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { + __internal__: { + migrations: { + version: "99.99.99", + }, }, + clusters: [ + { + id: "cluster1", + kubeConfigPath: "/invalid-kube-config", + contextName: "test", + preferences: { terminalCWD: "/foo" }, + workspace: "foo", + }, + { + id: "cluster2", + kubeConfigPath: "/valid-kube-config", + contextName: "foo", + preferences: { terminalCWD: "/foo" }, + workspace: "default", + }, + ], }); - - createCluster = mainDi.inject(createClusterInjectionToken); - - clusterStore = mainDi.inject(clusterStoreInjectable); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); - afterEach(() => { - mockFs.restore(); - }); - it("does not enable clusters with invalid kubeconfig", () => { const storedClusters = clusterStore.clustersList; @@ -340,56 +262,69 @@ users: describe("pre 3.6.0-beta.1 config with an existing cluster", () => { beforeEach(() => { - mockFs({ - "some-directory-for-user-data": { - "lens-cluster-store.json": JSON.stringify({ - __internal__: { - migrations: { - version: "3.5.0", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfig: minimalValidKubeConfig, - contextName: "cluster", - preferences: { - icon: "store://icon_path", - }, - }, - ], - }), - icon_path: testDataIcon, + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { + __internal__: { + migrations: { + version: "3.5.0", + }, }, + clusters: [ + { + id: "cluster1", + kubeConfig: minimalValidKubeConfig, + contextName: "cluster", + preferences: { + icon: "store://icon_path", + }, + }, + ], }); + writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon); - mainDi.override(storeMigrationVersionInjectable, () => "3.6.0"); + di.override(storeMigrationVersionInjectable, () => "3.6.0"); - createCluster = mainDi.inject(createClusterInjectionToken); - - clusterStore = mainDi.inject(clusterStoreInjectable); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); - afterEach(() => { - mockFs.restore(); - }); - it("migrates to modern format with kubeconfig in a file", async () => { const config = clusterStore.clustersList[0].kubeConfigPath; - expect(fs.readFileSync(config, "utf8")).toBe(minimalValidKubeConfig); + expect(readFileSync(config)).toBe(minimalValidKubeConfig); }); it("migrates to modern format with icon not in file", async () => { - const { icon } = clusterStore.clustersList[0].preferences; - - assert(icon); - expect(icon.startsWith("data:;base64,")).toBe(true); + expect(clusterStore.clustersList[0].preferences.icon).toMatch(/data:;base64,/); }); }); }); +const invalidKubeconfig = JSON.stringify({ + apiVersion: "v1", + clusters: [{ + cluster: { + server: "https://localhost", + }, + name: "test2", + }], + contexts: [{ + context: { + cluster: "test", + user: "test", + }, + name: "test", + }], + "current-context": "test", + kind: "Config", + preferences: {}, + users: [{ + user: { + token: "kubeconfig-user-q4lm4:xxxyyyy", + }, + name: "test", + }], +}); + const minimalValidKubeConfig = JSON.stringify({ apiVersion: "v1", clusters: [ diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts index a0a618d4df..ac8cecc1d2 100644 --- a/src/common/__tests__/hotbar-store.test.ts +++ b/src/common/__tests__/hotbar-store.test.ts @@ -4,10 +4,8 @@ */ import { anyObject } from "jest-mock-extended"; -import mockFs from "mock-fs"; import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import hotbarStoreInjectable from "../hotbars/store.injectable"; import type { HotbarStore } from "../hotbars/store"; @@ -19,7 +17,7 @@ import loggerInjectable from "../logger.injectable"; import type { Logger } from "../logger"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import fsInjectable from "../fs/fs.injectable"; +import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { return { @@ -47,8 +45,6 @@ describe("HotbarStore", () => { beforeEach(async () => { di = getDiForUnitTesting({ doGeneralOverrides: true }); - di.unoverride(hotbarStoreInjectable); - testCluster = getMockCatalogEntity({ apiVersion: "v1", kind: "Cluster", @@ -101,7 +97,7 @@ describe("HotbarStore", () => { di.override(loggerInjectable, () => loggerMock); - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); @@ -112,20 +108,10 @@ describe("HotbarStore", () => { awsCluster, catalogCatalogEntity, ])); - - di.permitSideEffects(fsInjectable); - di.permitSideEffects(getConfigurationFileModelInjectable); - di.unoverride(getConfigurationFileModelInjectable); - }); - - afterEach(() => { - mockFs.restore(); }); describe("given no previous data in store, running all migrations", () => { beforeEach(() => { - mockFs(); - hotbarStore = di.inject(hotbarStoreInjectable); hotbarStore.load(); @@ -276,66 +262,64 @@ describe("HotbarStore", () => { describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { beforeEach(() => { - mockFs({ - "some-directory-for-user-data": { - "lens-hotbar-store.json": JSON.stringify({ - __internal__: { - migrations: { - version: "5.0.0-beta.3", - }, - }, - hotbars: [ - { - id: "3caac17f-aec2-4723-9694-ad204465d935", - name: "myhotbar", - items: [ - { - entity: { - uid: "some-aws-id", - }, - }, - { - entity: { - uid: "55b42c3c7ba3b04193416cda405269a5", - }, - }, - { - entity: { - uid: "176fd331968660832f62283219d7eb6e", - }, - }, - { - entity: { - uid: "61c4fb45528840ebad1badc25da41d14", - name: "user1-context", - source: "local", - }, - }, - { - entity: { - uid: "27d6f99fe9e7548a6e306760bfe19969", - name: "foo2", - source: "local", - }, - }, - null, - { - entity: { - uid: "c0b20040646849bb4dcf773e43a0bf27", - name: "multinode-demo", - source: "local", - }, - }, - null, - null, - null, - null, - null, - ], - }, - ], - }), + const writeJsonSync = di.inject(writeJsonSyncInjectable); + + writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", { + __internal__: { + migrations: { + version: "5.0.0-beta.3", + }, }, + hotbars: [ + { + id: "3caac17f-aec2-4723-9694-ad204465d935", + name: "myhotbar", + items: [ + { + entity: { + uid: "some-aws-id", + }, + }, + { + entity: { + uid: "55b42c3c7ba3b04193416cda405269a5", + }, + }, + { + entity: { + uid: "176fd331968660832f62283219d7eb6e", + }, + }, + { + entity: { + uid: "61c4fb45528840ebad1badc25da41d14", + name: "user1-context", + source: "local", + }, + }, + { + entity: { + uid: "27d6f99fe9e7548a6e306760bfe19969", + name: "foo2", + source: "local", + }, + }, + null, + { + entity: { + uid: "c0b20040646849bb4dcf773e43a0bf27", + name: "multinode-demo", + source: "local", + }, + }, + null, + null, + null, + null, + null, + ], + }, + ], }); di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10"); diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts index 8ae213d412..7071fc5c17 100644 --- a/src/common/__tests__/user-store.test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -2,28 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ - -import mockFs from "mock-fs"; - -jest.mock("electron", () => ({ - app: { - getVersion: () => "99.99.99", - getName: () => "lens", - setName: jest.fn(), - setPath: jest.fn(), - getPath: () => "tmp", - getLocale: () => "en", - setLoginItemSettings: jest.fn(), - }, - ipcMain: { - on: jest.fn(), - handle: jest.fn(), - }, -})); - import type { UserStore } from "../user-store"; -import { Console } from "console"; -import { stdout, stderr } from "process"; import userStoreInjectable from "../user-store/user-store.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; @@ -31,13 +10,11 @@ import type { ClusterStoreModel } from "../cluster-store/cluster-store"; import { defaultThemeId } from "../vars"; import writeFileInjectable from "../fs/write-file.injectable"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import releaseChannelInjectable from "../vars/release-channel.injectable"; import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable"; -import fsInjectable from "../fs/fs.injectable"; - -console = new Console(stdout, stderr); +import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; +import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; describe("user store tests", () => { let userStore: UserStore; @@ -46,14 +23,8 @@ describe("user store tests", () => { beforeEach(async () => { di = getDiForUnitTesting({ doGeneralOverrides: true }); - mockFs(); - di.override(writeFileInjectable, () => () => Promise.resolve()); - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - - di.permitSideEffects(getConfigurationFileModelInjectable); - di.unoverride(getConfigurationFileModelInjectable); - di.permitSideEffects(fsInjectable); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); di.override(releaseChannelInjectable, () => ({ get: () => "latest" as const, @@ -64,13 +35,12 @@ describe("user store tests", () => { userStore = di.inject(userStoreInjectable); }); - afterEach(() => { - mockFs.restore(); - }); - describe("for an empty config", () => { beforeEach(() => { - mockFs({ "some-directory-for-user-data": { "lens-user-store.json": "{}", "kube_config": "{}" }}); + const writeJsonSync = di.inject(writeJsonSyncInjectable); + + writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {}); + writeJsonSync("/some-directory-for-user-data/kube_config", {}); userStore.load(); }); @@ -94,40 +64,38 @@ describe("user store tests", () => { describe("migrations", () => { beforeEach(() => { - mockFs({ - "some-directory-for-user-data": { - "lens-user-store.json": JSON.stringify({ - preferences: { colorTheme: "light" }, - }), - "lens-cluster-store.json": JSON.stringify({ - clusters: [ - { - id: "foobar", - kubeConfigPath: "some-directory-for-user-data/extension_data/foo/bar", - }, - { - id: "barfoo", - kubeConfigPath: "some/other/path", - }, - ], - } as ClusterStoreModel), - "extension_data": {}, - }, - "some": { - "other": { - "path": "is file", - }, - }, + const writeJsonSync = di.inject(writeJsonSyncInjectable); + const writeFileSync = di.inject(writeFileSyncInjectable); + + writeJsonSync("/some-directory-for-user-data/lens-user-store.json", { + preferences: { colorTheme: "light" }, }); + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { + clusters: [ + { + id: "foobar", + kubeConfigPath: "/some-directory-for-user-data/extension_data/foo/bar", + }, + { + id: "barfoo", + kubeConfigPath: "/some/other/path", + }, + ], + } as ClusterStoreModel); + + writeJsonSync("/some-directory-for-user-data/extension_data", {}); + + writeFileSync("/some/other/path", "is file"); + di.override(storeMigrationVersionInjectable, () => "10.0.0"); userStore.load(); }); it("skips clusters for adding to kube-sync with files under extension_data/", () => { - expect(userStore.syncKubeconfigEntries.has("some-directory-for-user-data/extension_data/foo/bar")).toBe(false); - expect(userStore.syncKubeconfigEntries.has("some/other/path")).toBe(true); + expect(userStore.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false); + expect(userStore.syncKubeconfigEntries.has("/some/other/path")).toBe(true); }); it("allows access to the colorTheme preference", () => { diff --git a/src/common/app-paths/app-paths.test.ts b/src/common/app-paths/app-paths.test.ts index b26a5aca5d..6e645f96fa 100644 --- a/src/common/app-paths/app-paths.test.ts +++ b/src/common/app-paths/app-paths.test.ts @@ -19,23 +19,23 @@ describe("app-paths", () => { builder = getApplicationBuilder(); const defaultAppPathsStub: AppPaths = { - currentApp: "some-current-app", - appData: "some-app-data", - cache: "some-cache", - crashDumps: "some-crash-dumps", - desktop: "some-desktop", - documents: "some-documents", - downloads: "some-downloads", - exe: "some-exe", - home: "some-home-path", - logs: "some-logs", - module: "some-module", - music: "some-music", - pictures: "some-pictures", - recent: "some-recent", - temp: "some-temp", - videos: "some-videos", - userData: "some-irrelevant-user-data", + currentApp: "/some-current-app", + appData: "/some-app-data", + cache: "/some-cache", + crashDumps: "/some-crash-dumps", + desktop: "/some-desktop", + documents: "/some-documents", + downloads: "/some-downloads", + exe: "/some-exe", + home: "/some-home-path", + logs: "/some-logs", + module: "/some-module", + music: "/some-music", + pictures: "/some-pictures", + recent: "/some-recent", + temp: "/some-temp", + videos: "/some-videos", + userData: "/some-irrelevant-user-data", }; builder.beforeApplicationStart((mainDi) => { @@ -71,23 +71,23 @@ describe("app-paths", () => { const actual = windowDi.inject(appPathsInjectable); expect(actual).toEqual({ - currentApp: "some-current-app", - appData: "some-app-data", - cache: "some-cache", - crashDumps: "some-crash-dumps", - desktop: "some-desktop", - documents: "some-documents", - downloads: "some-downloads", - exe: "some-exe", - home: "some-home-path", - logs: "some-logs", - module: "some-module", - music: "some-music", - pictures: "some-pictures", - recent: "some-recent", - temp: "some-temp", - videos: "some-videos", - userData: "some-app-data/some-product-name", + currentApp: "/some-current-app", + appData: "/some-app-data", + cache: "/some-cache", + crashDumps: "/some-crash-dumps", + desktop: "/some-desktop", + documents: "/some-documents", + downloads: "/some-downloads", + exe: "/some-exe", + home: "/some-home-path", + logs: "/some-logs", + module: "/some-module", + music: "/some-music", + pictures: "/some-pictures", + recent: "/some-recent", + temp: "/some-temp", + videos: "/some-videos", + userData: "/some-app-data/some-product-name", }); }); @@ -95,23 +95,23 @@ describe("app-paths", () => { const actual = mainDi.inject(appPathsInjectable); expect(actual).toEqual({ - currentApp: "some-current-app", - appData: "some-app-data", - cache: "some-cache", - crashDumps: "some-crash-dumps", - desktop: "some-desktop", - documents: "some-documents", - downloads: "some-downloads", - exe: "some-exe", - home: "some-home-path", - logs: "some-logs", - module: "some-module", - music: "some-music", - pictures: "some-pictures", - recent: "some-recent", - temp: "some-temp", - videos: "some-videos", - userData: "some-app-data/some-product-name", + currentApp: "/some-current-app", + appData: "/some-app-data", + cache: "/some-cache", + crashDumps: "/some-crash-dumps", + desktop: "/some-desktop", + documents: "/some-documents", + downloads: "/some-downloads", + exe: "/some-exe", + home: "/some-home-path", + logs: "/some-logs", + module: "/some-module", + music: "/some-music", + pictures: "/some-pictures", + recent: "/some-recent", + temp: "/some-temp", + videos: "/some-videos", + userData: "/some-app-data/some-product-name", }); }); }); @@ -123,7 +123,7 @@ describe("app-paths", () => { builder.beforeApplicationStart((mainDi) => { mainDi.override( directoryForIntegrationTestingInjectable, - () => "some-integration-testing-app-data", + () => "/some-integration-testing-app-data", ); }); @@ -136,8 +136,8 @@ describe("app-paths", () => { const { appData, userData } = windowDi.inject(appPathsInjectable); expect({ appData, userData }).toEqual({ - appData: "some-integration-testing-app-data", - userData: `some-integration-testing-app-data/some-product-name`, + appData: "/some-integration-testing-app-data", + userData: "/some-integration-testing-app-data/some-product-name", }); }); @@ -145,8 +145,8 @@ describe("app-paths", () => { const { appData, userData } = windowDi.inject(appPathsInjectable); expect({ appData, userData }).toEqual({ - appData: "some-integration-testing-app-data", - userData: "some-integration-testing-app-data/some-product-name", + appData: "/some-integration-testing-app-data", + userData: "/some-integration-testing-app-data/some-product-name", }); }); }); diff --git a/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts b/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts index 5d53e506bd..0580d372f1 100644 --- a/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts +++ b/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts @@ -6,15 +6,17 @@ import { getInjectable } from "@ogre-tools/injectable"; import directoryForKubeConfigsInjectable from "../directory-for-kube-configs/directory-for-kube-configs.injectable"; import joinPathsInjectable from "../../path/join-paths.injectable"; -const getCustomKubeConfigDirectoryInjectable = getInjectable({ +export type GetCustomKubeConfigFilePath = (fileName: string) => string; + +const getCustomKubeConfigFilePathInjectable = getInjectable({ id: "get-custom-kube-config-directory", - instantiate: (di) => { + instantiate: (di): GetCustomKubeConfigFilePath => { const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable); const joinPaths = di.inject(joinPathsInjectable); - return (directoryName: string) => joinPaths(directoryForKubeConfigs, directoryName); + return (fileName) => joinPaths(directoryForKubeConfigs, fileName); }, }); -export default getCustomKubeConfigDirectoryInjectable; +export default getCustomKubeConfigFilePathInjectable; diff --git a/src/common/fs/ensure-dir.global-override-for-injectable.ts b/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts similarity index 52% rename from src/common/fs/ensure-dir.global-override-for-injectable.ts rename to src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts index 4dd098b163..db4734994d 100644 --- a/src/common/fs/ensure-dir.global-override-for-injectable.ts +++ b/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts @@ -4,8 +4,6 @@ */ import { getGlobalOverride } from "../test-utils/get-global-override"; -import ensureDirInjectable from "./ensure-dir.injectable"; +import pathToNpmCliInjectable from "./path-to-npm-cli.injectable"; -export default getGlobalOverride(ensureDirInjectable, () => async () => { - throw new Error("tried to ensure directory without override"); -}); +export default getGlobalOverride(pathToNpmCliInjectable, () => "/some/npm/cli/path"); diff --git a/src/common/app-paths/path-to-npm-cli.injectable.ts b/src/common/app-paths/path-to-npm-cli.injectable.ts new file mode 100644 index 0000000000..1c32b9ed6a --- /dev/null +++ b/src/common/app-paths/path-to-npm-cli.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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"; + +const pathToNpmCliInjectable = getInjectable({ + id: "path-to-npm-cli", + instantiate: () => __non_webpack_require__.resolve("npm"), + causesSideEffects: true, +}); + +export default pathToNpmCliInjectable; diff --git a/src/common/catalog-entities/general.ts b/src/common/catalog-entities/general.ts index 2d3b404ee0..e81e59cc16 100644 --- a/src/common/catalog-entities/general.ts +++ b/src/common/catalog-entities/general.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { navigate } from "../../renderer/navigation"; import type { CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog"; +import type { CatalogEntityActionContext } from "../catalog/catalog-entity"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; interface GeneralEntitySpec extends CatalogEntitySpec { @@ -19,8 +19,8 @@ export class GeneralEntity extends CatalogEntity { if (app) { + const di = getLegacyGlobalDiForExtensionApi(); + const getClusterById = di.inject(getClusterByIdInjectable); + await getClusterById(this.getId())?.activate(); } else { await requestClusterActivation(this.getId(), false); @@ -87,6 +88,9 @@ export class KubernetesCluster< async disconnect(): Promise { if (app) { + const di = getLegacyGlobalDiForExtensionApi(); + const getClusterById = di.inject(getClusterByIdInjectable); + getClusterById(this.getId())?.disconnect(); } else { await requestClusterDisconnection(this.getId(), false); diff --git a/src/common/fs/write-buffer-sync.injectable.ts b/src/common/fs/write-buffer-sync.injectable.ts new file mode 100644 index 0000000000..d4d253ae66 --- /dev/null +++ b/src/common/fs/write-buffer-sync.injectable.ts @@ -0,0 +1,29 @@ +/** + * 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 getDirnameOfPathInjectable from "../path/get-dirname.injectable"; +import fsInjectable from "./fs.injectable"; + +export type WriteBufferSync = (filePath: string, contents: Buffer) => void; + +const writeBufferSyncInjectable = getInjectable({ + id: "write-buffer-sync", + instantiate: (di): WriteBufferSync => { + const { + writeFileSync, + ensureDirSync, + } = di.inject(fsInjectable); + const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); + + return (filePath, contents) => { + ensureDirSync(getDirnameOfPath(filePath), { + mode: 0o755, + }); + writeFileSync(filePath, contents); + }; + }, +}); + +export default writeBufferSyncInjectable; diff --git a/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts b/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts index fba8939880..5f51460a2b 100644 --- a/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts +++ b/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts @@ -10,6 +10,34 @@ import getConfigurationFileModelInjectable from "./get-configuration-file-model. import type Config from "conf"; import readJsonSyncInjectable from "../fs/read-json-sync.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; +import { get, set } from "lodash"; +import semver from "semver"; + +const MIGRATION_KEY = `__internal__.migrations.version`; + +const _isVersionInRangeFormat = (version: string) => { + return semver.clean(version) === null; +}; + +const _shouldPerformMigration = (candidateVersion: string, previousMigratedVersion: string, versionToMigrate: string) => { + if (_isVersionInRangeFormat(candidateVersion)) { + if (previousMigratedVersion !== "0.0.0" && semver.satisfies(previousMigratedVersion, candidateVersion)) { + return false; + } + + return semver.satisfies(versionToMigrate, candidateVersion); + } + + if (semver.lte(candidateVersion, previousMigratedVersion)) { + return false; + } + + if (semver.gt(candidateVersion, versionToMigrate)) { + return false; + } + + return true; +}; export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { const readJsonSync = di.inject(readJsonSyncInjectable); @@ -18,6 +46,7 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { return (options) => { assert(options.cwd, "Missing options.cwd"); assert(options.configName, "Missing options.configName"); + assert(options.projectVersion, "Missing options.projectVersion"); const configFilePath = path.posix.join(options.cwd, `${options.configName}.json`); let store: object = {}; @@ -28,11 +57,12 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { // ignore } - return { + const config = { get store() { return store; }, path: configFilePath, + get: (key: string) => get(store, key), set: (key: string, value: unknown) => { let currentState: object; @@ -49,5 +79,35 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { store = readJsonSync(configFilePath); }, } as Partial as Config; + + // Migrate + { + const migrations = options.migrations ?? []; + const versionToMigrate = options.projectVersion; + let previousMigratedVersion = get(store, MIGRATION_KEY) || "0.0.0"; + const newerVersions = Object.entries(migrations) + .filter(([candidateVersion]) => _shouldPerformMigration(candidateVersion, previousMigratedVersion, versionToMigrate)); + + let storeBackup = { ...store }; + + for (const [version, migration] of newerVersions) { + try { + migration(config); + set(store, MIGRATION_KEY, version); + previousMigratedVersion = version; + storeBackup = { ...store }; + } + catch (error) { + store = storeBackup; + throw new Error(`Something went wrong during the migration! Changes applied to the store until this failed migration will be restored. ${error}`); + } + } + + if (_isVersionInRangeFormat(previousMigratedVersion) || !semver.eq(previousMigratedVersion, versionToMigrate)) { + set(store, MIGRATION_KEY, versionToMigrate); + } + } + + return config; }; }); diff --git a/src/common/ipc/broadcast-message.global-override-for-injectable.ts b/src/common/ipc/broadcast-message.global-override-for-injectable.ts new file mode 100644 index 0000000000..e455b30cdc --- /dev/null +++ b/src/common/ipc/broadcast-message.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; +import broadcastMessageInjectable from "./broadcast-message.injectable"; + +export default getGlobalOverrideForFunction(broadcastMessageInjectable); diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index a714af73a4..f11227d17e 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -9,13 +9,13 @@ import { ipcMain, ipcRenderer, webContents } from "electron"; import { toJS } from "../utils/toJS"; -import logger from "../../main/logger"; import type { ClusterFrameInfo } from "../cluster-frames"; import { clusterFrameMap } from "../cluster-frames"; import type { Disposer } from "../utils"; import ipcMainInjectable from "../../main/utils/channel/ipc-main/ipc-main.injectable"; import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable"; +import loggerInjectable from "../logger.injectable"; export const broadcastMainChannel = "ipc:broadcast-main"; @@ -42,6 +42,9 @@ export async function broadcastMessage(channel: string, ...args: any[]): Promise return; } + const di = getLegacyGlobalDiForExtensionApi(); + const logger = di.inject(loggerInjectable); + ipcMain.listeners(channel).forEach((func) => func({ processId: undefined, frameId: undefined, sender: undefined, senderFrame: undefined, }, ...args)); diff --git a/src/common/k8s-api/__tests__/api-manager.test.ts b/src/common/k8s-api/__tests__/api-manager.test.ts index e99ac62018..b83f52c3f0 100644 --- a/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/src/common/k8s-api/__tests__/api-manager.test.ts @@ -11,11 +11,13 @@ import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import loggerInjectable from "../../logger.injectable"; import type { ApiManager } from "../api-manager"; import apiManagerInjectable from "../api-manager/manager.injectable"; import { KubeApi } from "../kube-api"; import { KubeObject } from "../kube-object"; import { KubeObjectStore } from "../kube-object.store"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; class TestApi extends KubeApi { protected async checkPreferredVersion() { @@ -56,6 +58,9 @@ describe("ApiManager", () => { const apiBase = "apis/v1/foo"; const fallbackApiBase = "/apis/extensions/v1beta1/foo"; const kubeApi = new TestApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }, { objectConstructor: KubeObject, apiBase, kind: "foo", @@ -64,6 +69,7 @@ describe("ApiManager", () => { }); const kubeStore = new TestStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, kubeApi); apiManager.registerApi(apiBase, kubeApi); diff --git a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts b/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts index e2caef39fa..94c6e0cd9c 100644 --- a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts +++ b/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts @@ -2,9 +2,9 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { KubeJsonApi } from "../kube-json-api"; import type { ApiManager } from "../api-manager"; -import { Ingress, IngressApi } from "../endpoints"; +import type { IngressApi } from "../endpoints"; +import { Ingress } from "../endpoints"; import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; import apiManagerInjectable from "../api-manager/manager.injectable"; import type { Fetch } from "../../fetch/fetch.injectable"; @@ -20,14 +20,17 @@ import directoryForUserDataInjectable from "../../app-paths/directory-for-user-d import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; +import type { DiContainer } from "@ogre-tools/injectable"; +import ingressApiInjectable from "../endpoints/ingress.api.injectable"; describe("KubeApi", () => { - let request: KubeJsonApi; + let di: DiContainer; let registerApiSpy: jest.SpiedFunction; let fetchMock: AsyncFnMock; beforeEach(async () => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); + di = getDiForUnitTesting({ doGeneralOverrides: true }); fetchMock = asyncFn(); di.override(fetchInjectable, () => fetchMock); @@ -37,6 +40,7 @@ describe("KubeApi", () => { di.override(storesAndApisCanBeCreatedInjectable, () => true); const createCluster = di.inject(createClusterInjectable); + const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", @@ -46,12 +50,11 @@ describe("KubeApi", () => { clusterServerUrl: "https://localhost:8080", })); - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - - request = createKubeJsonApi({ + di.override(apiKubeInjectable, () => createKubeJsonApi({ serverAddress: `http://127.0.0.1:9999`, apiBase: "/api-kube", - }); + })); + registerApiSpy = jest.spyOn(di.inject(apiManagerInjectable), "registerApi"); const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable); @@ -64,13 +67,7 @@ describe("KubeApi", () => { let getCall: Promise; beforeEach(async () => { - ingressApi = new IngressApi({ - request, - objectConstructor: Ingress, - apiBase: "/apis/networking.k8s.io/v1/ingresses", - fallbackApiBases: ["/apis/extensions/v1beta1/ingresses"], - checkPreferredVersion: true, - }); + ingressApi = di.inject(ingressApiInjectable); getCall = ingressApi.get({ name: "foo", namespace: "default", diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index a4f9fd5b21..7bce8a3b7c 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -4,9 +4,10 @@ */ import type { KubeApiWatchCallback } from "../kube-api"; import { KubeApi } from "../kube-api"; -import type { KubeJsonApi, KubeJsonApiData } from "../kube-json-api"; +import type { KubeJsonApiData } from "../kube-json-api"; import { PassThrough } from "stream"; -import { Deployment, DeploymentApi, NamespaceApi, Pod, PodApi } from "../endpoints"; +import type { DeploymentApi, NamespaceApi } from "../endpoints"; +import { Deployment, Pod, PodApi } from "../endpoints"; import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; import type { Fetch } from "../../fetch/fetch.injectable"; import fetchInjectable from "../../fetch/fetch.injectable"; @@ -26,6 +27,15 @@ import directoryForUserDataInjectable from "../../app-paths/directory-for-user-d import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; +import type { DiContainer } from "@ogre-tools/injectable"; +import deploymentApiInjectable from "../endpoints/deployment.api.injectable"; +import podApiInjectable from "../endpoints/pod.api.injectable"; +import namespaceApiInjectable from "../endpoints/namespace.api.injectable"; + +// NOTE: this is fine because we are testing something that only exported +// eslint-disable-next-line no-restricted-imports +import { PodsApi } from "../../../extensions/common-api/k8s-api"; describe("createKubeApiForRemoteCluster", () => { let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster; @@ -78,7 +88,7 @@ describe("createKubeApiForRemoteCluster", () => { user: { token: "daa", }, - }, Pod, PodApi); + }, Pod, PodsApi); }); it("uses the constructor", () => { @@ -131,17 +141,21 @@ describe("createKubeApiForRemoteCluster", () => { }); describe("KubeApi", () => { - let request: KubeJsonApi; let fetchMock: AsyncFnMock; + let di: DiContainer; beforeEach(async () => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); + di = getDiForUnitTesting({ doGeneralOverrides: true }); di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + fetchMock = asyncFn(); + di.override(fetchInjectable, () => fetchMock); + const createCluster = di.inject(createClusterInjectable); + const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", @@ -151,15 +165,10 @@ describe("KubeApi", () => { clusterServerUrl: "https://localhost:8080", })); - fetchMock = asyncFn(); - di.override(fetchInjectable, () => fetchMock); - - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - - request = createKubeJsonApi({ + di.override(apiKubeInjectable, () => createKubeJsonApi({ serverAddress: `http://127.0.0.1:9999`, apiBase: "/api-kube", - }); + })); const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable); @@ -170,9 +179,7 @@ describe("KubeApi", () => { let api: DeploymentApi; beforeEach(() => { - api = new DeploymentApi({ - request, - }); + api = di.inject(deploymentApiInjectable); }); describe("when patching a resource without providing a strategy", () => { @@ -337,9 +344,7 @@ describe("KubeApi", () => { let api: PodApi; beforeEach(() => { - api = new PodApi({ - request, - }); + api = di.inject(podApiInjectable); }); describe("when deleting by just name", () => { @@ -455,9 +460,7 @@ describe("KubeApi", () => { let api: NamespaceApi; beforeEach(() => { - api = new NamespaceApi({ - request, - }); + api = di.inject(namespaceApiInjectable); }); describe("when deleting by just name", () => { @@ -544,9 +547,7 @@ describe("KubeApi", () => { let stream: PassThrough; beforeEach(() => { - api = new PodApi({ - request, - }); + api = di.inject(podApiInjectable); stream = new PassThrough(); }); @@ -873,9 +874,7 @@ describe("KubeApi", () => { let api: PodApi; beforeEach(() => { - api = new PodApi({ - request, - }); + api = di.inject(podApiInjectable); }); describe("when creating a pod", () => { @@ -988,9 +987,7 @@ describe("KubeApi", () => { let api: PodApi; beforeEach(() => { - api = new PodApi({ - request, - }); + api = di.inject(podApiInjectable); }); describe("when updating a pod", () => { @@ -1100,9 +1097,7 @@ describe("KubeApi", () => { let api: PodApi; beforeEach(() => { - api = new PodApi({ - request, - }); + api = di.inject(podApiInjectable); }); describe("when listing pods with no descriptor", () => { diff --git a/src/common/k8s-api/__tests__/kube-object.store.test.ts b/src/common/k8s-api/__tests__/kube-object.store.test.ts index afe755a6ba..424cffba23 100644 --- a/src/common/k8s-api/__tests__/kube-object.store.test.ts +++ b/src/common/k8s-api/__tests__/kube-object.store.test.ts @@ -3,6 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { noop } from "../../utils"; import type { KubeApi } from "../kube-api"; import { KubeObject } from "../kube-object"; import type { KubeObjectStoreLoadingParams } from "../kube-object.store"; @@ -18,6 +19,13 @@ class FakeKubeObjectStore extends KubeObjectStore { isGlobalWatchEnabled: () => true, isLoadingAll: () => true, }, + logger: { + debug: noop, + error: noop, + info: noop, + silly: noop, + warn: noop, + }, }, api as KubeApi); } diff --git a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts b/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts index a90a52456d..dc3a3fef69 100644 --- a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts +++ b/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts @@ -3,11 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../logger.injectable"; import { apiKubePrefix } from "../vars"; import isDevelopmentInjectable from "../vars/is-development.injectable"; import apiBaseInjectable from "./api-base.injectable"; +import type { KubeApiConstructor } from "./create-kube-api-for-remote-cluster.injectable"; import createKubeJsonApiInjectable from "./create-kube-json-api.injectable"; -import type { KubeApiOptions } from "./kube-api"; import { KubeApi } from "./kube-api"; import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "./kube-object"; @@ -21,12 +22,12 @@ export interface CreateKubeApiForCluster { , Data extends KubeJsonApiDataFor>( cluster: CreateKubeApiForLocalClusterConfig, kubeClass: KubeObjectConstructor, - apiClass: new (apiOpts: KubeApiOptions) => Api + apiClass: KubeApiConstructor, ): Api; >( cluster: CreateKubeApiForLocalClusterConfig, kubeClass: KubeObjectConstructor, - apiClass?: new (apiOpts: KubeApiOptions) => KubeApi + apiClass?: KubeApiConstructor>, ): KubeApi; } @@ -36,27 +37,42 @@ const createKubeApiForClusterInjectable = getInjectable({ const apiBase = di.inject(apiBaseInjectable); const isDevelopment = di.inject(isDevelopmentInjectable); const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); + const logger = di.inject(loggerInjectable); return ( cluster: CreateKubeApiForLocalClusterConfig, kubeClass: KubeObjectConstructor>, - apiClass = KubeApi, - ) => ( - new apiClass({ - objectConstructor: kubeClass, - request: createKubeJsonApi( - { - serverAddress: apiBase.config.serverAddress, - apiBase: apiKubePrefix, - debug: isDevelopment, - }, { - headers: { - "Host": `${cluster.metadata.uid}.lens.app:${new URL(apiBase.config.serverAddress).port}`, - }, + apiClass?: KubeApiConstructor>, + ) => { + const request = createKubeJsonApi( + { + serverAddress: apiBase.config.serverAddress, + apiBase: apiKubePrefix, + debug: isDevelopment, + }, { + headers: { + "Host": `${cluster.metadata.uid}.lens.app:${new URL(apiBase.config.serverAddress).port}`, }, - ), - }) - ); + }); + + if (apiClass) { + return new apiClass({ + objectConstructor: kubeClass, + request, + }); + } + + return new KubeApi( + { + logger, + maybeKubeApi: undefined, + }, + { + objectConstructor: kubeClass, + request, + }, + ); + }; }, }); diff --git a/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts b/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts index d11b7ae34b..8b39387d1d 100644 --- a/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts +++ b/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { AgentOptions } from "https"; import { Agent } from "https"; import type { RequestInit } from "node-fetch"; +import loggerInjectable from "../logger.injectable"; import isDevelopmentInjectable from "../vars/is-development.injectable"; import createKubeJsonApiInjectable from "./create-kube-json-api.injectable"; import type { KubeApiOptions } from "./kube-api"; @@ -32,16 +33,18 @@ export interface CreateKubeApiForRemoteClusterConfig { agent?: Agent; } +export type KubeApiConstructor> = new (apiOpts: KubeApiOptions) => Api; + export interface CreateKubeApiForRemoteCluster { , Data extends KubeJsonApiDataFor>( config: CreateKubeApiForRemoteClusterConfig, kubeClass: KubeObjectConstructor, - apiClass: new (apiOpts: KubeApiOptions) => Api, + apiClass: KubeApiConstructor, ): Api; >( config: CreateKubeApiForRemoteClusterConfig, kubeClass: KubeObjectConstructor, - apiClass?: new (apiOpts: KubeApiOptions) => KubeApi, + apiClass?: KubeApiConstructor>, ): KubeApi; } @@ -50,8 +53,13 @@ const createKubeApiForRemoteClusterInjectable = getInjectable({ instantiate: (di): CreateKubeApiForRemoteCluster => { const isDevelopment = di.inject(isDevelopmentInjectable); const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); + const logger = di.inject(loggerInjectable); - return (config: CreateKubeApiForRemoteClusterConfig, kubeClass: KubeObjectConstructor>, apiClass = KubeApi) => { + return ( + config: CreateKubeApiForRemoteClusterConfig, + kubeClass: KubeObjectConstructor>, + apiClass?: KubeApiConstructor>, + ) => { const reqInit: RequestInit = {}; const agentOptions: AgentOptions = {}; @@ -93,10 +101,23 @@ const createKubeApiForRemoteClusterInjectable = getInjectable({ } : {}), }, reqInit); - return new apiClass({ - objectConstructor: kubeClass, - request, - }); + if (apiClass) { + return new apiClass({ + objectConstructor: kubeClass, + request, + }); + } + + return new KubeApi( + { + logger, + maybeKubeApi: undefined, + }, + { + objectConstructor: kubeClass, + request, + }, + ); }; }, }); diff --git a/src/common/k8s-api/create-kube-api.injectable.ts b/src/common/k8s-api/create-kube-api.injectable.ts new file mode 100644 index 0000000000..cce470b132 --- /dev/null +++ b/src/common/k8s-api/create-kube-api.injectable.ts @@ -0,0 +1,26 @@ +/** + * 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 loggerInjectable from "../logger.injectable"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "./kube-api"; +import maybeKubeApiInjectable from "./maybe-kube-api.injectable"; + +export interface CreateKubeApi { + (ctor: new (deps: KubeApiDependencies, opts: DerivedKubeApiOptions) => Api, opts?: DerivedKubeApiOptions): Api; +} + +const createKubeApiInjectable = getInjectable({ + id: "create-kube-api", + instantiate: (di): CreateKubeApi => { + const deps: KubeApiDependencies = { + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }; + + return (ctor, opts) => new ctor(deps, opts ?? {}); + }, +}); + +export default createKubeApiInjectable; diff --git a/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts b/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts index 38ba76d661..4965a86edc 100644 --- a/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts +++ b/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ClusterRoleBindingApi } from "./cluster-role-binding.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const clusterRoleBindingApiInjectable = getInjectable({ id: "cluster-role-binding-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleBindingApi is only accessible in certain environments"); - return new ClusterRoleBindingApi(); + return new ClusterRoleBindingApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/cluster-role-binding.api.ts b/src/common/k8s-api/endpoints/cluster-role-binding.api.ts index d9bc61af49..827a3ce95d 100644 --- a/src/common/k8s-api/endpoints/cluster-role-binding.api.ts +++ b/src/common/k8s-api/endpoints/cluster-role-binding.api.ts @@ -2,7 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; @@ -47,8 +47,8 @@ export class ClusterRoleBinding extends KubeObject< } export class ClusterRoleBindingApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: ClusterRoleBinding, }); diff --git a/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts b/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts index 9b56bc2559..b22e198d1e 100644 --- a/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts +++ b/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ClusterRoleApi } from "./cluster-role.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const clusterRoleApiInjectable = getInjectable({ id: "cluster-role-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleApi is only available in certain environments"); - return new ClusterRoleApi(); + return new ClusterRoleApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/cluster-role.api.ts b/src/common/k8s-api/endpoints/cluster-role.api.ts index f99168b395..e55f934df9 100644 --- a/src/common/k8s-api/endpoints/cluster-role.api.ts +++ b/src/common/k8s-api/endpoints/cluster-role.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; @@ -40,8 +40,8 @@ export class ClusterRole extends KubeObject< } export class ClusterRoleApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: ClusterRole, }); diff --git a/src/common/k8s-api/endpoints/cluster.api.injectable.ts b/src/common/k8s-api/endpoints/cluster.api.injectable.ts index 8d51e630ee..494ef9d94b 100644 --- a/src/common/k8s-api/endpoints/cluster.api.injectable.ts +++ b/src/common/k8s-api/endpoints/cluster.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ClusterApi } from "./cluster.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const clusterApiInjectable = getInjectable({ id: "cluster-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterApi is only available in certain environments"); - return new ClusterApi(); + return new ClusterApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/cluster.api.ts b/src/common/k8s-api/endpoints/cluster.api.ts index aae4c239ad..92f42f1e4e 100644 --- a/src/common/k8s-api/endpoints/cluster.api.ts +++ b/src/common/k8s-api/endpoints/cluster.api.ts @@ -4,7 +4,7 @@ */ import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export class ClusterApi extends KubeApi { @@ -18,9 +18,9 @@ export class ClusterApi extends KubeApi { */ static namespaced = true; - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Cluster, }); } diff --git a/src/common/k8s-api/endpoints/component-status.api.injectable.ts b/src/common/k8s-api/endpoints/component-status.api.injectable.ts index 395c825401..01bb16e6b4 100644 --- a/src/common/k8s-api/endpoints/component-status.api.injectable.ts +++ b/src/common/k8s-api/endpoints/component-status.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ComponentStatusApi } from "./component-status.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +import loggerInjectable from "../../logger.injectable"; const componentStatusApiInjectable = getInjectable({ id: "component-status-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "componentStatusApi is only available in certain environments"); - return new ComponentStatusApi(); + return new ComponentStatusApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/component-status.api.ts b/src/common/k8s-api/endpoints/component-status.api.ts index 5335207b71..a35fc92c44 100644 --- a/src/common/k8s-api/endpoints/component-status.api.ts +++ b/src/common/k8s-api/endpoints/component-status.api.ts @@ -4,7 +4,7 @@ */ import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export interface ComponentStatusCondition { @@ -28,8 +28,8 @@ export class ComponentStatus extends KubeObject { } export class ComponentStatusApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: ComponentStatus, }); diff --git a/src/common/k8s-api/endpoints/config-map.api.injectable.ts b/src/common/k8s-api/endpoints/config-map.api.injectable.ts index d643849f70..e41cc23111 100644 --- a/src/common/k8s-api/endpoints/config-map.api.injectable.ts +++ b/src/common/k8s-api/endpoints/config-map.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ConfigMapApi } from "./config-map.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const configMapApiInjectable = getInjectable({ id: "config-map-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "configMapApi is only available in certain environments"); - return new ConfigMapApi(); + return new ConfigMapApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/config-map.api.ts b/src/common/k8s-api/endpoints/config-map.api.ts index 922274d32a..a2860246b1 100644 --- a/src/common/k8s-api/endpoints/config-map.api.ts +++ b/src/common/k8s-api/endpoints/config-map.api.ts @@ -6,7 +6,7 @@ import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; import type { KubeJsonApiData } from "../kube-json-api"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import { autoBind } from "../../utils"; @@ -44,8 +44,8 @@ export class ConfigMap extends KubeObject< } export class ConfigMapApi extends KubeApi { - constructor(opts?: DerivedKubeApiOptions) { - super({ + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { objectConstructor: ConfigMap, ...opts ?? {}, }); diff --git a/src/common/k8s-api/endpoints/cron-job.api.injectable.ts b/src/common/k8s-api/endpoints/cron-job.api.injectable.ts index e2230ee2db..2831a726aa 100644 --- a/src/common/k8s-api/endpoints/cron-job.api.injectable.ts +++ b/src/common/k8s-api/endpoints/cron-job.api.injectable.ts @@ -7,6 +7,8 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { CronJobApi } from "./cron-job.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const cronJobApiInjectable = getInjectable({ id: "cron-job-api", @@ -14,6 +16,9 @@ const cronJobApiInjectable = getInjectable({ assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "cronJobApi is only available in certain environments"); return new CronJobApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }, { checkPreferredVersion: true, }); }, diff --git a/src/common/k8s-api/endpoints/cron-job.api.ts b/src/common/k8s-api/endpoints/cron-job.api.ts index 7dad6a857d..2ccb8c910c 100644 --- a/src/common/k8s-api/endpoints/cron-job.api.ts +++ b/src/common/k8s-api/endpoints/cron-job.api.ts @@ -7,13 +7,13 @@ import moment from "moment"; import type { NamespaceScopedMetadata, ObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; import { formatDuration } from "../../utils/formatDuration"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { JobTemplateSpec } from "./types/job-template-spec"; export class CronJobApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions) { + super(deps, { ...opts, objectConstructor: CronJob, }); diff --git a/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts b/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts index c441b07c3e..cf8a6560a0 100644 --- a/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts +++ b/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { CustomResourceDefinitionApi } from "./custom-resource-definition.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +import loggerInjectable from "../../logger.injectable"; const customResourceDefinitionApiInjectable = getInjectable({ id: "custom-resource-definition-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "customResourceDefinitionApi is only available in certain environments"); - return new CustomResourceDefinitionApi(); + return new CustomResourceDefinitionApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/custom-resource-definition.api.ts b/src/common/k8s-api/endpoints/custom-resource-definition.api.ts index 9235e2c992..b438f06b3a 100644 --- a/src/common/k8s-api/endpoints/custom-resource-definition.api.ts +++ b/src/common/k8s-api/endpoints/custom-resource-definition.api.ts @@ -8,7 +8,7 @@ import customResourcesRouteInjectable from "../../front-end-routing/routes/clust import { buildURL } from "../../utils/buildUrl"; import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { JSONSchemaProps } from "./types/json-schema-props"; @@ -234,8 +234,8 @@ export class CustomResourceDefinition extends KubeObject< } export class CustomResourceDefinitionApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: CustomResourceDefinition, checkPreferredVersion: true, ...opts, diff --git a/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts b/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts index a1e1d65084..8a904519b6 100644 --- a/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts +++ b/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { DaemonSetApi } from "./daemon-set.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const daemonSetApiInjectable = getInjectable({ id: "daemon-set-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "daemonSetApi is only available in certain environements"); - return new DaemonSetApi(); + return new DaemonSetApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/daemon-set.api.ts b/src/common/k8s-api/endpoints/daemon-set.api.ts index 00f9cfb2d9..6661e2ff43 100644 --- a/src/common/k8s-api/endpoints/daemon-set.api.ts +++ b/src/common/k8s-api/endpoints/daemon-set.api.ts @@ -5,7 +5,7 @@ import moment from "moment"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; @@ -83,9 +83,9 @@ export class DaemonSet extends KubeObject< } export class DaemonSetApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: DaemonSet, }); } diff --git a/src/common/k8s-api/endpoints/deployment.api.injectable.ts b/src/common/k8s-api/endpoints/deployment.api.injectable.ts index 26c98c39e6..47236b1a6f 100644 --- a/src/common/k8s-api/endpoints/deployment.api.injectable.ts +++ b/src/common/k8s-api/endpoints/deployment.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { DeploymentApi } from "./deployment.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const deploymentApiInjectable = getInjectable({ id: "deployment-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "deploymentApi is only available in certain environments"); - return new DeploymentApi(); + return new DeploymentApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/deployment.api.ts b/src/common/k8s-api/endpoints/deployment.api.ts index 318ea8563f..31bc55ae76 100644 --- a/src/common/k8s-api/endpoints/deployment.api.ts +++ b/src/common/k8s-api/endpoints/deployment.api.ts @@ -5,7 +5,7 @@ import moment from "moment"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { PodSpec } from "./pod.api"; import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; @@ -13,10 +13,10 @@ import { KubeObject } from "../kube-object"; import { hasTypedProperty, isNumber, isObject } from "../../utils"; export class DeploymentApi extends KubeApi { - constructor(opts?: DerivedKubeApiOptions) { - super({ - objectConstructor: Deployment, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { ...opts ?? {}, + objectConstructor: Deployment, }); } diff --git a/src/common/k8s-api/endpoints/endpoint.api.injectable.ts b/src/common/k8s-api/endpoints/endpoint.api.injectable.ts index 79a57cd3e7..6288dc2448 100644 --- a/src/common/k8s-api/endpoints/endpoint.api.injectable.ts +++ b/src/common/k8s-api/endpoints/endpoint.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { EndpointsApi } from "./endpoint.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const endpointsApiInjectable = getInjectable({ id: "endpoints-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "endpointsApi is only available in certain environments"); - return new EndpointsApi(); + return new EndpointsApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/endpoint.api.ts b/src/common/k8s-api/endpoints/endpoint.api.ts index dd61bc2211..c60377a887 100644 --- a/src/common/k8s-api/endpoints/endpoint.api.ts +++ b/src/common/k8s-api/endpoints/endpoint.api.ts @@ -6,7 +6,7 @@ import { autoBind } from "../../utils"; import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata, ObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; @@ -112,8 +112,8 @@ export class Endpoints extends KubeObject< } export class EndpointsApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: Endpoints, ...opts, }); diff --git a/src/common/k8s-api/endpoints/events.api.injectable.ts b/src/common/k8s-api/endpoints/events.api.injectable.ts index 83f0697d31..a64afb877e 100644 --- a/src/common/k8s-api/endpoints/events.api.injectable.ts +++ b/src/common/k8s-api/endpoints/events.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { KubeEventApi } from "./events.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const kubeEventApiInjectable = getInjectable({ id: "kube-event-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "kubeEventApi is only available in certain environments"); - return new KubeEventApi(); + return new KubeEventApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/events.api.ts b/src/common/k8s-api/endpoints/events.api.ts index 3a03c0ffbf..aca77712c8 100644 --- a/src/common/k8s-api/endpoints/events.api.ts +++ b/src/common/k8s-api/endpoints/events.api.ts @@ -7,7 +7,7 @@ import moment from "moment"; import type { KubeObjectMetadata, KubeObjectScope, ObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; import { formatDuration } from "../../utils/formatDuration"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; @@ -132,8 +132,8 @@ export class KubeEvent extends KubeObject { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: KubeEvent, ...opts, }); diff --git a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts index 7449053661..d891e45e0f 100644 --- a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts +++ b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { HorizontalPodAutoscalerApi } from "./horizontal-pod-autoscaler.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const horizontalPodAutoscalerApiInjectable = getInjectable({ id: "horizontal-pod-autoscaler-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "horizontalPodAutoscalerApi is only available in certain environments"); - return new HorizontalPodAutoscalerApi(); + return new HorizontalPodAutoscalerApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts index 70857fd881..71863d65ad 100644 --- a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts +++ b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts @@ -5,7 +5,7 @@ import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { OptionVarient } from "../../utils"; @@ -202,8 +202,8 @@ export class HorizontalPodAutoscaler extends KubeObject< } export class HorizontalPodAutoscalerApi extends KubeApi { - constructor(opts?: DerivedKubeApiOptions) { - super({ + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { objectConstructor: HorizontalPodAutoscaler, ...opts ?? {}, }); diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts index 33ec108a31..9d3fa94815 100644 --- a/src/common/k8s-api/endpoints/index.ts +++ b/src/common/k8s-api/endpoints/index.ts @@ -43,5 +43,4 @@ export * from "./service.api"; export * from "./service-account.api"; export * from "./stateful-set.api"; export * from "./storage-class.api"; -export * from "./legacy-globals"; export * from "./types"; diff --git a/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts b/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts index 33f8d69485..d7d9bf32b9 100644 --- a/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts +++ b/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts @@ -5,12 +5,15 @@ import { getInjectable } from "@ogre-tools/injectable"; import { IngressClassApi } from "./ingress-class.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const ingressClassApiInjectable = getInjectable({ id: "ingress-class-api", - instantiate: () => { - return new IngressClassApi(); - }, + instantiate: (di) => new IngressClassApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }), injectionToken: kubeApiInjectionToken, }); diff --git a/src/common/k8s-api/endpoints/ingress-class.api.ts b/src/common/k8s-api/endpoints/ingress-class.api.ts index 680fb7f686..5950192179 100644 --- a/src/common/k8s-api/endpoints/ingress-class.api.ts +++ b/src/common/k8s-api/endpoints/ingress-class.api.ts @@ -5,12 +5,12 @@ import type { KubeObjectMetadata, KubeObjectScope } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { ResourceDescriptor } from "../kube-api"; +import type { KubeApiDependencies, ResourceDescriptor } from "../kube-api"; import { KubeApi } from "../kube-api"; export class IngressClassApi extends KubeApi { - constructor() { - super({ + constructor(dependencies: KubeApiDependencies) { + super(dependencies, { objectConstructor: IngressClass, checkPreferredVersion: true, fallbackApiBases: ["/apis/extensions/v1beta1/ingressclasses"], diff --git a/src/common/k8s-api/endpoints/ingress.api.injectable.ts b/src/common/k8s-api/endpoints/ingress.api.injectable.ts index 350f8824e4..ec25b199b0 100644 --- a/src/common/k8s-api/endpoints/ingress.api.injectable.ts +++ b/src/common/k8s-api/endpoints/ingress.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { IngressApi } from "./ingress.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const ingressApiInjectable = getInjectable({ id: "ingress-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "ingressApi is only available in certain environments"); - return new IngressApi(); + return new IngressApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/ingress.api.ts b/src/common/k8s-api/endpoints/ingress.api.ts index d223530013..d35cd77c0b 100644 --- a/src/common/k8s-api/endpoints/ingress.api.ts +++ b/src/common/k8s-api/endpoints/ingress.api.ts @@ -6,14 +6,14 @@ import type { NamespaceScopedMetadata, TypedLocalObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; import { hasTypedProperty, isString, iter } from "../../utils"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { RequireExactlyOne } from "type-fest"; export class IngressApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Ingress, // Add fallback for Kubernetes <1.19 checkPreferredVersion: true, diff --git a/src/common/k8s-api/endpoints/job.api.injectable.ts b/src/common/k8s-api/endpoints/job.api.injectable.ts index fc25c8c61f..923b96a2eb 100644 --- a/src/common/k8s-api/endpoints/job.api.injectable.ts +++ b/src/common/k8s-api/endpoints/job.api.injectable.ts @@ -7,6 +7,8 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { JobApi } from "./job.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const jobApiInjectable = getInjectable({ id: "job-api", @@ -14,6 +16,9 @@ const jobApiInjectable = getInjectable({ assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "jobApi is only available in certain environments"); return new JobApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }, { checkPreferredVersion: true, }); }, diff --git a/src/common/k8s-api/endpoints/job.api.ts b/src/common/k8s-api/endpoints/job.api.ts index 66f4ef3768..ef47419e80 100644 --- a/src/common/k8s-api/endpoints/job.api.ts +++ b/src/common/k8s-api/endpoints/job.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { PodSpec } from "./pod.api"; import type { Container } from "./types/container"; @@ -95,9 +95,9 @@ export class Job extends KubeObject< } export class JobApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Job, }); } diff --git a/src/common/k8s-api/endpoints/lease.api.injectable.ts b/src/common/k8s-api/endpoints/lease.api.injectable.ts index 41bd5e3935..f5d8e73b55 100644 --- a/src/common/k8s-api/endpoints/lease.api.injectable.ts +++ b/src/common/k8s-api/endpoints/lease.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { LeaseApi } from "./lease.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const leaseApiInjectable = getInjectable({ id: "lease-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "leaseApi is only available in certain environments"); - return new LeaseApi(); + return new LeaseApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/lease.api.ts b/src/common/k8s-api/endpoints/lease.api.ts index 22cfb778e4..25f636dcfb 100644 --- a/src/common/k8s-api/endpoints/lease.api.ts +++ b/src/common/k8s-api/endpoints/lease.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; @@ -47,9 +47,9 @@ export class Lease extends KubeObject< } export class LeaseApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Lease, }); } diff --git a/src/common/k8s-api/endpoints/legacy-globals.ts b/src/common/k8s-api/endpoints/legacy-globals.ts deleted file mode 100644 index b4355e720a..0000000000 --- a/src/common/k8s-api/endpoints/legacy-globals.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import configMapApiInjectable from "./config-map.api.injectable"; -import cronJobApiInjectable from "./cron-job.api.injectable"; -import jobApiInjectable from "./job.api.injectable"; -import networkPolicyApiInjectable from "./network-policy.api.injectable"; -import nodeApiInjectable from "./node.api.injectable"; -import persistentVolumeClaimApiInjectable from "./persistent-volume-claim.api.injectable"; -import podApiInjectable from "./pod.api.injectable"; -import resourceQuotaApiInjectable from "./resource-quota.api.injectable"; -import roleApiInjectable from "./role.api.injectable"; -import secretApiInjectable from "./secret.api.injectable"; -import serviceApiInjectable from "./service.api.injectable"; -import storageClassApiInjectable from "./storage-class.api.injectable"; - -/** - * @deprecated use `di.inject(roleApiInjectable)` instead - */ -export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable); - -/** - * @deprecated use `di.inject(podApiInjectable)` instead - */ -export const podApi = asLegacyGlobalForExtensionApi(podApiInjectable); - -/** - * @deprecated use `di.inject(cronJobApiInjectable)` instead - */ -export const cronJobApi = asLegacyGlobalForExtensionApi(cronJobApiInjectable); - -/** - * @deprecated use `di.inject(jobApiInjectable)` instead - */ -export const jobApi = asLegacyGlobalForExtensionApi(jobApiInjectable); - -/** - * @deprecated use `di.inject(configMapApiInjectable)` instead - */ -export const configMapApi = asLegacyGlobalForExtensionApi(configMapApiInjectable); - -/** - * @deprecated use `di.inject(networkPolicyApiInjectable)` instead - */ -export const networkPolicyApi = asLegacyGlobalForExtensionApi(networkPolicyApiInjectable); - -/** - * @deprecated use `di.inject(nodeApiInjectable)` instead - */ -export const nodeApi = asLegacyGlobalForExtensionApi(nodeApiInjectable); - -/** - * @deprecated use `di.inject(persistentVolumeClaimApiInjectable)` instead - */ -export const persistentVolumeClaimApi = asLegacyGlobalForExtensionApi(persistentVolumeClaimApiInjectable); - -/** - * @deprecated use `di.inject(resourceQuotaApiInjectable)` instead - */ -export const resourceQuotaApi = asLegacyGlobalForExtensionApi(resourceQuotaApiInjectable); - -/** - * @deprecated use `di.inject(secretApiInjectable)` instead - */ -export const secretApi = asLegacyGlobalForExtensionApi(secretApiInjectable); - -/** - * @deprecated use `di.inject(serviceApiInjectable)` instead - */ -export const serviceApi = asLegacyGlobalForExtensionApi(serviceApiInjectable); - -/** - * @deprecated use `di.inject(storageClassApiInjectable)` instead - */ -export const storageClassApi = asLegacyGlobalForExtensionApi(storageClassApiInjectable); diff --git a/src/common/k8s-api/endpoints/limit-range.api.injectable.ts b/src/common/k8s-api/endpoints/limit-range.api.injectable.ts index c2898c998a..7f50e2df92 100644 --- a/src/common/k8s-api/endpoints/limit-range.api.injectable.ts +++ b/src/common/k8s-api/endpoints/limit-range.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { LimitRangeApi } from "./limit-range.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const limitRangeApiInjectable = getInjectable({ id: "limit-range-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "limitRangeApi is only available in certain environments"); - return new LimitRangeApi(); + return new LimitRangeApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/limit-range.api.ts b/src/common/k8s-api/endpoints/limit-range.api.ts index 5ed0974edc..eec533993c 100644 --- a/src/common/k8s-api/endpoints/limit-range.api.ts +++ b/src/common/k8s-api/endpoints/limit-range.api.ts @@ -5,7 +5,7 @@ import type { NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export enum LimitType { @@ -62,8 +62,8 @@ export class LimitRange extends KubeObject< } export class LimitRangeApi extends KubeApi { - constructor(opts?: DerivedKubeApiOptions) { - super({ + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { objectConstructor: LimitRange, ...opts ?? {}, }); diff --git a/src/common/k8s-api/endpoints/namespace.api.injectable.ts b/src/common/k8s-api/endpoints/namespace.api.injectable.ts index c55c6b9521..fe722433c1 100644 --- a/src/common/k8s-api/endpoints/namespace.api.injectable.ts +++ b/src/common/k8s-api/endpoints/namespace.api.injectable.ts @@ -7,6 +7,8 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { NamespaceApi } from "./namespace.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const namespaceApiInjectable = getInjectable({ id: "namespace-api", @@ -14,7 +16,10 @@ const namespaceApiInjectable = getInjectable({ instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "namespaceApi is only available in certain environments"); - return new NamespaceApi(); + return new NamespaceApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/namespace.api.ts b/src/common/k8s-api/endpoints/namespace.api.ts index 774d91cf61..3de24d3df9 100644 --- a/src/common/k8s-api/endpoints/namespace.api.ts +++ b/src/common/k8s-api/endpoints/namespace.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { ClusterScopedMetadata, KubeObjectStatus } from "../kube-object"; import { KubeObject } from "../kube-object"; @@ -36,9 +36,9 @@ export class Namespace extends KubeObject< } export class NamespaceApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Namespace, }); } diff --git a/src/common/k8s-api/endpoints/network-policy.api.injectable.ts b/src/common/k8s-api/endpoints/network-policy.api.injectable.ts index 58a316605a..66c6a95732 100644 --- a/src/common/k8s-api/endpoints/network-policy.api.injectable.ts +++ b/src/common/k8s-api/endpoints/network-policy.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { NetworkPolicyApi } from "./network-policy.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const networkPolicyApiInjectable = getInjectable({ id: "network-policy-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "networkPolicyApi is only available in certain environments"); - return new NetworkPolicyApi(); + return new NetworkPolicyApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/network-policy.api.ts b/src/common/k8s-api/endpoints/network-policy.api.ts index 518874f7aa..78520d5539 100644 --- a/src/common/k8s-api/endpoints/network-policy.api.ts +++ b/src/common/k8s-api/endpoints/network-policy.api.ts @@ -5,7 +5,7 @@ import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export interface IPolicyIpBlock { @@ -124,8 +124,8 @@ export class NetworkPolicy extends KubeObject< } export class NetworkPolicyApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: NetworkPolicy, ...opts, }); diff --git a/src/common/k8s-api/endpoints/node.api.injectable.ts b/src/common/k8s-api/endpoints/node.api.injectable.ts index 4a0faf3270..b780d69ee7 100644 --- a/src/common/k8s-api/endpoints/node.api.injectable.ts +++ b/src/common/k8s-api/endpoints/node.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { NodeApi } from "./node.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const nodeApiInjectable = getInjectable({ id: "node-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "nodeApi is only available in certain environments"); - return new NodeApi(); + return new NodeApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/node.api.ts b/src/common/k8s-api/endpoints/node.api.ts index e1d726bed1..158f359a57 100644 --- a/src/common/k8s-api/endpoints/node.api.ts +++ b/src/common/k8s-api/endpoints/node.api.ts @@ -6,14 +6,14 @@ import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; import { cpuUnitsToNumber, unitsToBytes, isObject } from "../../../renderer/utils"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import { TypedRegEx } from "typed-regex"; export class NodeApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Node, }); } diff --git a/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts b/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts index e765cf0c38..5d21ba9587 100644 --- a/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts +++ b/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PersistentVolumeClaimApi } from "./persistent-volume-claim.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const persistentVolumeClaimApiInjectable = getInjectable({ id: "persistent-volume-claim-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeClaimApi is only available in certain environments"); - return new PersistentVolumeClaimApi(); + return new PersistentVolumeClaimApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts b/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts index 4c3c575699..947d9139a3 100644 --- a/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts +++ b/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts @@ -6,15 +6,15 @@ import type { LabelSelector, NamespaceScopedMetadata, TypedLocalObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; import type { Pod } from "./pod.api"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import { object } from "../../utils"; import type { ResourceRequirements } from "./types/resource-requirements"; export class PersistentVolumeClaimApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: PersistentVolumeClaim, }); } diff --git a/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts b/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts index 38ad80845c..cde587942c 100644 --- a/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts +++ b/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PersistentVolumeApi } from "./persistent-volume.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const persistentVolumeApiInjectable = getInjectable({ id: "persistent-volume-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeApi is only available in certain environments"); - return new PersistentVolumeApi(); + return new PersistentVolumeApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/persistent-volume.api.ts b/src/common/k8s-api/endpoints/persistent-volume.api.ts index db230e9da3..f4d80a82eb 100644 --- a/src/common/k8s-api/endpoints/persistent-volume.api.ts +++ b/src/common/k8s-api/endpoints/persistent-volume.api.ts @@ -6,7 +6,7 @@ import type { ClusterScopedMetadata, LabelSelector, ObjectReference, TypedLocalObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; import { unitsToBytes } from "../../utils"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { ResourceRequirements } from "./types/resource-requirements"; @@ -103,8 +103,8 @@ export class PersistentVolume extends KubeObject< } export class PersistentVolumeApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: PersistentVolume, }); diff --git a/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts b/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts index c2e793433b..28ecff01b1 100644 --- a/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts +++ b/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PodDisruptionBudgetApi } from "./pod-disruption-budget.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const podDisruptionBudgetApiInjectable = getInjectable({ id: "pod-disruption-budget-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podDisruptionBudgetApi is only available in certain environments"); - return new PodDisruptionBudgetApi(); + return new PodDisruptionBudgetApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts b/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts index 169efc9b70..0010bbb642 100644 --- a/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts +++ b/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts @@ -5,7 +5,7 @@ import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export interface PodDisruptionBudgetSpec { @@ -52,8 +52,8 @@ export class PodDisruptionBudget extends KubeObject< } export class PodDisruptionBudgetApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: PodDisruptionBudget, ...opts, }); diff --git a/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts b/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts index ecdbfa920d..ddd2b109ef 100644 --- a/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts +++ b/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PodMetricsApi } from "./pod-metrics.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const podMetricsApiInjectable = getInjectable({ id: "pod-metrics-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podMetricsApi is only available in certain environments"); - return new PodMetricsApi(); + return new PodMetricsApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/pod-metrics.api.ts b/src/common/k8s-api/endpoints/pod-metrics.api.ts index 6db15dd6b7..84a58026d5 100644 --- a/src/common/k8s-api/endpoints/pod-metrics.api.ts +++ b/src/common/k8s-api/endpoints/pod-metrics.api.ts @@ -5,7 +5,7 @@ import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; @@ -52,8 +52,8 @@ export class PodMetrics extends KubeObject< } export class PodMetricsApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: PodMetrics, }); diff --git a/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts b/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts index 44e25a54b1..c62b568e87 100644 --- a/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts +++ b/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PodSecurityPolicyApi } from "./pod-security-policy.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const podSecurityPolicyApiInjectable = getInjectable({ id: "pod-security-policy-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podSecurityPolicyApi is only available in certain environments"); - return new PodSecurityPolicyApi(); + return new PodSecurityPolicyApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/pod-security-policy.api.ts b/src/common/k8s-api/endpoints/pod-security-policy.api.ts index 9655fb70a1..f0e9e1110c 100644 --- a/src/common/k8s-api/endpoints/pod-security-policy.api.ts +++ b/src/common/k8s-api/endpoints/pod-security-policy.api.ts @@ -5,7 +5,7 @@ import type { ClusterScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export interface PodSecurityPolicySpec { @@ -111,8 +111,8 @@ export class PodSecurityPolicy extends KubeObject< } export class PodSecurityPolicyApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: PodSecurityPolicy, diff --git a/src/common/k8s-api/endpoints/pod.api.injectable.ts b/src/common/k8s-api/endpoints/pod.api.injectable.ts index f88a396bab..f2c1635b14 100644 --- a/src/common/k8s-api/endpoints/pod.api.injectable.ts +++ b/src/common/k8s-api/endpoints/pod.api.injectable.ts @@ -7,6 +7,8 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PodApi } from "./pod.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const podApiInjectable = getInjectable({ id: "pod-api", @@ -14,7 +16,10 @@ const podApiInjectable = getInjectable({ instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podApi is only available in certain environments"); - return new PodApi(); + return new PodApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/pod.api.ts b/src/common/k8s-api/endpoints/pod.api.ts index 862a28da94..3a0bed57cd 100644 --- a/src/common/k8s-api/endpoints/pod.api.ts +++ b/src/common/k8s-api/endpoints/pod.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions, ResourceDescriptor } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies, ResourceDescriptor } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { RequireExactlyOne } from "type-fest"; import type { KubeObjectMetadata, LocalObjectReference, Affinity, Toleration, NamespaceScopedMetadata } from "../kube-object"; @@ -17,9 +17,9 @@ import type { Container } from "./types/container"; import type { ObjectFieldSelector, ResourceFieldSelector } from "./types"; export class PodApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: Pod, }); } diff --git a/src/common/k8s-api/endpoints/priority-class.api.injectable.ts b/src/common/k8s-api/endpoints/priority-class.api.injectable.ts index 8aae454967..e21a85a290 100644 --- a/src/common/k8s-api/endpoints/priority-class.api.injectable.ts +++ b/src/common/k8s-api/endpoints/priority-class.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { PriorityClassApi } from "./priority-class.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const priorityClassApiInjectable = getInjectable({ id: "priority-class-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "PriorityClassApi is only available in certain environments"); - return new PriorityClassApi(); + return new PriorityClassApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/priority-class.api.ts b/src/common/k8s-api/endpoints/priority-class.api.ts index 516ae07ef4..1d181b6038 100644 --- a/src/common/k8s-api/endpoints/priority-class.api.ts +++ b/src/common/k8s-api/endpoints/priority-class.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; @@ -57,8 +57,8 @@ export class PriorityClass extends KubeObject< } export class PriorityClassApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: PriorityClass, ...opts, }); diff --git a/src/common/k8s-api/endpoints/replica-set.api.injectable.ts b/src/common/k8s-api/endpoints/replica-set.api.injectable.ts index 2bda99b703..c6c75d8192 100644 --- a/src/common/k8s-api/endpoints/replica-set.api.injectable.ts +++ b/src/common/k8s-api/endpoints/replica-set.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ReplicaSetApi } from "./replica-set.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const replicaSetApiInjectable = getInjectable({ id: "replica-set-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "replicaSetApi is only available in certain environments"); - return new ReplicaSetApi(); + return new ReplicaSetApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/replica-set.api.ts b/src/common/k8s-api/endpoints/replica-set.api.ts index 401ba6cf05..1fee553b8d 100644 --- a/src/common/k8s-api/endpoints/replica-set.api.ts +++ b/src/common/k8s-api/endpoints/replica-set.api.ts @@ -3,16 +3,16 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; import type { PodTemplateSpec } from "./types/pod-template-spec"; export class ReplicaSetApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: ReplicaSet, }); } diff --git a/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts b/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts index b10865fe47..a82fa3abd9 100644 --- a/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts +++ b/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ResourceQuotaApi } from "./resource-quota.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const resourceQuotaApiInjectable = getInjectable({ id: "resource-quota-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "resourceQuotaApi is only available in certain environments"); - return new ResourceQuotaApi(); + return new ResourceQuotaApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/resource-quota.api.ts b/src/common/k8s-api/endpoints/resource-quota.api.ts index b0ad18fed9..9316f9d932 100644 --- a/src/common/k8s-api/endpoints/resource-quota.api.ts +++ b/src/common/k8s-api/endpoints/resource-quota.api.ts @@ -5,7 +5,7 @@ import type { NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export type IResourceQuotaValues = Partial> & { @@ -65,8 +65,8 @@ export class ResourceQuota extends KubeObject< } export class ResourceQuotaApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: ResourceQuota, ...opts, }); diff --git a/src/common/k8s-api/endpoints/role-binding.api.injectable.ts b/src/common/k8s-api/endpoints/role-binding.api.injectable.ts index 7b802f78ce..489b20401a 100644 --- a/src/common/k8s-api/endpoints/role-binding.api.injectable.ts +++ b/src/common/k8s-api/endpoints/role-binding.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { RoleBindingApi } from "./role-binding.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const roleBindingApiInjectable = getInjectable({ id: "role-binding-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleBindingApi is only available in certain environments"); - return new RoleBindingApi(); + return new RoleBindingApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/role-binding.api.ts b/src/common/k8s-api/endpoints/role-binding.api.ts index 20620fcf39..3923efee89 100644 --- a/src/common/k8s-api/endpoints/role-binding.api.ts +++ b/src/common/k8s-api/endpoints/role-binding.api.ts @@ -5,7 +5,7 @@ import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { RoleRef } from "./types/role-ref"; @@ -44,8 +44,8 @@ export class RoleBinding extends KubeObject< } export class RoleBindingApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: RoleBinding, }); diff --git a/src/common/k8s-api/endpoints/role.api.injectable.ts b/src/common/k8s-api/endpoints/role.api.injectable.ts index d2b09f8bf7..d91d6a1fb8 100644 --- a/src/common/k8s-api/endpoints/role.api.injectable.ts +++ b/src/common/k8s-api/endpoints/role.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { RoleApi } from "./role.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const roleApiInjectable = getInjectable({ id: "role-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleApi is only available in certain environments"); - return new RoleApi(); + return new RoleApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/role.api.ts b/src/common/k8s-api/endpoints/role.api.ts index 23bee908ba..d6bd0e79d3 100644 --- a/src/common/k8s-api/endpoints/role.api.ts +++ b/src/common/k8s-api/endpoints/role.api.ts @@ -5,7 +5,7 @@ import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { PolicyRule } from "./types/policy-rule"; @@ -35,8 +35,8 @@ export class Role extends KubeObject< } export class RoleApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: Role, }); diff --git a/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts b/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts index 1b13d13270..3fbeab9a09 100644 --- a/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts +++ b/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { RuntimeClassApi } from "./runtime-class.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const runtimeClassApiInjectable = getInjectable({ id: "runtime-class-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "RuntimeClassApi is only available in certain environments"); - return new RuntimeClassApi(); + return new RuntimeClassApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/runtime-class.api.ts b/src/common/k8s-api/endpoints/runtime-class.api.ts index fa1867ea07..16e3cefab5 100644 --- a/src/common/k8s-api/endpoints/runtime-class.api.ts +++ b/src/common/k8s-api/endpoints/runtime-class.api.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope, Toleration } from "../kube-object"; @@ -63,8 +63,8 @@ export class RuntimeClass extends KubeObject< } export class RuntimeClassApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { objectConstructor: RuntimeClass, ...opts, }); diff --git a/src/common/k8s-api/endpoints/secret.api.injectable.ts b/src/common/k8s-api/endpoints/secret.api.injectable.ts index 916fbe1e93..c4173987da 100644 --- a/src/common/k8s-api/endpoints/secret.api.injectable.ts +++ b/src/common/k8s-api/endpoints/secret.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { SecretApi } from "./secret.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const secretApiInjectable = getInjectable({ id: "secret-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "secretApi is only available in certain environments"); - return new SecretApi(); + return new SecretApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/secret.api.ts b/src/common/k8s-api/endpoints/secret.api.ts index ff6b0ff845..a5a3b5eb1c 100644 --- a/src/common/k8s-api/endpoints/secret.api.ts +++ b/src/common/k8s-api/endpoints/secret.api.ts @@ -7,7 +7,7 @@ import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } fro import { KubeObject } from "../kube-object"; import type { KubeJsonApiData } from "../kube-json-api"; import { autoBind } from "../../utils"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export enum SecretType { @@ -72,8 +72,8 @@ export class Secret extends KubeObject< } export class SecretApi extends KubeApi { - constructor(options: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, options: DerivedKubeApiOptions = {}) { + super(deps, { ...options, objectConstructor: Secret, }); diff --git a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts index b66a2b4fc9..8034b9d47f 100644 --- a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts +++ b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { SelfSubjectRulesReviewApi } from "./self-subject-rules-reviews.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const selfSubjectRulesReviewApiInjectable = getInjectable({ id: "self-subject-rules-review-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "selfSubjectRulesReviewApi is only available in certain environments"); - return new SelfSubjectRulesReviewApi(); + return new SelfSubjectRulesReviewApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts index 61714d9ca3..2c73d89e77 100644 --- a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts +++ b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts @@ -4,13 +4,13 @@ */ import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export class SelfSubjectRulesReviewApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: SelfSubjectRulesReview, }); } diff --git a/src/common/k8s-api/endpoints/service-account.api.injectable.ts b/src/common/k8s-api/endpoints/service-account.api.injectable.ts index da202d6ccd..70a477ac54 100644 --- a/src/common/k8s-api/endpoints/service-account.api.injectable.ts +++ b/src/common/k8s-api/endpoints/service-account.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ServiceAccountApi } from "./service-account.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const serviceAccountApiInjectable = getInjectable({ id: "service-account-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceAccountApi is only available in certain environments"); - return new ServiceAccountApi(); + return new ServiceAccountApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/service-account.api.ts b/src/common/k8s-api/endpoints/service-account.api.ts index 01bc31dc64..e92ea667d9 100644 --- a/src/common/k8s-api/endpoints/service-account.api.ts +++ b/src/common/k8s-api/endpoints/service-account.api.ts @@ -5,7 +5,7 @@ import type { KubeObjectMetadata, KubeObjectScope, LocalObjectReference, NamespaceScopedMetadata, ObjectReference } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; @@ -50,8 +50,8 @@ export class ServiceAccount extends KubeObject< } export class ServiceAccountApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: ServiceAccount, }); diff --git a/src/common/k8s-api/endpoints/service.api.injectable.ts b/src/common/k8s-api/endpoints/service.api.injectable.ts index 31885238de..78a0f744de 100644 --- a/src/common/k8s-api/endpoints/service.api.injectable.ts +++ b/src/common/k8s-api/endpoints/service.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { ServiceApi } from "./service.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const serviceApiInjectable = getInjectable({ id: "service-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceApi is only available in certain environments"); - return new ServiceApi(); + return new ServiceApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/service.api.ts b/src/common/k8s-api/endpoints/service.api.ts index 302ad3556d..f574bea53d 100644 --- a/src/common/k8s-api/endpoints/service.api.ts +++ b/src/common/k8s-api/endpoints/service.api.ts @@ -5,7 +5,7 @@ import type { NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; export interface ServicePort { @@ -129,8 +129,8 @@ export class Service extends KubeObject< } export class ServiceApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: Service, }); diff --git a/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts b/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts index fa206cf957..1dd58fde3c 100644 --- a/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts +++ b/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { StatefulSetApi } from "./stateful-set.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const statefulSetApiInjectable = getInjectable({ id: "stateful-set-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "statefulSetApi is only available in certain environments"); - return new StatefulSetApi(); + return new StatefulSetApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/stateful-set.api.ts b/src/common/k8s-api/endpoints/stateful-set.api.ts index 8d8e9553b3..e91a856262 100644 --- a/src/common/k8s-api/endpoints/stateful-set.api.ts +++ b/src/common/k8s-api/endpoints/stateful-set.api.ts @@ -5,7 +5,7 @@ import moment from "moment"; -import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; import { KubeObject } from "../kube-object"; @@ -13,9 +13,9 @@ import type { PodTemplateSpec } from "./types/pod-template-spec"; import type { PersistentVolumeClaimTemplateSpec } from "./types/persistent-volume-claim-template-spec"; export class StatefulSetApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) { - super({ - ...opts, + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, objectConstructor: StatefulSet, }); } diff --git a/src/common/k8s-api/endpoints/storage-class.api.injectable.ts b/src/common/k8s-api/endpoints/storage-class.api.injectable.ts index 7f1abab299..e65247aa8d 100644 --- a/src/common/k8s-api/endpoints/storage-class.api.injectable.ts +++ b/src/common/k8s-api/endpoints/storage-class.api.injectable.ts @@ -7,13 +7,18 @@ import assert from "assert"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; import { StorageClassApi } from "./storage-class.api"; import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; const storageClassApiInjectable = getInjectable({ id: "storage-class-api", instantiate: (di) => { assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "storageClassApi is only available in certain environments"); - return new StorageClassApi(); + return new StorageClassApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); }, injectionToken: kubeApiInjectionToken, diff --git a/src/common/k8s-api/endpoints/storage-class.api.ts b/src/common/k8s-api/endpoints/storage-class.api.ts index ff3b233ce5..bf121e47db 100644 --- a/src/common/k8s-api/endpoints/storage-class.api.ts +++ b/src/common/k8s-api/endpoints/storage-class.api.ts @@ -6,7 +6,7 @@ import { autoBind } from "../../utils"; import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions } from "../kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; @@ -86,8 +86,8 @@ export class StorageClass extends KubeObject< } export class StorageClassApi extends KubeApi { - constructor(opts: DerivedKubeApiOptions = {}) { - super({ + constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { + super(deps, { ...opts, objectConstructor: StorageClass, }); diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index ea0bc26d4e..c2a8906400 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -22,10 +22,7 @@ import type { PartialDeep } from "type-fest"; import type { Logger } from "../logger"; import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import autoRegistrationEmitterInjectable from "./api-manager/auto-registration-emitter.injectable"; -import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import { apiKubeInjectionToken } from "./api-kube"; import type AbortController from "abort-controller"; -import loggerInjectable from "../logger.injectable"; import { matches } from "lodash/fp"; /** @@ -81,28 +78,6 @@ export interface DerivedKubeApiOptions { request?: KubeJsonApi; } -/** - * @deprecated This type is only present for backwards compatable typescript support - */ -export interface IgnoredKubeApiOptions { - /** - * @deprecated this option is overridden and should not be used - */ - objectConstructor?: any; - /** - * @deprecated this option is overridden and should not be used - */ - kind?: any; - /** - * @deprecated this option is overridden and should not be used - */ - isNamespaces?: any; - /** - * @deprecated this option is overridden and should not be used - */ - apiBase?: any; -} - export interface KubeApiQueryParams { watch?: boolean | number; resourceVersion?: string; @@ -257,6 +232,7 @@ function legacyRegisterApi(api: KubeApi): void { export interface KubeApiDependencies { readonly logger: Logger; + readonly maybeKubeApi: KubeJsonApi | undefined; } export class KubeApi< @@ -281,12 +257,10 @@ export class KubeApi< protected readonly fullApiPathname: string; protected readonly fallbackApiBases: string[] | undefined; - protected readonly dependencies: KubeApiDependencies; - - constructor(opts: KubeApiOptions) { + constructor(protected readonly dependencies: KubeApiDependencies, opts: KubeApiOptions) { const { objectConstructor, - request = asLegacyGlobalForExtensionApi(apiKubeInjectionToken), + request = this.dependencies.maybeKubeApi, kind = objectConstructor.kind, isNamespaced, apiBase: fullApiPathname = objectConstructor.apiBase, @@ -315,10 +289,6 @@ export class KubeApi< this.request = request; this.objectConstructor = objectConstructor; legacyRegisterApi(this); - - this.dependencies = { - logger: asLegacyGlobalForExtensionApi(loggerInjectable), - }; } get apiVersionWithGroup() { diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 9e7c541b58..ee6a32265e 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -14,7 +14,7 @@ import type { KubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-a import { parseKubeApi } from "./kube-api-parse"; import type { RequestInit } from "node-fetch"; import type { Patch } from "rfc6902"; -import logger from "../logger"; +import type { Logger } from "../logger"; import assert from "assert"; import type { PartialDeep } from "type-fest"; import { entries } from "../utils/objects"; @@ -86,6 +86,7 @@ export type JsonPatch = Patch; export interface KubeObjectStoreDependencies { readonly context: ClusterContext; + readonly logger: Logger; } export abstract class KubeObjectStore< @@ -498,7 +499,7 @@ export abstract class KubeObjectStore< const { type, object } = event; if (!object.metadata?.uid) { - logger.warn("[KUBE-STORE]: watch event did not have defined .metadata.uid, skipping", { event }); + this.dependencies.logger.warn("[KUBE-STORE]: watch event did not have defined .metadata.uid, skipping", { event }); // Other parts of the code will break if this happens continue; } @@ -528,7 +529,7 @@ export abstract class KubeObjectStore< break; } } catch (error) { - logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); + this.dependencies.logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); } } diff --git a/src/common/k8s-api/kube-object.ts b/src/common/k8s-api/kube-object.ts index 53bc6defdf..d88c19db89 100644 --- a/src/common/k8s-api/kube-object.ts +++ b/src/common/k8s-api/kube-object.ts @@ -12,12 +12,11 @@ import type { ItemObject } from "../item.store"; import type { Patch } from "rfc6902"; import assert from "assert"; import type { JsonObject } from "type-fest"; -import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; import requestKubeObjectPatchInjectable from "./endpoints/resource-applier.api/request-patch.injectable"; -import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import { apiKubeInjectionToken } from "./api-kube"; import requestKubeObjectCreationInjectable from "./endpoints/resource-applier.api/request-update.injectable"; import { dump } from "js-yaml"; +import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export type KubeJsonApiDataFor = K extends KubeObject ? KubeJsonApiData @@ -644,7 +643,8 @@ export class KubeObject< } } - const requestKubeObjectPatch = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectPatchInjectable); + const di = getLegacyGlobalDiForExtensionApi(); + const requestKubeObjectPatch = di.inject(requestKubeObjectPatchInjectable); const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch); if (!result.callWasSuccessful) { @@ -664,7 +664,8 @@ export class KubeObject< * @deprecated use KubeApi.update instead */ async update(data: Partial): Promise { - const requestKubeObjectCreation = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectCreationInjectable); + const di = getLegacyGlobalDiForExtensionApi(); + const requestKubeObjectCreation = di.inject(requestKubeObjectCreationInjectable); const descriptor = dump({ ...this.toPlainObject(), ...data, @@ -685,7 +686,8 @@ export class KubeObject< delete(params?: object) { assert(this.selfLink, "selfLink must be present to delete self"); - const apiKube = asLegacyGlobalForExtensionApi(apiKubeInjectionToken); + const di = getLegacyGlobalDiForExtensionApi(); + const apiKube = di.inject(apiKubeInjectionToken); return apiKube.del(this.selfLink, params); } diff --git a/src/common/k8s-api/maybe-kube-api.injectable.ts b/src/common/k8s-api/maybe-kube-api.injectable.ts new file mode 100644 index 0000000000..6717c00881 --- /dev/null +++ b/src/common/k8s-api/maybe-kube-api.injectable.ts @@ -0,0 +1,19 @@ +/** + * 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 { apiKubeInjectionToken } from "./api-kube"; + +const maybeKubeApiInjectable = getInjectable({ + id: "maybe-kube-api", + instantiate: (di) => { + try { + return di.inject(apiKubeInjectionToken); + } catch { + return undefined; + } + }, +}); + +export default maybeKubeApiInjectable; diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index dc388efbc8..c439c29d16 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -5,7 +5,6 @@ import { KubeConfig } from "@kubernetes/client-node"; import yaml from "js-yaml"; -import logger from "../main/logger"; import type { Cluster, Context, User } from "@kubernetes/client-node/dist/config_types"; import { newClusters, newContexts, newUsers } from "@kubernetes/client-node/dist/config_types"; import { isDefined } from "./utils"; @@ -220,8 +219,6 @@ export function dumpConfigYaml(kubeConfig: PartialDeep): string { users, }; - logger.debug("Dumping KubeConfig:", config); - // skipInvalid: true makes dump ignore undefined values return yaml.dump(config, { skipInvalid: true }); } diff --git a/src/common/logger.global-override-for-injectable.ts b/src/common/logger.global-override-for-injectable.ts new file mode 100644 index 0000000000..cad548cd22 --- /dev/null +++ b/src/common/logger.global-override-for-injectable.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import loggerInjectable from "./logger.injectable"; +import { getGlobalOverride } from "./test-utils/get-global-override"; +import { noop } from "./utils"; + +export default getGlobalOverride(loggerInjectable, () => ({ + warn: noop, + debug: noop, + error: noop, + info: noop, + silly: noop, +})); diff --git a/src/common/logger.ts b/src/common/logger.ts index 0b460a48ff..ad81271bfa 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { asLegacyGlobalForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import loggerInjectable from "./logger.injectable"; export interface Logger { info: (message: string, ...args: any) => void; @@ -13,10 +11,3 @@ export interface Logger { warn: (message: string, ...args: any) => void; silly: (message: string, ...args: any) => void; } - -/** - * @deprecated use `di.inject(loggerInjectable)` instead - */ -const logger = asLegacyGlobalForExtensionApi(loggerInjectable); - -export default logger; diff --git a/src/common/test-utils/use-fake-time.ts b/src/common/test-utils/use-fake-time.ts index 2a0956cebb..e455984861 100644 --- a/src/common/test-utils/use-fake-time.ts +++ b/src/common/test-utils/use-fake-time.ts @@ -16,7 +16,7 @@ export const advanceFakeTime = (milliseconds: number) => { }); }; -export const useFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { +export const testUsingFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { usingFakeTime = true; jest.useFakeTimers(); diff --git a/src/common/utils/environment-variables.injectable.ts b/src/common/utils/environment-variables.injectable.ts deleted file mode 100644 index 899d012f78..0000000000 --- a/src/common/utils/environment-variables.injectable.ts +++ /dev/null @@ -1,27 +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"; - -const environmentVariablesInjectable = getInjectable({ - id: "environment-variables", - - instantiate: () => { - const JEST_WORKER_ID = process.env.JEST_WORKER_ID; - const CICD = process.env.CICD; - - return { - // Compile-time environment variables - JEST_WORKER_ID, - CICD, - - // Runtime environment variables - LENS_DISABLE_GPU: process.env.LENS_DISABLE_GPU, - }; - }, - - causesSideEffects: true, -}); - -export default environmentVariablesInjectable; diff --git a/src/common/fs/access-path.global-override-for-injectable.ts b/src/common/utils/get-random-id.global-override-for-injectable.ts similarity index 51% rename from src/common/fs/access-path.global-override-for-injectable.ts rename to src/common/utils/get-random-id.global-override-for-injectable.ts index 747d839682..a0f87b4180 100644 --- a/src/common/fs/access-path.global-override-for-injectable.ts +++ b/src/common/utils/get-random-id.global-override-for-injectable.ts @@ -4,8 +4,6 @@ */ import { getGlobalOverride } from "../test-utils/get-global-override"; -import accessPathInjectable from "./access-path.injectable"; +import getRandomIdInjectable from "./get-random-id.injectable"; -export default getGlobalOverride(accessPathInjectable, () => async () => { - throw new Error("tried to verify path access without override"); -}); +export default getGlobalOverride(getRandomIdInjectable, () => () => "some-irrelevant-random-id"); diff --git a/src/common/utils/reactive-now/reactive-now.test.tsx b/src/common/utils/reactive-now/reactive-now.test.tsx index d2deb951b7..ab9b185438 100644 --- a/src/common/utils/reactive-now/reactive-now.test.tsx +++ b/src/common/utils/reactive-now/reactive-now.test.tsx @@ -8,14 +8,14 @@ import type { IComputedValue } from "mobx"; import { computed, observe } from "mobx"; import React from "react"; import { observer } from "mobx-react"; -import { advanceFakeTime, useFakeTime } from "../../test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../test-utils/use-fake-time"; import { reactiveNow } from "./reactive-now"; describe("reactiveNow", () => { let someComputed: IComputedValue; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); someComputed = computed(() => { const currentTimestamp = reactiveNow(); diff --git a/src/common/vars.ts b/src/common/vars.ts index 6edc6fb4e1..8f53d6354c 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -6,21 +6,6 @@ // App's common configuration for any process (main, renderer, build pipeline, etc.) import type { ThemeId } from "../renderer/themes/lens-theme"; -/** - * @deprecated Switch to using isTestEnvInjectable - */ -export const isTestEnv = !!process.env.JEST_WORKER_ID; - -/** - * @deprecated Switch to using isProductionInjectable - */ -export const isProduction = process.env.NODE_ENV === "production"; - -/** - * @deprecated Switch to using isDevelopmentInjectable - */ -export const isDevelopment = !isTestEnv && !isProduction; - export const publicPath = "/build/" as string; export const defaultThemeId: ThemeId = "lens-dark"; export const defaultFontSize = 12; diff --git a/src/renderer/vars/application-information.global-override-for-injectable.ts b/src/common/vars/application-information.global-override-for-injectable.ts similarity index 79% rename from src/renderer/vars/application-information.global-override-for-injectable.ts rename to src/common/vars/application-information.global-override-for-injectable.ts index acc47ce83d..e0b886fa65 100644 --- a/src/renderer/vars/application-information.global-override-for-injectable.ts +++ b/src/common/vars/application-information.global-override-for-injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getGlobalOverride } from "../../common/test-utils/get-global-override"; -import applicationInformationInjectable from "../../common/vars/application-information-injectable"; +import { getGlobalOverride } from "../test-utils/get-global-override"; +import applicationInformationInjectable from "./application-information-injectable"; export default getGlobalOverride(applicationInformationInjectable, () => ({ name: "some-product-name", diff --git a/src/common/vars/is-development.injectable.ts b/src/common/vars/is-development.injectable.ts index 190d754d8d..af7aeb3b91 100644 --- a/src/common/vars/is-development.injectable.ts +++ b/src/common/vars/is-development.injectable.ts @@ -4,17 +4,10 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import isProductionInjectable from "./is-production.injectable"; -import isTestEnvInjectable from "./is-test-env.injectable"; const isDevelopmentInjectable = getInjectable({ id: "is-development", - - instantiate: (di) => { - const isProduction = di.inject(isProductionInjectable); - const isTestEnv = di.inject(isTestEnvInjectable); - - return !isTestEnv && !isProduction; - }, + instantiate: (di) => !di.inject(isProductionInjectable), }); export default isDevelopmentInjectable; diff --git a/src/common/vars/is-test-env.injectable.ts b/src/common/vars/is-test-env.injectable.ts deleted file mode 100644 index 85965d0098..0000000000 --- a/src/common/vars/is-test-env.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 environmentVariablesInjectable from "../utils/environment-variables.injectable"; - -const isTestEnvInjectable = getInjectable({ - id: "is-test-env", - - instantiate: (di) => { - const { JEST_WORKER_ID: jestWorkerId } = di.inject(environmentVariablesInjectable); - - return !!jestWorkerId; - }, -}); - -export default isTestEnvInjectable; diff --git a/src/common/vars/lens-resources-dir.global-override-for-injectable.ts b/src/common/vars/lens-resources-dir.global-override-for-injectable.ts new file mode 100644 index 0000000000..1a72b0ccf7 --- /dev/null +++ b/src/common/vars/lens-resources-dir.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "../test-utils/get-global-override"; +import lensResourcesDirInjectable from "./lens-resources-dir.injectable"; + +export default getGlobalOverride(lensResourcesDirInjectable, () => "/irrelavent-dir-for-lens-resources"); diff --git a/src/common/vars/normalized-platform-architecture.injectable.ts b/src/common/vars/normalized-platform-architecture.injectable.ts index c2053d2d8e..6b98856268 100644 --- a/src/common/vars/normalized-platform-architecture.injectable.ts +++ b/src/common/vars/normalized-platform-architecture.injectable.ts @@ -3,11 +3,14 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import processArchInjectable from "./process-arch.injectable"; const normalizedPlatformArchitectureInjectable = getInjectable({ id: "normalized-platform-architecture", - instantiate: () => { - switch (process.arch) { + instantiate: (di) => { + const platformArch = di.inject(processArchInjectable); + + switch (platformArch) { case "arm64": return "arm64"; case "x64": @@ -18,10 +21,9 @@ const normalizedPlatformArchitectureInjectable = getInjectable({ case "ia32": return "ia32"; default: - throw new Error(`arch=${process.arch} is unsupported`); + throw new Error(`arch=${platformArch} is unsupported`); } }, - causesSideEffects: true, }); export default normalizedPlatformArchitectureInjectable; diff --git a/src/common/vars/platform.global-override-for-injectable.ts b/src/common/vars/platform.global-override-for-injectable.ts new file mode 100644 index 0000000000..4bb06dec5e --- /dev/null +++ b/src/common/vars/platform.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "../test-utils/get-global-override"; +import platformInjectable from "./platform.injectable"; + +export default getGlobalOverride(platformInjectable, () => "darwin"); diff --git a/src/common/vars/platform.injectable.ts b/src/common/vars/platform.injectable.ts index f2c681d657..00d6e42aca 100644 --- a/src/common/vars/platform.injectable.ts +++ b/src/common/vars/platform.injectable.ts @@ -4,7 +4,6 @@ */ import { getInjectable } from "@ogre-tools/injectable"; -// Todo: OCP by creating distinct injectables for platforms. export const allPlatforms = ["win32", "darwin", "linux"] as const; const platformInjectable = getInjectable({ diff --git a/src/common/vars/process-arch.global-override-for-injectable.ts b/src/common/vars/process-arch.global-override-for-injectable.ts new file mode 100644 index 0000000000..42d74d4ec8 --- /dev/null +++ b/src/common/vars/process-arch.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "../test-utils/get-global-override"; +import processArchInjectable from "./process-arch.injectable"; + +export default getGlobalOverride(processArchInjectable, () => "x64"); diff --git a/src/common/vars/process-arch.injectable.ts b/src/common/vars/process-arch.injectable.ts new file mode 100644 index 0000000000..5504855341 --- /dev/null +++ b/src/common/vars/process-arch.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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"; + +const processArchInjectable = getInjectable({ + id: "process-arch", + instantiate: () => process.arch, + causesSideEffects: true, +}); + +export default processArchInjectable; diff --git a/src/extensions/__tests__/extension-loader.test.ts b/src/extensions/__tests__/extension-loader.test.ts index 6e9c7cd0f9..1c010f7640 100644 --- a/src/extensions/__tests__/extension-loader.test.ts +++ b/src/extensions/__tests__/extension-loader.test.ts @@ -4,12 +4,9 @@ */ import type { ExtensionLoader } from "../extension-loader"; -import { Console } from "console"; -import { stdout, stderr } from "process"; import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; import { runInAction } from "mobx"; import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable"; -import mockFs from "mock-fs"; import { delay } from "../../renderer/utils"; import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable"; @@ -17,8 +14,6 @@ import type { IpcRenderer } from "electron"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import currentlyInClusterFrameInjectable from "../../renderer/routes/currently-in-cluster-frame.injectable"; -console = new Console(stdout, stderr); - const manifestPath = "manifest/path"; const manifestPath2 = "manifest/path2"; const manifestPath3 = "manifest/path3"; @@ -111,8 +106,6 @@ describe("ExtensionLoader", () => { }, }) as unknown as IpcRenderer); - mockFs(); - updateExtensionStateMock = jest.fn(); di.override(updateExtensionsStateInjectable, () => updateExtensionStateMock); @@ -120,10 +113,6 @@ describe("ExtensionLoader", () => { extensionLoader = di.inject(extensionLoaderInjectable); }); - afterEach(() => { - mockFs.restore(); - }); - it("renderer updates extension after ipc broadcast", async () => { expect(extensionLoader.userExtensions).toMatchInlineSnapshot(`Map {}`); diff --git a/src/extensions/common-api/index.ts b/src/extensions/common-api/index.ts index 6e4b39b1b7..01e41ca11c 100644 --- a/src/extensions/common-api/index.ts +++ b/src/extensions/common-api/index.ts @@ -11,8 +11,10 @@ import { Util } from "./utils"; import * as Catalog from "./catalog"; import * as Types from "./types"; import * as Proxy from "./proxy"; +import loggerInjectable from "../../common/logger.injectable"; +import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import logger from "../../common/logger"; +const logger = asLegacyGlobalForExtensionApi(loggerInjectable); export { App, diff --git a/src/extensions/common-api/k8s-api.ts b/src/extensions/common-api/k8s-api.ts index 9b62af7551..3856429717 100644 --- a/src/extensions/common-api/k8s-api.ts +++ b/src/extensions/common-api/k8s-api.ts @@ -18,15 +18,40 @@ import type { KubernetesCluster } from "./catalog"; import type { KubeApiDataFrom, KubeObjectStoreOptions } from "../../common/k8s-api/kube-object.store"; import { KubeObjectStore as InternalKubeObjectStore } from "../../common/k8s-api/kube-object.store"; import type { KubeJsonApiDataFor, KubeObject } from "../../common/k8s-api/kube-object"; -import type { KubeApi } from "../../common/k8s-api/kube-api"; +import type { DerivedKubeApiOptions, KubeApiDependencies, KubeApiOptions } from "../../common/k8s-api/kube-api"; +import { KubeApi as InternalKubeApi } from "../../common/k8s-api/kube-api"; import clusterFrameContextForNamespacedResourcesInjectable from "../../renderer/cluster-frame-context/for-namespaced-resources.injectable"; import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context"; +import loggerInjectable from "../../common/logger.injectable"; +import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import maybeKubeApiInjectable from "../../common/k8s-api/maybe-kube-api.injectable"; +import { DeploymentApi as InternalDeploymentApi, IngressApi as InternalIngressApi, NodeApi, PersistentVolumeClaimApi, PodApi } from "../../common/k8s-api/endpoints"; export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable); export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable); export const forRemoteCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForRemoteClusterInjectable); -export { KubeApi } from "../../common/k8s-api/kube-api"; +const getKubeApiDeps = (): KubeApiDependencies => { + const di = getLegacyGlobalDiForExtensionApi(); + + return { + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }; +}; + +// NOTE: this is done to preserve `instanceOf` behaviour +function KubeApiCstr< + Object extends KubeObject = KubeObject, + Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, +>(opts: KubeApiOptions) { + return new InternalKubeApi(getKubeApiDeps(), opts); +} + +export const KubeApi = KubeApiCstr as unknown as new< + Object extends KubeObject = KubeObject, + Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, +>(opts: KubeApiOptions) => InternalKubeApi; export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable); @@ -80,7 +105,7 @@ export { export abstract class KubeObjectStore< K extends KubeObject = KubeObject, - A extends KubeApi = KubeApi>, + A extends InternalKubeApi = InternalKubeApi>, D extends KubeJsonApiDataFor = KubeApiDataFrom, > extends InternalKubeObjectStore { /** @@ -104,9 +129,12 @@ export abstract class KubeObjectStore< */ constructor(); constructor(api?: A, opts?: KubeObjectStoreOptions) { + const di = getLegacyGlobalDiForExtensionApi(); + super( { - context: asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable), + context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion api!, @@ -122,15 +150,65 @@ export { type KubeObjectStoreSubscribeParams, } from "../../common/k8s-api/kube-object.store"; +/** + * @deprecated This type is only present for backwards compatable typescript support + */ +export interface IgnoredKubeApiOptions { + /** + * @deprecated this option is overridden and should not be used + */ + objectConstructor?: any; + /** + * @deprecated this option is overridden and should not be used + */ + kind?: any; + /** + * @deprecated this option is overridden and should not be used + */ + isNamespaces?: any; + /** + * @deprecated this option is overridden and should not be used + */ + apiBase?: any; +} + +// NOTE: these *Constructor functions MUST be `function` to work with `new X()` +function PodsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { + return new PodApi(getKubeApiDeps(), opts ?? {}); +} + +export const PodsApi = PodsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PodApi; + +function NodesApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { + return new NodeApi(getKubeApiDeps(), opts ?? {}); +} + +export const NodesApi = NodesApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => NodeApi; + +function DeploymentApiConstructor(opts?: DerivedKubeApiOptions) { + return new InternalDeploymentApi(getKubeApiDeps(), opts ?? {}); +} + +export const DeploymentApi = DeploymentApiConstructor as unknown as new (opts?: DerivedKubeApiOptions) => InternalDeploymentApi; + +function IngressApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { + return new InternalIngressApi(getKubeApiDeps(), opts ?? {}); +} + +export const IngressApi = IngressApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => InternalIngressApi; + +function PersistentVolumeClaimsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { + return new PersistentVolumeClaimApi(getKubeApiDeps(), opts ?? {}); +} + +export const PersistentVolumeClaimsApi = PersistentVolumeClaimsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PersistentVolumeClaimApi; + export { type Container as IPodContainer, type PodContainerStatus as IPodContainerStatus, Pod, - PodApi as PodsApi, Node, - NodeApi as NodesApi, Deployment, - DeploymentApi, DaemonSet, StatefulSet, Job, @@ -146,11 +224,10 @@ export { PriorityClass, Service, Endpoints as Endpoint, - Ingress, IngressApi, + Ingress, NetworkPolicy, PersistentVolume, PersistentVolumeClaim, - PersistentVolumeClaimApi as PersistentVolumeClaimsApi, StorageClass, Namespace, KubeEvent, diff --git a/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts b/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts index 9d5891a01a..e7ad2285bc 100644 --- a/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts +++ b/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts @@ -3,11 +3,14 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../../common/logger.injectable"; import { ExtensionInstallationStateStore } from "./extension-installation-state-store"; const extensionInstallationStateStoreInjectable = getInjectable({ id: "extension-installation-state-store", - instantiate: () => new ExtensionInstallationStateStore(), + instantiate: (di) => new ExtensionInstallationStateStore({ + logger: di.inject(loggerInjectable), + }), }); export default extensionInstallationStateStoreInjectable; diff --git a/src/extensions/extension-installation-state-store/extension-installation-state-store.ts b/src/extensions/extension-installation-state-store/extension-installation-state-store.ts index 1942740690..093c80934b 100644 --- a/src/extensions/extension-installation-state-store/extension-installation-state-store.ts +++ b/src/extensions/extension-installation-state-store/extension-installation-state-store.ts @@ -4,12 +4,12 @@ */ import { action, computed, observable } from "mobx"; -import logger from "../../main/logger"; import { disposer } from "../../renderer/utils"; import type { ExtendableDisposer } from "../../renderer/utils"; import * as uuid from "uuid"; import { broadcastMessage } from "../../common/ipc"; import { ipcRenderer } from "electron"; +import type { Logger } from "../../common/logger"; export enum ExtensionInstallationState { INSTALLING = "installing", @@ -17,26 +17,29 @@ export enum ExtensionInstallationState { IDLE = "idle", } +interface Dependencies { + readonly logger: Logger; +} + const Prefix = "[ExtensionInstallationStore]"; +const installingFromMainChannel = "extension-installation-state-store:install"; +const clearInstallingFromMainChannel = "extension-installation-state-store:clear-install"; + export class ExtensionInstallationStateStore { - private InstallingFromMainChannel = - "extension-installation-state-store:install"; + private readonly preInstallIds = observable.set(); + private readonly uninstallingExtensions = observable.set(); + private readonly installingExtensions = observable.set(); - private ClearInstallingFromMainChannel = - "extension-installation-state-store:clear-install"; - - private PreInstallIds = observable.set(); - private UninstallingExtensions = observable.set(); - private InstallingExtensions = observable.set(); + constructor(private readonly dependencies: Dependencies) {} bindIpcListeners = () => { ipcRenderer - .on(this.InstallingFromMainChannel, (event, extId) => { + .on(installingFromMainChannel, (event, extId) => { this.setInstalling(extId); }) - .on(this.ClearInstallingFromMainChannel, (event, extId) => { + .on(clearInstallingFromMainChannel, (event, extId) => { this.clearInstalling(extId); }); }; @@ -47,7 +50,7 @@ export class ExtensionInstallationStateStore { * @throws if state is not IDLE */ @action setInstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to set ${extId} as installing`); + this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as installing`); const curState = this.getInstallationState(extId); @@ -57,7 +60,7 @@ export class ExtensionInstallationStateStore { ); } - this.InstallingExtensions.add(extId); + this.installingExtensions.add(extId); }; /** @@ -65,7 +68,7 @@ export class ExtensionInstallationStateStore { * @param extId the ID of the extension */ setInstallingFromMain = (extId: string): void => { - broadcastMessage(this.InstallingFromMainChannel, extId); + broadcastMessage(installingFromMainChannel, extId); }; /** @@ -73,7 +76,7 @@ export class ExtensionInstallationStateStore { * @param extId the ID of the extension */ clearInstallingFromMain = (extId: string): void => { - broadcastMessage(this.ClearInstallingFromMainChannel, extId); + broadcastMessage(clearInstallingFromMainChannel, extId); }; /** @@ -85,14 +88,14 @@ export class ExtensionInstallationStateStore { @action startPreInstall = (): ExtendableDisposer => { const preInstallStepId = uuid.v4(); - logger.debug( + this.dependencies.logger.debug( `${Prefix}: starting a new preinstall phase: ${preInstallStepId}`, ); - this.PreInstallIds.add(preInstallStepId); + this.preInstallIds.add(preInstallStepId); return disposer(() => { - this.PreInstallIds.delete(preInstallStepId); - logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); + this.preInstallIds.delete(preInstallStepId); + this.dependencies.logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); }); }; @@ -102,7 +105,7 @@ export class ExtensionInstallationStateStore { * @throws if state is not IDLE */ @action setUninstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to set ${extId} as uninstalling`); + this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as uninstalling`); const curState = this.getInstallationState(extId); @@ -112,7 +115,7 @@ export class ExtensionInstallationStateStore { ); } - this.UninstallingExtensions.add(extId); + this.uninstallingExtensions.add(extId); }; /** @@ -121,13 +124,13 @@ export class ExtensionInstallationStateStore { * @throws if state is not INSTALLING */ @action clearInstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to clear ${extId} as installing`); + this.dependencies.logger.debug(`${Prefix}: trying to clear ${extId} as installing`); const curState = this.getInstallationState(extId); switch (curState) { case ExtensionInstallationState.INSTALLING: - return void this.InstallingExtensions.delete(extId); + return void this.installingExtensions.delete(extId); default: throw new Error( `${Prefix}: cannot clear INSTALLING state for ${extId}, it is currently ${curState}`, @@ -141,13 +144,13 @@ export class ExtensionInstallationStateStore { * @throws if state is not UNINSTALLING */ @action clearUninstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to clear ${extId} as uninstalling`); + this.dependencies.logger.debug(`${Prefix}: trying to clear ${extId} as uninstalling`); const curState = this.getInstallationState(extId); switch (curState) { case ExtensionInstallationState.UNINSTALLING: - return void this.UninstallingExtensions.delete(extId); + return void this.uninstallingExtensions.delete(extId); default: throw new Error( `${Prefix}: cannot clear UNINSTALLING state for ${extId}, it is currently ${curState}`, @@ -160,11 +163,11 @@ export class ExtensionInstallationStateStore { * @param extId The ID of the extension */ getInstallationState = (extId: string): ExtensionInstallationState => { - if (this.InstallingExtensions.has(extId)) { + if (this.installingExtensions.has(extId)) { return ExtensionInstallationState.INSTALLING; } - if (this.UninstallingExtensions.has(extId)) { + if (this.uninstallingExtensions.has(extId)) { return ExtensionInstallationState.UNINSTALLING; } @@ -197,14 +200,14 @@ export class ExtensionInstallationStateStore { * The current number of extensions installing */ @computed get installing(): number { - return this.InstallingExtensions.size; + return this.installingExtensions.size; } /** * The current number of extensions uninstalling */ get uninstalling(): number { - return this.UninstallingExtensions.size; + return this.uninstallingExtensions.size; } /** @@ -225,7 +228,7 @@ export class ExtensionInstallationStateStore { * The current number of extensions preinstalling */ get preinstalling(): number { - return this.PreInstallIds.size; + return this.preInstallIds.size; } /** diff --git a/src/extensions/extension-installer/extension-installer.injectable.ts b/src/extensions/extension-installer/extension-installer.injectable.ts index 528d504958..92b4436701 100644 --- a/src/extensions/extension-installer/extension-installer.injectable.ts +++ b/src/extensions/extension-installer/extension-installer.injectable.ts @@ -3,6 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable"; +import loggerInjectable from "../../common/logger.injectable"; import { ExtensionInstaller } from "./extension-installer"; import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory/extension-package-root-directory.injectable"; @@ -11,6 +13,8 @@ const extensionInstallerInjectable = getInjectable({ instantiate: (di) => new ExtensionInstaller({ extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable), + logger: di.inject(loggerInjectable), + pathToNpmCli: di.inject(pathToNpmCliInjectable), }), }); diff --git a/src/extensions/extension-installer/extension-installer.ts b/src/extensions/extension-installer/extension-installer.ts index 941f8700d1..223477d0c4 100644 --- a/src/extensions/extension-installer/extension-installer.ts +++ b/src/extensions/extension-installer/extension-installer.ts @@ -5,12 +5,14 @@ import AwaitLock from "await-lock"; import child_process from "child_process"; -import logger from "../../main/logger"; +import type { Logger } from "../../common/logger"; const logModule = "[EXTENSION-INSTALLER]"; interface Dependencies { - extensionPackageRootDirectory: string; + readonly extensionPackageRootDirectory: string; + readonly logger: Logger; + readonly pathToNpmCli: string; } const baseNpmInstallArgs = [ @@ -27,13 +29,9 @@ const baseNpmInstallArgs = [ * Installs dependencies for extensions */ export class ExtensionInstaller { - private installLock = new AwaitLock(); + private readonly installLock = new AwaitLock(); - constructor(private dependencies: Dependencies) {} - - get npmPath() { - return __non_webpack_require__.resolve("npm"); - } + constructor(private readonly dependencies: Dependencies) {} /** * Install single package using npm @@ -43,9 +41,9 @@ export class ExtensionInstaller { await this.installLock.acquireAsync(); try { - logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); + this.dependencies.logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); await this.npm(...baseNpmInstallArgs, name); - logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); + this.dependencies.logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); } finally { this.installLock.release(); } @@ -53,7 +51,7 @@ export class ExtensionInstaller { private npm(...args: string[]): Promise { return new Promise((resolve, reject) => { - const child = child_process.fork(this.npmPath, args, { + const child = child_process.fork(this.dependencies.pathToNpmCli, args, { cwd: this.dependencies.extensionPackageRootDirectory, silent: true, env: {}, diff --git a/src/extensions/ipc/ipc-main.ts b/src/extensions/ipc/ipc-main.ts index 05a2c00645..cd18bcc56c 100644 --- a/src/extensions/ipc/ipc-main.ts +++ b/src/extensions/ipc/ipc-main.ts @@ -4,12 +4,11 @@ */ import { ipcMain } from "electron"; import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; -import { Disposers } from "../lens-extension"; +import { Disposers, lensExtensionDependencies } from "../lens-extension"; import type { LensMainExtension } from "../lens-main-extension"; import type { Disposer } from "../../common/utils"; import { once } from "lodash"; import { ipcMainHandle } from "../../common/ipc"; -import logger from "../../main/logger"; export abstract class IpcMain extends IpcRegistrar { constructor(extension: LensMainExtension) { @@ -28,12 +27,12 @@ export abstract class IpcMain extends IpcRegistrar { listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; const cleanup = once(() => { - logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); return ipcMain.removeListener(prefixedChannel, listener); }); - logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); ipcMain.addListener(prefixedChannel, listener); this.extension[Disposers].push(cleanup); @@ -48,10 +47,10 @@ export abstract class IpcMain extends IpcRegistrar { handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); ipcMainHandle(prefixedChannel, handler); this.extension[Disposers].push(() => { - logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); return ipcMain.removeHandler(prefixedChannel); }); diff --git a/src/extensions/ipc/ipc-registrar.ts b/src/extensions/ipc/ipc-registrar.ts index 7f4e4ef94d..04222bd64b 100644 --- a/src/extensions/ipc/ipc-registrar.ts +++ b/src/extensions/ipc/ipc-registrar.ts @@ -12,7 +12,7 @@ export const IpcPrefix = Symbol(); export abstract class IpcRegistrar extends Singleton { readonly [IpcPrefix]: string; - constructor(protected extension: LensExtension) { + constructor(protected readonly extension: LensExtension) { super(); this[IpcPrefix] = createHash("sha256").update(extension.id).digest("hex"); } diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 89cd5dbee5..b1b024c091 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -4,10 +4,9 @@ */ import { Disposers, LensExtension, lensExtensionDependencies } from "./lens-extension"; -import type { CatalogEntity } from "../common/catalog"; +import type { CatalogEntity, CategoryFilter } from "../common/catalog"; import type { Disposer } from "../common/utils"; import type { EntityFilter } from "../renderer/api/catalog/entity/registry"; -import type { CategoryFilter } from "../renderer/api/catalog-category-registry"; import type { TopBarRegistration } from "../renderer/components/layout/top-bar/top-bar-registration"; import type { KubernetesCluster } from "../common/catalog-entities"; import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/welcome-menu-items/welcome-menu-registration"; diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index 83188c7d39..dc8c52053d 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -14,12 +14,19 @@ import createWorkloadLogsTabInjectable from "../../renderer/components/dock/logs import sendCommandInjectable from "../../renderer/components/dock/terminal/send-command.injectable"; import renameTabInjectable from "../../renderer/components/dock/dock/rename-tab.injectable"; import { asLegacyGlobalObjectForExtensionApiWithModifications } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications"; -import { podStore } from "../../renderer/components/+workloads-pods/legacy-store"; import { ConfirmDialog as _ConfirmDialog } from "../../renderer/components/confirm-dialog"; import type { ConfirmDialogBooleanParams, ConfirmDialogParams, ConfirmDialogProps } from "../../renderer/components/confirm-dialog"; import openConfirmDialogInjectable from "../../renderer/components/confirm-dialog/open.injectable"; import confirmInjectable from "../../renderer/components/confirm-dialog/confirm.injectable"; import notificationsStoreInjectable from "../../renderer/components/notifications/notifications-store.injectable"; +import podStoreInjectable from "../../renderer/components/+workloads-pods/store.injectable"; +import getDetailsUrlInjectable from "../../renderer/components/kube-detail-params/get-details-url.injectable"; +import showDetailsInjectable from "../../renderer/components/kube-detail-params/show-details.injectable"; +import showCheckedErrorNotificationInjectable from "../../renderer/components/notifications/show-checked-error.injectable"; +import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable"; +import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable"; +import showShortInfoNotificationInjectable from "../../renderer/components/notifications/show-short-info.injectable"; +import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable"; // layouts export * from "../../renderer/components/layout/main-layout"; @@ -65,7 +72,25 @@ export * from "../../renderer/components/drawer"; export * from "../../renderer/components/dialog"; export * from "../../renderer/components/line-progress"; export * from "../../renderer/components/menu"; -export * from "../../renderer/components/notifications"; + +export type { + CreateNotificationOptions, + Notification, + NotificationId, + NotificationMessage, + NotificationStatus, + ShowNotification, + NotificationsStore, +} from "../../renderer/components/notifications"; + +export const Notifications = { + ok: asLegacyGlobalFunctionForExtensionApi(showSuccessNotificationInjectable), + error: asLegacyGlobalFunctionForExtensionApi(showErrorNotificationInjectable), + checkedError: asLegacyGlobalFunctionForExtensionApi(showCheckedErrorNotificationInjectable), + info: asLegacyGlobalFunctionForExtensionApi(showInfoNotificationInjectable), + shortInfo: asLegacyGlobalFunctionForExtensionApi(showShortInfoNotificationInjectable), +}; + export * from "../../renderer/components/spinner"; export * from "../../renderer/components/stepper"; export * from "../../renderer/components/wizard"; @@ -82,8 +107,17 @@ export { type MonacoTheme, type MonacoCustomTheme, } from "../../renderer/components/monaco-editor"; +/** + * @deprecated Use `Renderer.Navigation.getDetailsUrl` + */ +export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable); + +/** + * @deprecated Use `Renderer.Navigation.showDetails` + */ +export const showDetails = asLegacyGlobalFunctionForExtensionApi(showDetailsInjectable); + // kube helpers -export * from "../../renderer/components/kube-detail-params"; export * from "../../renderer/components/kube-object-details"; export * from "../../renderer/components/kube-object-list-layout"; export * from "../../renderer/components/kube-object-menu"; @@ -103,6 +137,7 @@ export const terminalStore = asLegacyGlobalObjectForExtensionApiWithModification ); const renameTab = asLegacyGlobalFunctionForExtensionApi(renameTabInjectable); +const podStore = asLegacyGlobalForExtensionApi(podStoreInjectable); export const logTabStore = asLegacyGlobalObjectForExtensionApiWithModifications( logTabStoreInjectable, diff --git a/src/extensions/renderer-api/navigation.ts b/src/extensions/renderer-api/navigation.ts index b2ea235ecc..ec7081c948 100644 --- a/src/extensions/renderer-api/navigation.ts +++ b/src/extensions/renderer-api/navigation.ts @@ -11,7 +11,7 @@ import isActiveRouteInjectable from "../../renderer/navigation/is-route-active.i import navigateInjectable from "../../renderer/navigation/navigate.injectable"; import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -export type { PageParamInit, PageParam } from "../../renderer/navigation"; +export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param"; export type { URLParams } from "../../common/utils/buildUrl"; export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable); diff --git a/src/features/application-menu/application-menu.test.ts b/src/features/application-menu/application-menu.test.ts index e0128b83fb..0db35db744 100644 --- a/src/features/application-menu/application-menu.test.ts +++ b/src/features/application-menu/application-menu.test.ts @@ -5,7 +5,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import populateApplicationMenuInjectable from "./main/populate-application-menu.injectable"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths"; import platformInjectable, { allPlatforms } from "../../common/vars/platform.injectable"; @@ -14,7 +14,7 @@ describe.each(allPlatforms)("application-menu, given platform is '%s'", (platfor let populateApplicationMenuMock: jest.Mock; beforeEach(async () => { - useFakeTime(); + testUsingFakeTime(); populateApplicationMenuMock = jest.fn(); diff --git a/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts b/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts index 7cae6d0079..7f35861a45 100644 --- a/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts +++ b/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts @@ -5,7 +5,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import populateApplicationMenuInjectable from "./main/populate-application-menu.injectable"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths"; import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemInjectionToken from "./main/menu-items/application-menu-item-injection-token"; @@ -18,7 +18,7 @@ describe("handling-of-orphan-application-menu-items, given orphan menu item", () let logErrorMock: jest.Mock; beforeEach(async () => { - useFakeTime(); + testUsingFakeTime(); populateApplicationMenuMock = jest.fn(); logErrorMock = jest.fn(); diff --git a/src/features/application-update/analytics-for-installing-update.test.ts b/src/features/application-update/analytics-for-installing-update.test.ts index 2a7b8d19fd..ce77d37229 100644 --- a/src/features/application-update/analytics-for-installing-update.test.ts +++ b/src/features/application-update/analytics-for-installing-update.test.ts @@ -17,7 +17,7 @@ import type { DownloadPlatformUpdate } from "./main/download-update/download-pla import downloadPlatformUpdateInjectable from "./main/download-update/download-platform-update/download-platform-update.injectable"; import quitAndInstallUpdateInjectable from "./main/quit-and-install-update.injectable"; import periodicalCheckForUpdatesInjectable from "./child-features/periodical-checking-of-updates/main/periodical-check-for-updates.injectable"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import getBuildVersionInjectable from "../../main/vars/build-version/get-build-version.injectable"; @@ -29,7 +29,7 @@ describe("analytics for installing update", () => { let mainDi: DiContainer; beforeEach(async () => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx b/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx index 88f59a720c..adede427d2 100644 --- a/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx +++ b/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx @@ -16,7 +16,7 @@ import type { ApplicationBuilder } from "../../../../renderer/components/test-ut import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import quitAndInstallUpdateInjectable from "../../main/quit-and-install-update.injectable"; -import { advanceFakeTime, useFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; function daysToMilliseconds(days: number) { return Math.round(days * 24 * 60 * 60 * 1000); @@ -29,7 +29,7 @@ describe("encourage user to update when sufficient time passed since update was let quitAndInstallUpdateMock: jest.MockedFunction<() => void>; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); applicationBuilder = getApplicationBuilder(); diff --git a/src/features/application-update/child-features/application-update-using-top-bar/renderer/update-application-top-bar-item/update-button/update-button.tsx b/src/features/application-update/child-features/application-update-using-top-bar/renderer/update-application-top-bar-item/update-button/update-button.tsx index 317e64936b..acdd9929f2 100644 --- a/src/features/application-update/child-features/application-update-using-top-bar/renderer/update-application-top-bar-item/update-button/update-button.tsx +++ b/src/features/application-update/child-features/application-update-using-top-bar/renderer/update-application-top-bar-item/update-button/update-button.tsx @@ -24,7 +24,7 @@ interface Dependencies { update: () => void; } -export const NonInjectedUpdateButton = observer(({ warningLevel, update, id }: UpdateButtonProps & Dependencies) => { +const NonInjectedUpdateButton = observer(({ warningLevel, update, id }: UpdateButtonProps & Dependencies) => { const buttonId = id ?? "update-lens-button"; const menuIconProps: IconProps = { material: "update", small: true }; const [opened, setOpened] = useState(false); diff --git a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts index b5a333d24e..39ac24570a 100644 --- a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts +++ b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts @@ -14,7 +14,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; -import { advanceFakeTime, useFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; import quitAndInstallUpdateInjectable from "../../main/quit-and-install-update.injectable"; import timeAfterUpdateMustBeInstalledInjectable from "./renderer/force-update-modal/time-after-update-must-be-installed.injectable"; import secondsAfterInstallStartsInjectable from "./renderer/force-update-modal/seconds-after-install-starts.injectable"; @@ -31,7 +31,7 @@ describe("force user to update when too long since update was downloaded", () => let quitAndInstallUpdateMock: jest.Mock; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); applicationBuilder = getApplicationBuilder(); diff --git a/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts b/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts index e38ebdcbfc..22765b85de 100644 --- a/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts +++ b/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts @@ -9,7 +9,7 @@ import electronUpdaterIsActiveInjectable from "../../../../main/electron-app/fea import publishIsConfiguredInjectable from "../../main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import periodicalCheckForUpdatesInjectable from "./main/periodical-check-for-updates.injectable"; -import { advanceFakeTime, useFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; const ENOUGH_TIME = 1000 * 60 * 60 * 2; @@ -18,7 +18,7 @@ describe("periodical checking of updates", () => { let processCheckingForUpdatesMock: jest.Mock; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/application-update/installing-update.test.ts b/src/features/application-update/installing-update.test.ts index d065f407e3..81529a851f 100644 --- a/src/features/application-update/installing-update.test.ts +++ b/src/features/application-update/installing-update.test.ts @@ -16,7 +16,7 @@ import type { DownloadPlatformUpdate } from "./main/download-update/download-pla import downloadPlatformUpdateInjectable from "./main/download-update/download-platform-update/download-platform-update.injectable"; import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable"; import processCheckingForUpdatesInjectable from "./main/process-checking-for-updates.injectable"; -import { useFakeTime } from "../../common/test-utils/use-fake-time"; +import { testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable"; describe("installing update", () => { @@ -27,7 +27,7 @@ describe("installing update", () => { let setUpdateOnQuitMock: jest.Mock; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap index c1e14103da..49d085fc85 100644 --- a/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap +++ b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap @@ -3863,20 +3863,6 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="value" /> -
- - Id - - - some-entity-id - -
-
- - Id - - - some-weblink-id - -
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/__snapshots__/order-of-sidebar-items.test.tsx.snap b/src/features/cluster/__snapshots__/order-of-sidebar-items.test.tsx.snap index e00b4127e0..faf31e0cd7 100644 --- a/src/features/cluster/__snapshots__/order-of-sidebar-items.test.tsx.snap +++ b/src/features/cluster/__snapshots__/order-of-sidebar-items.test.tsx.snap @@ -2,6 +2,44 @@ exports[`cluster - order of sidebar items when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -567,6 +605,44 @@ exports[`cluster - order of sidebar items when rendered renders 1`] = ` exports[`cluster - order of sidebar items when rendered when parent is expanded renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-core.test.tsx.snap b/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-core.test.tsx.snap index 8324df1e81..3f79b3e219 100644 --- a/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-core.test.tsx.snap +++ b/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-core.test.tsx.snap @@ -2,6 +2,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations given empty state for expanded sidebar items already exists, when rendered renders without errors 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -540,6 +578,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given no initially persisted state for sidebar items, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1078,6 +1154,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given no initially persisted state for sidebar items, when rendered when a parent sidebar item is expanded renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1636,6 +1750,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given no initially persisted state for sidebar items, when rendered when a parent sidebar item is expanded when a child of the parent is selected renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2072,6 +2224,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2487,6 +2677,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given state for expanded sidebar items already exists, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3045,6 +3273,44 @@ exports[`cluster - sidebar and tab navigation for core given core registrations exports[`cluster - sidebar and tab navigation for core given core registrations given state for expanded unknown sidebar items already exists, when rendered renders without errors 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap b/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap index 5e46f2e19a..0936bd6264 100644 --- a/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap +++ b/src/features/cluster/__snapshots__/sidebar-and-tab-navigation-for-extensions.test.tsx.snap @@ -2,6 +2,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given empty state for expanded sidebar items already exists, when rendered renders without errors 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -540,6 +578,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given no initially persisted state for sidebar items, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1078,6 +1154,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given no initially persisted state for sidebar items, when rendered when a parent sidebar item is expanded renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1652,6 +1766,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given no initially persisted state for sidebar items, when rendered when a parent sidebar item is expanded when a child of the parent is selected renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2145,6 +2297,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given no initially persisted state for sidebar items, when rendered when a parent sidebar item is expanded when a child of the parent is selected when selecting sibling tab renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2638,6 +2828,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3094,6 +3322,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given state for expanded sidebar items already exists, when rendered renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3668,6 +3934,44 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit exports[`cluster - sidebar and tab navigation for extensions given extension with cluster pages and cluster page menus given state for expanded unknown sidebar items already exists, when rendered renders without errors 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/__snapshots__/visibility-of-sidebar-items.test.tsx.snap b/src/features/cluster/__snapshots__/visibility-of-sidebar-items.test.tsx.snap index 12daa81196..78c7646b7e 100644 --- a/src/features/cluster/__snapshots__/visibility-of-sidebar-items.test.tsx.snap +++ b/src/features/cluster/__snapshots__/visibility-of-sidebar-items.test.tsx.snap @@ -2,6 +2,44 @@ exports[`cluster - visibility of sidebar items given kube resource for route is not allowed renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -512,6 +550,44 @@ exports[`cluster - visibility of sidebar items given kube resource for route is exports[`cluster - visibility of sidebar items given kube resource for route is not allowed when kube resource becomes allowed renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/__snapshots__/workload-overview.test.tsx.snap b/src/features/cluster/__snapshots__/workload-overview.test.tsx.snap index a1cb791fe4..7e07408ae0 100644 --- a/src/features/cluster/__snapshots__/workload-overview.test.tsx.snap +++ b/src/features/cluster/__snapshots__/workload-overview.test.tsx.snap @@ -3,6 +3,44 @@ exports[`workload overview when navigating to workload overview renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx b/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx index 5c527edbb4..5cbf8267de 100644 --- a/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx +++ b/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx @@ -18,6 +18,7 @@ import type { Cluster } from "../../../common/cluster/cluster"; import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import joinPathsInjectable from "../../../common/path/join-paths.injectable"; +import { advanceFakeTime } from "../../../common/test-utils/use-fake-time"; const currentClusterServerUrl = "https://localhost"; const nonCurrentClusterServerUrl = "http://localhost"; @@ -135,11 +136,8 @@ describe("Deleting a cluster", () => { describe("when the dialog is opened for the current cluster", () => { // TODO: replace with actual behaviour instead of technical use beforeEach(async () => { - openDeleteClusterDialog({ - cluster: currentCluster, - config, - }); - + openDeleteClusterDialog(config, currentCluster); + advanceFakeTime(1000); await rendered.findByTestId("delete-cluster-dialog"); }); @@ -159,11 +157,8 @@ describe("Deleting a cluster", () => { describe("when the dialog is opened for not the current cluster", () => { // TODO: replace with actual behaviour instead of technical use beforeEach(async () => { - openDeleteClusterDialog({ - cluster: nonCurrentCluster, - config, - }); - + openDeleteClusterDialog(config, nonCurrentCluster); + advanceFakeTime(1000); await rendered.findByTestId("delete-cluster-dialog"); }); @@ -219,11 +214,8 @@ describe("Deleting a cluster", () => { describe("when the dialog is opened", () => { // TODO: replace with actual behaviour instead of technical use beforeEach(async () => { - openDeleteClusterDialog({ - cluster: currentCluster, - config, - }); - + openDeleteClusterDialog(config, currentCluster); + advanceFakeTime(1000); await rendered.findByTestId("delete-cluster-dialog"); }); @@ -258,11 +250,8 @@ describe("Deleting a cluster", () => { describe("when the dialog is opened", () => { // TODO: replace with actual behaviour instead of technical use beforeEach(async () => { - openDeleteClusterDialog({ - cluster: currentCluster, - config, - }); - + openDeleteClusterDialog(config, currentCluster); + advanceFakeTime(1000); await rendered.findByTestId("delete-cluster-dialog"); }); diff --git a/src/features/cluster/extension-api/__snapshots__/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/extension-api/__snapshots__/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx.snap index b820870aa4..df94d42877 100644 --- a/src/features/cluster/extension-api/__snapshots__/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/extension-api/__snapshots__/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,44 @@ exports[`disable-cluster-pages-when-cluster-is-not-relevant given extension should be enabled for the cluster, when navigating renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -393,6 +431,44 @@ exports[`disable-cluster-pages-when-cluster-is-not-relevant given extension shou exports[`disable-cluster-pages-when-cluster-is-not-relevant given extension shouldn't be enabled for the cluster, when navigating renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -905,6 +981,44 @@ exports[`disable-cluster-pages-when-cluster-is-not-relevant given extension shou exports[`disable-cluster-pages-when-cluster-is-not-relevant given not yet known if extension should be enabled for the cluster, when navigating renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/extension-api/__snapshots__/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/extension-api/__snapshots__/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx.snap index 21068f25b2..0faccf1aaa 100644 --- a/src/features/cluster/extension-api/__snapshots__/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/extension-api/__snapshots__/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,44 @@ exports[`disable sidebar items when cluster is not relevant given extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -533,6 +571,44 @@ exports[`disable sidebar items when cluster is not relevant given extension shou exports[`disable sidebar items when cluster is not relevant given extension shouldn't be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1045,6 +1121,44 @@ exports[`disable sidebar items when cluster is not relevant given extension shou exports[`disable sidebar items when cluster is not relevant given not yet known if extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/extension-api/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx b/src/features/cluster/extension-api/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx index 0c69f49f80..fcd5044639 100644 --- a/src/features/cluster/extension-api/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx +++ b/src/features/cluster/extension-api/disable-cluster-pages-when-cluster-is-not-relevant.test.tsx @@ -10,7 +10,6 @@ import { getApplicationBuilder } from "../../../renderer/components/test-utils/g import type { TestExtensionRenderer } from "../../../renderer/components/test-utils/get-extension-fake"; import type { KubernetesCluster } from "../../../common/catalog-entities"; import React from "react"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; describe("disable-cluster-pages-when-cluster-is-not-relevant", () => { let builder: ApplicationBuilder; @@ -23,10 +22,6 @@ describe("disable-cluster-pages-when-cluster-is-not-relevant", () => { builder.setEnvironmentToClusterFrame(); - builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - }); - isEnabledForClusterMock = asyncFn(); const testExtension = { diff --git a/src/features/cluster/extension-api/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx b/src/features/cluster/extension-api/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx index f94a0049d9..efc16c8ef2 100644 --- a/src/features/cluster/extension-api/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx +++ b/src/features/cluster/extension-api/disable-sidebar-items-when-cluster-is-not-relevant.test.tsx @@ -9,7 +9,6 @@ import type { ApplicationBuilder } from "../../../renderer/components/test-utils import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; import type { KubernetesCluster } from "../../../common/catalog-entities"; import React from "react"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; describe("disable sidebar items when cluster is not relevant", () => { let builder: ApplicationBuilder; @@ -21,10 +20,6 @@ describe("disable sidebar items when cluster is not relevant", () => { builder.setEnvironmentToClusterFrame(); - builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - }); - isEnabledForClusterMock = asyncFn(); const testExtension = { diff --git a/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap index 2e20387adc..5f9f15db46 100644 --- a/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,120 @@ exports[`disable kube object detail items when cluster is not relevant given extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ some-kind: some-name + + + content_copy + + +
+ Copy +
+
+
+
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
+
+ Some detail +
+
+
+ + Events + +
+
+
+
+
+
@@ -35,11 +149,12 @@ exports[`disable kube object detail items when cluster is not relevant given ext >
@@ -252,118 +367,130 @@ exports[`disable kube object detail items when cluster is not relevant given ext class="contents" >
-
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - some-name - -
-
- - Namespace - - - some-namespace - -
-
- Some detail -
-
-
- - Events - -
+ Overview
-
+
+
+
+
+ Overview +
+
+
+ + +
+
+
+ All namespaces +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-kind: some-name + + + content_copy + + +
+ Copy +
+
+
+
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
+
+
+ + Events + +
+
+
+
+
+
@@ -534,11 +770,12 @@ exports[`disable kube object detail items when cluster is not relevant given ext >
@@ -751,113 +988,130 @@ exports[`disable kube object detail items when cluster is not relevant given ext class="contents" >
-
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - some-name - -
-
- - Namespace - - - some-namespace - -
-
-
- - Events - -
+ Overview
-
+
+
+
+
+ Overview +
+
+
+ + +
+
+
+ All namespaces +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-kind: some-name + + + content_copy + + +
+ Copy +
+
+
+
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
+
+
+ + Events + +
+
+
+
+
+
@@ -1028,11 +1391,12 @@ exports[`disable kube object detail items when cluster is not relevant given not >
@@ -1245,113 +1609,130 @@ exports[`disable kube object detail items when cluster is not relevant given not class="contents" >
-
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - some-name - -
-
- - Namespace - - - some-namespace - -
-
-
- - Events - -
+ Overview
-
+
+
+
+
+ Overview +
+
+
+ + +
+
+
+ All namespaces +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-kind: some-name + + + content_copy + + +
+ Copy +
+
+
+
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
+
+
+ + Events + +
+
+
+
+
+
@@ -35,11 +144,12 @@ exports[`reactively hide kube object detail item renders 1`] = ` >
@@ -252,113 +362,130 @@ exports[`reactively hide kube object detail item renders 1`] = ` class="contents" >
-
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - some-name - -
-
- - Namespace - - - some-namespace - -
-
-
- - Events - -
+ Overview
-
+
+
+
+
+ Overview +
+
+
+ + +
+
+
+ All namespaces +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-kind: some-name + + + content_copy + + +
+ Copy +
+
+
+
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
+
+ Some detail +
+
+
+ + Events + +
+
+
+
+
+
@@ -529,11 +770,12 @@ exports[`reactively hide kube object detail item when the item is shown renders >
@@ -746,118 +988,130 @@ exports[`reactively hide kube object detail item when the item is shown renders class="contents" >
-
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - some-name - -
-
- - Namespace - - - some-namespace - -
-
- Some detail -
-
-
- - Events - -
+ Overview
-
+
+
+
+
+ Overview +
+
+
+ + +
+
+
+ All namespaces +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+
+
+
apiManager.registerApi(api); apiManager.registerStore(store); - - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - - runInAction(() => { - windowDi.register(testRouteInjectable, testRouteComponentInjectable); - }); }); isEnabledForClusterMock = asyncFn(); @@ -81,12 +68,8 @@ describe("disable kube object detail items when cluster is not relevant", () => rendered = await builder.render(); const windowDi = builder.applicationWindow.only.di; - - const navigateToRoute = windowDi.inject(navigateToRouteInjectionToken); const showDetails = windowDi.inject(showDetailsInjectable); - const testRoute = windowDi.inject(testRouteInjectable); - navigateToRoute(testRoute); showDetails("/apis/some-api-version/namespaces/some-namespace/some-kind/some-name"); builder.extensions.enable(testExtension); @@ -137,30 +120,6 @@ describe("disable kube object detail items when cluster is not relevant", () => }); }); -const testRouteInjectable = getInjectable({ - id: "test-route", - - instantiate: () => ({ - path: "/test-route", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -const testRouteComponentInjectable = getInjectable({ - id: "test-route-component", - - instantiate: (di) => ({ - route: di.inject(testRouteInjectable), - - Component: () => , - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - const getKubeObjectStub = (kind: string, apiVersion: string) => KubeObject.create({ apiVersion, diff --git a/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx b/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx index b046dc8ee8..1a22d5d0b0 100644 --- a/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx +++ b/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx @@ -5,16 +5,11 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; -import { getInjectable } from "@ogre-tools/injectable"; -import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token"; import type { IObservableValue } from "mobx"; import { runInAction, computed, observable } from "mobx"; import React from "react"; -import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; -import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; import { KubeObject } from "../../../../common/k8s-api/kube-object"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable"; -import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details"; import type { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; import type { KubeApi } from "../../../../common/k8s-api/kube-api"; import showDetailsInjectable from "../../../../renderer/components/kube-detail-params/show-details.injectable"; @@ -41,10 +36,6 @@ describe("reactively hide kube object detail item", () => { apiManager.registerApi(api); apiManager.registerStore(store); - - runInAction(() => { - windowDi.register(testRouteInjectable, testRouteComponentInjectable); - }); }); someObservable = observable.box(false); @@ -76,12 +67,8 @@ describe("reactively hide kube object detail item", () => { rendered = await builder.render(); const windowDi = builder.applicationWindow.only.di; - - const navigateToRoute = windowDi.inject(navigateToRouteInjectionToken); const showDetails = windowDi.inject(showDetailsInjectable); - const testRoute = windowDi.inject(testRouteInjectable); - navigateToRoute(testRoute); showDetails("/apis/some-api-version/namespaces/some-namespace/some-kind/some-name"); builder.extensions.enable(testExtension); @@ -114,31 +101,6 @@ describe("reactively hide kube object detail item", () => { expect(actual).toBeInTheDocument(); }); }); - -}); - -const testRouteInjectable = getInjectable({ - id: "test-route", - - instantiate: () => ({ - path: "/test-route", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -const testRouteComponentInjectable = getInjectable({ - id: "test-route-component", - - instantiate: (di) => ({ - route: di.inject(testRouteInjectable), - - Component: () => , - }), - - injectionToken: routeSpecificComponentInjectionToken, }); const getKubeObjectStub = (kind: string, apiVersion: string) => diff --git a/src/features/cluster/kube-object-menu/extension-api/__snapshots__/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/kube-object-menu/extension-api/__snapshots__/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx.snap index 292a820f4b..f1f1bb8490 100644 --- a/src/features/cluster/kube-object-menu/extension-api/__snapshots__/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/kube-object-menu/extension-api/__snapshots__/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,44 @@ exports[`disable kube object menu items when cluster is not relevant given extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -398,6 +436,44 @@ exports[`disable kube object menu items when cluster is not relevant given exten exports[`disable kube object menu items when cluster is not relevant given extension shouldn't be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -787,6 +863,44 @@ exports[`disable kube object menu items when cluster is not relevant given exten exports[`disable kube object menu items when cluster is not relevant given not yet known if extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx b/src/features/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx index 75df0f9359..f7dff90f24 100644 --- a/src/features/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx +++ b/src/features/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx @@ -15,7 +15,6 @@ import React from "react"; import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; import { KubeObject } from "../../../../common/k8s-api/kube-object"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; import { KubeObjectMenu } from "../../../../renderer/components/kube-object-menu"; describe("disable kube object menu items when cluster is not relevant", () => { @@ -31,8 +30,6 @@ describe("disable kube object menu items when cluster is not relevant", () => { builder.setEnvironmentToClusterFrame(); builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - runInAction(() => { windowDi.register(testRouteInjectable, testRouteComponentInjectable); }); diff --git a/src/features/cluster/kube-object-status-icon/__snapshots__/show-status-for-a-kube-object.test.tsx.snap b/src/features/cluster/kube-object-status-icon/__snapshots__/show-status-for-a-kube-object.test.tsx.snap index dec48cdc12..0105bb93ee 100644 --- a/src/features/cluster/kube-object-status-icon/__snapshots__/show-status-for-a-kube-object.test.tsx.snap +++ b/src/features/cluster/kube-object-status-icon/__snapshots__/show-status-for-a-kube-object.test.tsx.snap @@ -3,6 +3,44 @@ exports[`show status for a kube object given application starts and in test page renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -387,6 +425,44 @@ exports[`show status for a kube object given application starts and in test page exports[`show status for a kube object given application starts and in test page when critical status emerges renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -811,6 +887,44 @@ exports[`show status for a kube object given application starts and in test page exports[`show status for a kube object given application starts and in test page when info status emerges renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1235,6 +1349,44 @@ exports[`show status for a kube object given application starts and in test page exports[`show status for a kube object given application starts and in test page when status for irrelevant kube object api version emerges renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1619,6 +1771,44 @@ exports[`show status for a kube object given application starts and in test page exports[`show status for a kube object given application starts and in test page when status for irrelevant kube object kind emerges renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2003,6 +2193,44 @@ exports[`show status for a kube object given application starts and in test page exports[`show status for a kube object given application starts and in test page when warning status emerges renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/kube-object-status-icon/extension-api/__snapshots__/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/kube-object-status-icon/extension-api/__snapshots__/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx.snap index 9fc7e6bbf1..56d42fced8 100644 --- a/src/features/cluster/kube-object-status-icon/extension-api/__snapshots__/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/kube-object-status-icon/extension-api/__snapshots__/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,44 @@ exports[`disable kube object statuses when cluster is not relevant given extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -427,6 +465,44 @@ exports[`disable kube object statuses when cluster is not relevant given extensi exports[`disable kube object statuses when cluster is not relevant given extension shouldn't be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -811,6 +887,44 @@ exports[`disable kube object statuses when cluster is not relevant given extensi exports[`disable kube object statuses when cluster is not relevant given not yet known if extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/kube-object-status-icon/extension-api/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx b/src/features/cluster/kube-object-status-icon/extension-api/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx index b3bc2b3716..49fae34daf 100644 --- a/src/features/cluster/kube-object-status-icon/extension-api/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx +++ b/src/features/cluster/kube-object-status-icon/extension-api/disable-kube-object-statuses-when-cluster-is-not-relevant.test.tsx @@ -17,7 +17,6 @@ import { routeSpecificComponentInjectionToken } from "../../../../renderer/route import { KubeObjectStatusIcon } from "../../../../renderer/components/kube-object-status-icon/kube-object-status-icon"; import { KubeObject } from "../../../../common/k8s-api/kube-object"; import { KubeObjectStatusLevel } from "../../../../common/k8s-api/kube-object-status"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; describe("disable kube object statuses when cluster is not relevant", () => { let builder: ApplicationBuilder; @@ -32,8 +31,6 @@ describe("disable kube object statuses when cluster is not relevant", () => { builder.setEnvironmentToClusterFrame(); builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - runInAction(() => { windowDi.register(testRouteInjectable, testRouteComponentInjectable); }); diff --git a/src/features/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx b/src/features/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx index 8e197f3f30..aebc03a0f4 100644 --- a/src/features/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx +++ b/src/features/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx @@ -12,7 +12,6 @@ import { observable, runInAction, computed } from "mobx"; import React from "react"; import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; import { KubeObject } from "../../../../common/k8s-api/kube-object"; import { KubeObjectStatusLevel } from "../../../../common/k8s-api/kube-object-status"; import { KubeObjectStatusIcon } from "../../../../renderer/components/kube-object-status-icon"; @@ -28,8 +27,6 @@ describe("reactively hide kube object status", () => { builder.setEnvironmentToClusterFrame(); builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - runInAction(() => { windowDi.register(testRouteInjectable, testRouteComponentInjectable); }); diff --git a/src/features/cluster/kube-object-status-icon/show-status-for-a-kube-object.test.tsx b/src/features/cluster/kube-object-status-icon/show-status-for-a-kube-object.test.tsx index 95d508351d..49fe7f9efd 100644 --- a/src/features/cluster/kube-object-status-icon/show-status-for-a-kube-object.test.tsx +++ b/src/features/cluster/kube-object-status-icon/show-status-for-a-kube-object.test.tsx @@ -5,7 +5,7 @@ import { KubeObjectStatusLevel } from "../../../common/k8s-api/kube-object-status"; import { KubeObject } from "../../../common/k8s-api/kube-object"; import React from "react"; -import { useFakeTime } from "../../../common/test-utils/use-fake-time"; +import { testUsingFakeTime } from "../../../common/test-utils/use-fake-time"; import type { DiContainer } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import type { IAtom } from "mobx"; @@ -28,7 +28,7 @@ describe("show status for a kube object", () => { let criticalStatusIsShown: boolean; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap b/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap index c898e2656f..6675611fca 100644 --- a/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap +++ b/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap @@ -3,6 +3,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -643,6 +681,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with failure renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1288,6 +1364,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given change in configuration renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2002,6 +2116,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given clicking the context menu for second namespace, when clicking to edit namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2694,6 +2846,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given clicking the context menu for second namespace, when clicking to edit namespace when second namespace resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3458,6 +3648,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given clicking the context menu for second namespace, when clicking to edit namespace when second namespace resolves when clicking dock tab for the first namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -4224,6 +4452,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given invalid change in configuration renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -4952,6 +5218,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given no changes in the configuration, when selecting to save renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -5675,6 +5979,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given no changes in the configuration, when selecting to save when saving resolves with failure renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -6389,6 +6731,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given no changes in the configuration, when selecting to save when saving resolves with success renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -7103,6 +7483,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -7817,6 +8235,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to cancel renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -8366,6 +8822,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to save and close renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -9089,6 +9583,44 @@ metadata: exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to save and close when saving resolves with failure renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -9638,6 +10170,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to save and close when saving resolves with success renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -10187,6 +10757,44 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves without namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-previously-opened-tab.test.tsx.snap b/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-previously-opened-tab.test.tsx.snap index 9554d197b6..1612e1d052 100644 --- a/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-previously-opened-tab.test.tsx.snap +++ b/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-previously-opened-tab.test.tsx.snap @@ -3,6 +3,44 @@ exports[`cluster/namespaces - edit namespaces from previously opened tab given tab was previously opened, when application is started renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -550,6 +588,44 @@ exports[`cluster/namespaces - edit namespaces from previously opened tab given t exports[`cluster/namespaces - edit namespaces from previously opened tab given tab was previously opened, when application is started when call for namespace resolves with namespace renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx b/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx index f37ada1736..a3761caf21 100644 --- a/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx +++ b/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx @@ -23,27 +23,19 @@ import showErrorNotificationInjectable from "../../../renderer/components/notifi import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable"; import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; -import { controlWhenStoragesAreReady } from "../../../renderer/utils/create-storage/storages-are-ready"; describe("cluster/namespaces - edit namespace from new tab", () => { let builder: ApplicationBuilder; - let callForNamespaceMock: AsyncFnMock; - let callForPatchNamespaceMock: AsyncFnMock; + let callForResourceMock: AsyncFnMock; + let callForPatchResourceMock: AsyncFnMock; let showSuccessNotificationMock: jest.Mock; let showErrorNotificationMock: jest.Mock; - let storagesAreReady: () => Promise; beforeEach(() => { builder = getApplicationBuilder(); builder.setEnvironmentToClusterFrame(); - callForNamespaceMock = asyncFn(); - callForPatchNamespaceMock = asyncFn(); - - showSuccessNotificationMock = jest.fn(); - showErrorNotificationMock = jest.fn(); - builder.beforeWindowStart((windowDi) => { windowDi.override( directoryForLensLocalStorageInjectable, @@ -52,17 +44,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => { windowDi.override(hostedClusterIdInjectable, () => "some-cluster-id"); - storagesAreReady = controlWhenStoragesAreReady(windowDi); + showSuccessNotificationMock = jest.fn(); + windowDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock); - windowDi.override( - showSuccessNotificationInjectable, - () => showSuccessNotificationMock, - ); - - windowDi.override( - showErrorNotificationInjectable, - () => showErrorNotificationMock, - ); + showErrorNotificationMock = jest.fn(); + windowDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock); windowDi.override(getRandomIdForEditResourceTabInjectable, () => jest @@ -71,31 +57,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => { .mockReturnValueOnce("some-second-tab-id"), ); - windowDi.override(callForResourceInjectable, () => async (selfLink: string) => { - if ( - [ - "/apis/some-api-version/namespaces/some-uid", - "/apis/some-api-version/namespaces/some-other-uid", - ].includes(selfLink) - ) { - return await callForNamespaceMock(selfLink); - } + callForResourceMock = asyncFn(); + windowDi.override(callForResourceInjectable, () => callForResourceMock); - return undefined; - }); - - windowDi.override(callForPatchResourceInjectable, () => async (namespace, ...args) => { - if ( - [ - "/apis/some-api-version/namespaces/some-uid", - "/apis/some-api-version/namespaces/some-other-uid", - ].includes(namespace.selfLink) - ) { - return await callForPatchNamespaceMock(namespace, ...args); - } - - return undefined; - }); + callForPatchResourceMock = asyncFn(); + windowDi.override(callForPatchResourceInjectable, () => callForPatchResourceMock); }); builder.allowKubeResource({ @@ -111,18 +77,13 @@ describe("cluster/namespaces - edit namespace from new tab", () => { beforeEach(async () => { rendered = await builder.render(); - await storagesAreReady(); - windowDi = builder.applicationWindow.only.di; - const navigateToNamespaces = windowDi.inject( - navigateToNamespacesInjectable, - ); + const navigateToNamespaces = windowDi.inject(navigateToNamespacesInjectable); + const dockStore = windowDi.inject(dockStoreInjectable); navigateToNamespaces(); - const dockStore = windowDi.inject(dockStoreInjectable); - // TODO: Make TerminalWindow unit testable to allow realistic behaviour dockStore.closeTab("terminal"); }); @@ -193,7 +154,7 @@ describe("cluster/namespaces - edit namespace from new tab", () => { }); it("calls for namespace", () => { - expect(callForNamespaceMock).toHaveBeenCalledWith( + expect(callForResourceMock).toHaveBeenCalledWith( "/apis/some-api-version/namespaces/some-uid", ); }); @@ -216,7 +177,7 @@ describe("cluster/namespaces - edit namespace from new tab", () => { }, }); - await callForNamespaceMock.resolve({ + await callForResourceMock.resolve({ callWasSuccessful: true, response: someNamespace, }); @@ -263,7 +224,7 @@ metadata: }); it("calls for save with empty values", () => { - expect(callForPatchNamespaceMock).toHaveBeenCalledWith( + expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, [], ); @@ -293,7 +254,7 @@ metadata: describe("when saving resolves with success", () => { beforeEach(async () => { - await callForPatchNamespaceMock.resolve({ + await callForPatchResourceMock.resolve({ callWasSuccessful: true, response: { name: "some-name", kind: "Namespace" }, }); @@ -342,7 +303,7 @@ metadata: describe("when saving resolves with failure", () => { beforeEach(async () => { - await callForPatchNamespaceMock.resolve({ + await callForPatchResourceMock.resolve({ callWasSuccessful: false, error: "some-error", }); @@ -411,7 +372,7 @@ metadata: describe("when saving resolves with success", () => { beforeEach(async () => { - await callForPatchNamespaceMock.resolve({ + await callForPatchResourceMock.resolve({ callWasSuccessful: true, response: { name: "some-name", kind: "Namespace" }, }); @@ -430,7 +391,7 @@ metadata: describe("when saving resolves with failure", () => { beforeEach(async () => { - await callForPatchNamespaceMock.resolve({ + await callForPatchResourceMock.resolve({ callWasSuccessful: false, error: "Some error", }); @@ -558,7 +519,7 @@ metadata: }); it("calls for save with changed configuration", () => { - expect(callForPatchNamespaceMock).toHaveBeenCalledWith( + expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, [ { @@ -580,7 +541,7 @@ metadata: }); it("given save resolves and another change in configuration, when saving, calls for save with changed configuration", async () => { - await callForPatchNamespaceMock.resolve({ + await callForPatchResourceMock.resolve({ callWasSuccessful: true, response: { @@ -610,7 +571,7 @@ metadata: }); - callForPatchNamespaceMock.mockClear(); + callForPatchResourceMock.mockClear(); const saveButton = rendered.getByTestId( "save-edit-resource-from-tab-for-some-first-tab-id", @@ -618,7 +579,7 @@ metadata: fireEvent.click(saveButton); - expect(callForPatchNamespaceMock).toHaveBeenCalledWith( + expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, [ { @@ -717,7 +678,7 @@ metadata: describe("given clicking the context menu for second namespace, when clicking to edit namespace", () => { beforeEach(() => { - callForNamespaceMock.mockClear(); + callForResourceMock.mockClear(); // TODO: Make implementation match the description const namespaceStub = new Namespace(someOtherNamespaceDataStub); @@ -750,7 +711,7 @@ metadata: }); it("calls for second namespace", () => { - expect(callForNamespaceMock).toHaveBeenCalledWith( + expect(callForResourceMock).toHaveBeenCalledWith( "/apis/some-api-version/namespaces/some-other-uid", ); }); @@ -772,7 +733,7 @@ metadata: }, }); - await callForNamespaceMock.resolve({ + await callForResourceMock.resolve({ callWasSuccessful: true, response: someOtherNamespace, }); @@ -798,7 +759,7 @@ metadata: }); it("when selecting to save, calls for save of second namespace", () => { - callForPatchNamespaceMock.mockClear(); + callForPatchResourceMock.mockClear(); const saveButton = rendered.getByTestId( "save-edit-resource-from-tab-for-some-second-tab-id", @@ -806,7 +767,7 @@ metadata: fireEvent.click(saveButton); - expect(callForPatchNamespaceMock).toHaveBeenCalledWith( + expect(callForPatchResourceMock).toHaveBeenCalledWith( someOtherNamespace, [], ); @@ -814,7 +775,7 @@ metadata: describe("when clicking dock tab for the first namespace", () => { beforeEach(() => { - callForNamespaceMock.mockClear(); + callForResourceMock.mockClear(); const tab = rendered.getByTestId("dock-tab-for-some-first-tab-id"); @@ -844,7 +805,7 @@ metadata: }); it("does not call for namespace", () => { - expect(callForNamespaceMock).not.toHaveBeenCalled(); + expect(callForResourceMock).not.toHaveBeenCalledWith("/apis/some-api-version/namespaces/some-uid"); }); it("has configuration in the editor", () => { @@ -865,7 +826,7 @@ metadata: }); it("when selecting to save, calls for save of first namespace", () => { - callForPatchNamespaceMock.mockClear(); + callForPatchResourceMock.mockClear(); const saveButton = rendered.getByTestId( "save-edit-resource-from-tab-for-some-first-tab-id", @@ -873,7 +834,7 @@ metadata: fireEvent.click(saveButton); - expect(callForPatchNamespaceMock).toHaveBeenCalledWith( + expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, [], ); @@ -885,7 +846,7 @@ metadata: describe("when call for namespace resolves without namespace", () => { beforeEach(async () => { - await callForNamespaceMock.resolve({ + await callForResourceMock.resolve({ callWasSuccessful: true, response: undefined, }); @@ -914,7 +875,7 @@ metadata: describe("when call for namespace resolves with failure", () => { beforeEach(async () => { - await callForNamespaceMock.resolve({ + await callForResourceMock.resolve({ callWasSuccessful: false, error: "some-error", }); diff --git a/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx b/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx index 4cf63ee353..0d49469951 100644 --- a/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx +++ b/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx @@ -11,8 +11,6 @@ import asyncFn from "@async-fn/jest"; import type { CallForResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; import callForResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; -import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; -import { controlWhenStoragesAreReady } from "../../../renderer/utils/create-storage/storages-are-ready"; import writeJsonFileInjectable from "../../../common/fs/write-json-file.injectable"; import { TabKind } from "../../../renderer/components/dock/dock/store"; import { Namespace } from "../../../common/k8s-api/endpoints"; @@ -20,7 +18,6 @@ import { Namespace } from "../../../common/k8s-api/endpoints"; describe("cluster/namespaces - edit namespaces from previously opened tab", () => { let builder: ApplicationBuilder; let callForNamespaceMock: AsyncFnMock; - let storagesAreReady: () => Promise; beforeEach(() => { builder = getApplicationBuilder(); @@ -35,10 +32,6 @@ describe("cluster/namespaces - edit namespaces from previously opened tab", () = () => "/some-directory-for-lens-local-storage", ); - windowDi.override(hostedClusterIdInjectable, () => "some-cluster-id"); - - storagesAreReady = controlWhenStoragesAreReady(windowDi); - windowDi.override(callForResourceInjectable, () => callForNamespaceMock); }); @@ -83,8 +76,6 @@ describe("cluster/namespaces - edit namespaces from previously opened tab", () = }); rendered = await builder.render(); - - await storagesAreReady(); }); it("renders", () => { diff --git a/src/features/cluster/sidebar-and-tab-navigation-for-core.test.tsx b/src/features/cluster/sidebar-and-tab-navigation-for-core.test.tsx index fd4d95d760..a4995c38ae 100644 --- a/src/features/cluster/sidebar-and-tab-navigation-for-core.test.tsx +++ b/src/features/cluster/sidebar-and-tab-navigation-for-core.test.tsx @@ -7,7 +7,6 @@ import { getInjectable } from "@ogre-tools/injectable"; import React from "react"; import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; -import directoryForLensLocalStorageInjectable from "../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token"; import type { SidebarItemRegistration } from "../../renderer/components/layout/sidebar-items.injectable"; import { sidebarItemsInjectionToken } from "../../renderer/components/layout/sidebar-items.injectable"; @@ -21,8 +20,7 @@ import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable" import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; -import sidebarStorageInjectable from "../../renderer/components/layout/sidebar-storage/sidebar-storage.injectable"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import storageSaveDelayInjectable from "../../renderer/utils/create-storage/storage-save-delay.injectable"; import { flushPromises } from "../../common/test-utils/flush-promises"; @@ -31,7 +29,7 @@ describe("cluster - sidebar and tab navigation for core", () => { let rendered: RenderResult; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); @@ -39,11 +37,6 @@ describe("cluster - sidebar and tab navigation for core", () => { builder.beforeWindowStart((windowDi) => { windowDi.override(storageSaveDelayInjectable, () => 250); - - windowDi.override( - directoryForLensLocalStorageInjectable, - () => "/some-directory-for-lens-local-storage", - ); }); }); @@ -94,10 +87,10 @@ describe("cluster - sidebar and tab navigation for core", () => { describe("given state for expanded sidebar items already exists, when rendered", () => { beforeEach(async () => { builder.beforeWindowStart(async (windowDi) => { - const writeJsonFileFake = windowDi.inject(writeJsonFileInjectable); + const writeJsonFile = windowDi.inject(writeJsonFileInjectable); - await writeJsonFileFake( - "/some-directory-for-lens-local-storage/some-cluster-id.json", + await writeJsonFile( + "/some-directory-for-app-data/some-product-name/lens-local-storage/some-cluster-id.json", { sidebar: { expanded: { "some-parent-id": true }, @@ -105,10 +98,6 @@ describe("cluster - sidebar and tab navigation for core", () => { }, }, ); - - const sidebarStorage = windowDi.inject(sidebarStorageInjectable); - - await sidebarStorage.whenReady; }); rendered = await builder.render(); @@ -137,7 +126,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const writeJsonFileFake = windowDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", + "/some-directory-for-app-data/some-product-name/lens-local-storage/some-hosted-cluster-id.json", { sidebar: { expanded: { "some-unknown-parent-id": true }, @@ -167,7 +156,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const writeJsonFileFake = windowDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", + "/some-directory-for-app-data/some-product-name/lens-local-storage/some-hosted-cluster-id.json", { someThingButSidebar: {}, }, @@ -273,7 +262,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const pathExistsFake = windowDi.inject(pathExistsInjectable); const actual = await pathExistsFake( - "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", + "/some-directory-for-app-data/some-product-name/lens-local-storage/some-hosted-cluster-id.json", ); expect(actual).toBe(false); @@ -287,7 +276,7 @@ describe("cluster - sidebar and tab navigation for core", () => { await flushPromises(); const actual = await readJsonFileFake( - "/some-directory-for-lens-local-storage/some-cluster-id.json", + "/some-directory-for-app-data/some-product-name/lens-local-storage/some-cluster-id.json", ); expect(actual).toEqual({ diff --git a/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx b/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx index 2441afa000..27a211a0cb 100644 --- a/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx +++ b/src/features/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx @@ -15,7 +15,7 @@ import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; import assert from "assert"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import type { IObservableValue } from "mobx"; import { runInAction, computed, observable } from "mobx"; import storageSaveDelayInjectable from "../../renderer/utils/create-storage/storage-save-delay.injectable"; @@ -27,7 +27,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { let rendered: RenderResult; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); applicationBuilder = getApplicationBuilder(); diff --git a/src/features/cluster/workloads/overview/extension-api/__snapshots__/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/workloads/overview/extension-api/__snapshots__/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx.snap index 1802cccace..1b29e79e6b 100644 --- a/src/features/cluster/workloads/overview/extension-api/__snapshots__/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/workloads/overview/extension-api/__snapshots__/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx.snap @@ -3,6 +3,44 @@ exports[`disable workloads overview details when cluster is not relevant given extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -520,6 +558,44 @@ exports[`disable workloads overview details when cluster is not relevant given e exports[`disable workloads overview details when cluster is not relevant given extension shouldn't be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1032,6 +1108,44 @@ exports[`disable workloads overview details when cluster is not relevant given e exports[`disable workloads overview details when cluster is not relevant given not yet known if extension should be enabled for the cluster renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/cluster/workloads/overview/extension-api/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx b/src/features/cluster/workloads/overview/extension-api/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx index 1ac5a2d17c..e40b7361fb 100644 --- a/src/features/cluster/workloads/overview/extension-api/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx +++ b/src/features/cluster/workloads/overview/extension-api/disable-workloads-overview-details-when-cluster-is-not-relevant.test.tsx @@ -8,7 +8,6 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../../../../renderer/components/test-utils/get-application-builder"; import type { KubernetesCluster } from "../../../../../common/catalog-entities"; import { getApplicationBuilder } from "../../../../../renderer/components/test-utils/get-application-builder"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; import navigateToWorkloadsOverviewInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable"; import React from "react"; @@ -23,9 +22,6 @@ describe("disable workloads overview details when cluster is not relevant", () = builder = getApplicationBuilder(); builder.setEnvironmentToClusterFrame(); - builder.beforeWindowStart((windowDi) => { - windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); - }); isEnabledForClusterMock = asyncFn(); diff --git a/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap b/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap index 3c7fbe7b4a..7932686bbf 100644 --- a/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap +++ b/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap @@ -311,7 +311,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -350,7 +350,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -1014,7 +1014,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -1053,7 +1053,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -1756,7 +1756,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -1795,7 +1795,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -2609,7 +2609,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -2648,7 +2648,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -3359,7 +3359,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -3398,7 +3398,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -4212,7 +4212,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -4251,7 +4251,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -5247,7 +5247,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -5286,7 +5286,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -6100,7 +6100,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -6139,7 +6139,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -7135,7 +7135,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -7174,7 +7174,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -7989,7 +7989,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -8028,7 +8028,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -8739,7 +8739,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -8778,7 +8778,7 @@ exports[`add custom helm repository in preferences when navigating to preference @@ -9478,7 +9478,7 @@ exports[`add custom helm repository in preferences when navigating to preference > @@ -9517,7 +9517,7 @@ exports[`add custom helm repository in preferences when navigating to preference diff --git a/src/features/helm-charts/__snapshots__/add-helm-repository-from-list-in-preferences.test.ts.snap b/src/features/helm-charts/__snapshots__/add-helm-repository-from-list-in-preferences.test.ts.snap index 652b16b3d1..516b4a0e1e 100644 --- a/src/features/helm-charts/__snapshots__/add-helm-repository-from-list-in-preferences.test.ts.snap +++ b/src/features/helm-charts/__snapshots__/add-helm-repository-from-list-in-preferences.test.ts.snap @@ -311,7 +311,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -350,7 +350,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -1014,7 +1014,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -1053,7 +1053,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -1756,7 +1756,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -1795,7 +1795,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -2551,7 +2551,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -2590,7 +2590,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -3293,7 +3293,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -3332,7 +3332,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -4035,7 +4035,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -4074,7 +4074,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -4766,7 +4766,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -4805,7 +4805,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -5542,7 +5542,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -5581,7 +5581,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -6381,7 +6381,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -6420,7 +6420,7 @@ exports[`add helm repository from list in preferences when navigating to prefere @@ -7157,7 +7157,7 @@ exports[`add helm repository from list in preferences when navigating to prefere > @@ -7196,7 +7196,7 @@ exports[`add helm repository from list in preferences when navigating to prefere diff --git a/src/features/helm-charts/__snapshots__/listing-active-helm-repositories-in-preferences.test.ts.snap b/src/features/helm-charts/__snapshots__/listing-active-helm-repositories-in-preferences.test.ts.snap index 108919c374..08746bc247 100644 --- a/src/features/helm-charts/__snapshots__/listing-active-helm-repositories-in-preferences.test.ts.snap +++ b/src/features/helm-charts/__snapshots__/listing-active-helm-repositories-in-preferences.test.ts.snap @@ -311,7 +311,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -350,7 +350,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -1014,7 +1014,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -1053,7 +1053,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -1745,7 +1745,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -1784,7 +1784,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -2368,7 +2368,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -2407,7 +2407,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -3099,7 +3099,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -3138,7 +3138,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -3722,7 +3722,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -3761,7 +3761,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -4453,7 +4453,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -4492,7 +4492,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -5229,7 +5229,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -5268,7 +5268,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -5852,7 +5852,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -5891,7 +5891,7 @@ exports[`listing active helm repositories in preferences when navigating to pref @@ -6475,7 +6475,7 @@ exports[`listing active helm repositories in preferences when navigating to pref > @@ -6514,7 +6514,7 @@ exports[`listing active helm repositories in preferences when navigating to pref diff --git a/src/features/helm-charts/__snapshots__/remove-helm-repository-from-list-of-active-repository-in-preferences.test.ts.snap b/src/features/helm-charts/__snapshots__/remove-helm-repository-from-list-of-active-repository-in-preferences.test.ts.snap index 7ea83480c0..afac42ce72 100644 --- a/src/features/helm-charts/__snapshots__/remove-helm-repository-from-list-of-active-repository-in-preferences.test.ts.snap +++ b/src/features/helm-charts/__snapshots__/remove-helm-repository-from-list-of-active-repository-in-preferences.test.ts.snap @@ -311,7 +311,7 @@ exports[`remove helm repository from list of active repositories in preferences > @@ -350,7 +350,7 @@ exports[`remove helm repository from list of active repositories in preferences @@ -1014,7 +1014,7 @@ exports[`remove helm repository from list of active repositories in preferences > @@ -1053,7 +1053,7 @@ exports[`remove helm repository from list of active repositories in preferences @@ -1728,7 +1728,7 @@ exports[`remove helm repository from list of active repositories in preferences > @@ -1767,7 +1767,7 @@ exports[`remove helm repository from list of active repositories in preferences @@ -2442,7 +2442,7 @@ exports[`remove helm repository from list of active repositories in preferences > @@ -2481,7 +2481,7 @@ exports[`remove helm repository from list of active repositories in preferences diff --git a/src/features/helm-charts/add-custom-helm-repository-in-preferences.test.ts b/src/features/helm-charts/add-custom-helm-repository-in-preferences.test.ts index 684cde0038..495d2e9fb3 100644 --- a/src/features/helm-charts/add-custom-helm-repository-in-preferences.test.ts +++ b/src/features/helm-charts/add-custom-helm-repository-in-preferences.test.ts @@ -18,7 +18,7 @@ import isPathInjectable from "../../renderer/components/input/validators/is-path import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable"; import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable"; import type { AsyncResult } from "../../common/utils/async-result"; -import { useFakeTime } from "../../common/test-utils/use-fake-time"; +import { testUsingFakeTime } from "../../common/test-utils/use-fake-time"; describe("add custom helm repository in preferences", () => { let builder: ApplicationBuilder; @@ -33,7 +33,7 @@ describe("add custom helm repository in preferences", () => { builder = getApplicationBuilder(); - useFakeTime("2021-01-01 12:00:00"); + testUsingFakeTime("2021-01-01 12:00:00"); execFileMock = asyncFn(); getActiveHelmRepositoriesMock = asyncFn(); diff --git a/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.global-override-for-injectable.ts b/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.global-override-for-injectable.ts new file mode 100644 index 0000000000..0a1304ac2d --- /dev/null +++ b/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverrideForFunction } from "../../../../../../../common/test-utils/get-global-override-for-function"; +import requestPublicHelmRepositoriesInjectable from "./request-public-helm-repositories.injectable"; + +export default getGlobalOverrideForFunction(requestPublicHelmRepositoriesInjectable); diff --git a/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-new-tab.test.ts.snap b/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-new-tab.test.ts.snap index 185284c761..1e47ed84e1 100644 --- a/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-new-tab.test.ts.snap +++ b/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-new-tab.test.ts.snap @@ -3,6 +3,44 @@ exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -434,6 +472,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1349,6 +1425,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2109,6 +2223,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given changing version to be installed renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3113,6 +3265,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given changing version to be installed when version is selected renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -4091,6 +4281,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given changing version to be installed when version is selected when default configuration resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -5064,6 +5292,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given custom name is inputted renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -6037,6 +6303,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given invalid change in configuration renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -7032,6 +7336,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given namespace selection is opened renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -8036,6 +8378,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given namespace selection is opened when namespace is selected renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -9009,6 +9389,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given no changes in configuration, when installing the chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -9991,43 +10409,22 @@ exports[`installing helm chart from new tab given tab for installing chart was n
- - - info_outline - - -
-
-

- Chart Release - - some-release - - successfully created. -

-
-
+ +
+
+ Close +
+
+
+
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -11605,6 +12052,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given no changes in configuration, when installing the chart when installation resolves when selected to show execution output renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -12505,6 +12990,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given opening details for second chart, when details resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -13724,6 +14247,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given opening details for second chart, when details resolve when selecting to install second chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -14536,6 +15097,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given opening details for second chart, when details resolve when selecting to install second chart when configuration and versions resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -15563,6 +16162,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given opening details for second chart, when details resolve when selecting to install second chart when configuration and versions resolve when selecting the dock tab for installing first chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -16588,6 +17225,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve given valid change in configuration renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -17561,6 +18236,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -18534,6 +19247,44 @@ exports[`installing helm chart from new tab given tab for installing chart was n exports[`installing helm chart from new tab given tab for installing chart was not previously opened and application is started when navigating to helm charts when selecting to install the chart when default configuration and versions resolve when cancelled renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-previously-opened-tab.test.ts.snap b/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-previously-opened-tab.test.ts.snap index a2cc46c340..7636ade12e 100644 --- a/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-previously-opened-tab.test.ts.snap +++ b/src/features/helm-charts/installing-chart/__snapshots__/installing-helm-chart-from-previously-opened-tab.test.ts.snap @@ -3,6 +3,44 @@ exports[`installing helm chart from previously opened tab given tab for installing chart was previously opened, when application is started renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -525,6 +563,44 @@ exports[`installing helm chart from previously opened tab given tab for installi exports[`installing helm chart from previously opened tab given tab for installing chart was previously opened, when application is started when configuration and version resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
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 e949e6c3c6..de1103cdc7 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 @@ -3,6 +3,44 @@ exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -502,6 +540,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1171,6 +1247,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1897,6 +2011,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2806,6 +2958,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve when readme resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3725,6 +3915,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve when readme resolves when selecting different version renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -4634,6 +4862,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve when readme resolves when selecting different version when readme rejects renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -5547,6 +5813,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve when readme resolves when selecting different version when readme resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -6466,6 +6770,44 @@ exports[`opening dock tab for installing helm chart given application is started exports[`opening dock tab for installing helm chart given application is started, when navigating to helm charts when charts resolve when opening details of a chart when chart versions resolve when readme resolves when selecting to install the chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts b/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts index 0b4f749acc..db11c245aa 100644 --- a/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts +++ b/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts @@ -9,13 +9,9 @@ import type { ApplicationBuilder } from "../../../renderer/components/test-utils import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; import { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; import getRandomInstallChartTabIdInjectable from "../../../renderer/components/dock/install-chart/get-random-install-chart-tab-id.injectable"; -import namespaceStoreInjectable from "../../../renderer/components/+namespaces/store.injectable"; -import type { NamespaceStore } from "../../../renderer/components/+namespaces/store"; import writeJsonFileInjectable from "../../../common/fs/write-json-file.injectable"; import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; -import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; import { TabKind } from "../../../renderer/components/dock/dock/store"; -import { controlWhenStoragesAreReady } from "../../../renderer/utils/create-storage/storages-are-ready"; import requestCreateHelmReleaseInjectable from "../../../common/k8s-api/endpoints/helm-releases.api/request-create.injectable"; import type { RequestHelmChartVersions } from "../../../common/k8s-api/endpoints/helm-charts.api/request-versions.injectable"; import requestHelmChartVersionsInjectable from "../../../common/k8s-api/endpoints/helm-charts.api/request-versions.injectable"; @@ -26,7 +22,6 @@ describe("installing helm chart from previously opened tab", () => { let builder: ApplicationBuilder; let requestHelmChartVersionsMock: AsyncFnMock; let requestHelmChartValuesMock: AsyncFnMock; - let storagesAreReady: () => Promise; beforeEach(() => { builder = getApplicationBuilder(); @@ -36,29 +31,15 @@ describe("installing helm chart from previously opened tab", () => { requestHelmChartVersionsMock = asyncFn(); requestHelmChartValuesMock = asyncFn(); - builder.beforeWindowStart((windowDi) => { - storagesAreReady = controlWhenStoragesAreReady(windowDi); + builder.namespaces.add("default"); + builder.namespaces.add("some-other-namespace"); + builder.beforeWindowStart((windowDi) => { windowDi.override(directoryForLensLocalStorageInjectable, () => "/some-directory-for-lens-local-storage"); - windowDi.override(hostedClusterIdInjectable, () => "some-cluster-id"); windowDi.override(requestHelmChartVersionsInjectable, () => requestHelmChartVersionsMock); windowDi.override(requestHelmChartValuesInjectable, () => requestHelmChartValuesMock); windowDi.override(requestCreateHelmReleaseInjectable, () => jest.fn()); - // TODO: Replace store mocking with mock for the actual side-effect (where the namespaces are coming from) - windowDi.override( - namespaceStoreInjectable, - () => - ({ - contextNamespaces: [], - items: [ - { getName: () => "default" }, - { getName: () => "some-other-namespace" }, - ], - selectNamespaces: () => {}, - } as unknown as NamespaceStore), - ); - windowDi.override(getRandomInstallChartTabIdInjectable, () => jest .fn(() => "some-irrelevant-tab-id") @@ -106,8 +87,6 @@ describe("installing helm chart from previously opened tab", () => { }); rendered = await builder.render(); - - await storagesAreReady(); }); it("renders", () => { diff --git a/src/features/helm-charts/upgrade-chart/__snapshots__/upgrade-chart-new-tab.test.ts.snap b/src/features/helm-charts/upgrade-chart/__snapshots__/upgrade-chart-new-tab.test.ts.snap index 95757a0756..0aaff6db77 100644 --- a/src/features/helm-charts/upgrade-chart/__snapshots__/upgrade-chart-new-tab.test.ts.snap +++ b/src/features/helm-charts/upgrade-chart/__snapshots__/upgrade-chart-new-tab.test.ts.snap @@ -3,6 +3,44 @@ exports[`New Upgrade Helm Chart Dock Tab given a namespace is selected when navigating to the helm releases view renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -665,6 +703,44 @@ exports[`New Upgrade Helm Chart Dock Tab given a namespace is selected when navi exports[`New Upgrade Helm Chart Dock Tab given a namespace is selected when navigating to the helm releases view when helm releases resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1424,6 +1500,44 @@ exports[`New Upgrade Helm Chart Dock Tab given a namespace is selected when navi exports[`New Upgrade Helm Chart Dock Tab given a namespace is selected when navigating to the helm releases view when helm releases resolves when clicking the menu for a helm release renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/helm-charts/upgrade-chart/upgrade-chart-new-tab.test.ts b/src/features/helm-charts/upgrade-chart/upgrade-chart-new-tab.test.ts index c58cf24ba2..7d46856fec 100644 --- a/src/features/helm-charts/upgrade-chart/upgrade-chart-new-tab.test.ts +++ b/src/features/helm-charts/upgrade-chart/upgrade-chart-new-tab.test.ts @@ -17,7 +17,7 @@ import type { RequestHelmReleaseConfiguration } from "../../../common/k8s-api/en import requestHelmReleaseConfigurationInjectable from "../../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable"; import type { RequestHelmReleases } from "../../../common/k8s-api/endpoints/helm-releases.api/request-releases.injectable"; import requestHelmReleasesInjectable from "../../../common/k8s-api/endpoints/helm-releases.api/request-releases.injectable"; -import { advanceFakeTime, useFakeTime } from "../../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../../common/test-utils/use-fake-time"; import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable"; import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; @@ -51,7 +51,7 @@ describe("New Upgrade Helm Chart Dock Tab", () => { navigateToHelmReleases = windowDi.inject(navigateToHelmReleasesInjectable); }); - useFakeTime("2020-01-12 12:00:00"); + testUsingFakeTime("2020-01-12 12:00:00"); builder.namespaces.add("my-first-namespace"); builder.namespaces.add("my-second-namespace"); diff --git a/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap b/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap index 9287419673..0a6f6a752a 100644 --- a/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap +++ b/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap @@ -3,6 +3,44 @@ exports[`showing details for helm release given application is started when navigating to helm releases renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -746,6 +784,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve but there is none, renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -1494,6 +1570,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -2420,6 +2534,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -3407,6 +3559,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -4394,6 +4584,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -5626,6 +5854,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when changing the configuration renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -6858,6 +7124,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when changing the configuration when saving renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -8090,6 +8394,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when changing the configuration when saving when requestHelmCharts resolves when requestHelmChartVersions resolves when update resolves with failure renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -9139,6 +9481,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when changing the configuration when saving when requestHelmCharts resolves when requestHelmChartVersions resolves when update resolves with success renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -10190,6 +10570,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when changing the configuration when toggling to see only user defined values when configuration resolves renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -11422,6 +11840,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolve with release when configuration resolves when selecting to upgrade chart renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -12409,6 +12865,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when call for release resolves with error renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -13399,6 +13893,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when details is closed renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -14325,6 +14857,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when details is closed when opening details for same release renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -15312,6 +15882,44 @@ exports[`showing details for helm release given application is started when navi exports[`showing details for helm release given application is started when navigating to helm releases when releases resolve when selecting release to see details when opening details for second release renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
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 b743ce013a..9294b3057e 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 @@ -15,7 +15,7 @@ import type { RequestHelmReleaseConfiguration } from "../../common/k8s-api/endpo import requestHelmReleaseConfigurationInjectable from "../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable"; import type { RequestHelmReleaseUpdate } from "../../common/k8s-api/endpoints/helm-releases.api/request-update.injectable"; import requestHelmReleaseUpdateInjectable from "../../common/k8s-api/endpoints/helm-releases.api/request-update.injectable"; -import { useFakeTime } from "../../common/test-utils/use-fake-time"; +import { testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import type { RequestDetailedHelmRelease } from "../../renderer/components/+helm-releases/release-details/release-details-model/request-detailed-helm-release.injectable"; import requestDetailedHelmReleaseInjectable from "../../renderer/components/+helm-releases/release-details/release-details-model/request-detailed-helm-release.injectable"; import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable"; @@ -45,7 +45,7 @@ describe("showing details for helm release", () => { let showCheckedErrorNotificationMock: jest.Mock; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/pod-logs/__snapshots__/download-logs.test.tsx.snap b/src/features/pod-logs/__snapshots__/download-logs.test.tsx.snap index b9bec84de6..c6f0631b90 100644 --- a/src/features/pod-logs/__snapshots__/download-logs.test.tsx.snap +++ b/src/features/pod-logs/__snapshots__/download-logs.test.tsx.snap @@ -3,6 +3,44 @@ exports[`download logs options in logs dock tab opening pod logs when logs available renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
@@ -858,6 +896,44 @@ exports[`download logs options in logs dock tab opening pod logs when logs avail exports[`download logs options in logs dock tab opening pod logs when logs not available renders 1`] = `
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
diff --git a/src/features/preferences/__snapshots__/navigation-to-kubernetes-preferences.test.ts.snap b/src/features/preferences/__snapshots__/navigation-to-kubernetes-preferences.test.ts.snap index b3d9e24a3a..b796e7b0b3 100644 --- a/src/features/preferences/__snapshots__/navigation-to-kubernetes-preferences.test.ts.snap +++ b/src/features/preferences/__snapshots__/navigation-to-kubernetes-preferences.test.ts.snap @@ -1060,7 +1060,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences > @@ -1099,7 +1099,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences diff --git a/src/features/preferences/closing-preferences.test.tsx b/src/features/preferences/closing-preferences.test.tsx index 7f979969aa..7d30400589 100644 --- a/src/features/preferences/closing-preferences.test.tsx +++ b/src/features/preferences/closing-preferences.test.tsx @@ -13,12 +13,12 @@ import { computed, runInAction } from "mobx"; import React from "react"; import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token"; import observableHistoryInjectable from "../../renderer/navigation/observable-history.injectable"; -import { searchParamsOptions } from "../../renderer/navigation"; import { createMemoryHistory } from "history"; import { createObservableHistory } from "mobx-observable-history"; import navigateToFrontPageInjectable from "../../common/front-end-routing/navigate-to-front-page.injectable"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; import { preferenceItemInjectionToken } from "./renderer/preference-items/preference-item-injection-token"; +import { searchParamsOptions } from "../../renderer/navigation/search-params"; describe("preferences - closing-preferences", () => { let builder: ApplicationBuilder; diff --git a/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts b/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts index 654b361eb5..a7a6e4ae60 100644 --- a/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts +++ b/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts @@ -9,7 +9,7 @@ import type { ClusterManager } from "../../main/cluster/manager"; import exitAppInjectable from "../../main/electron-app/features/exit-app.injectable"; import clusterManagerInjectable from "../../main/cluster/manager.injectable"; import stopServicesAndExitAppInjectable from "../../main/stop-services-and-exit-app.injectable"; -import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; describe("quitting the app using application menu", () => { describe("given application has started", () => { @@ -18,7 +18,7 @@ describe("quitting the app using application menu", () => { let exitAppMock: jest.Mock; beforeEach(async () => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); builder = getApplicationBuilder(); diff --git a/src/features/shell-sync/main/compute-shell-environment.global-override-for-injectable.ts b/src/features/shell-sync/main/compute-shell-environment.global-override-for-injectable.ts new file mode 100644 index 0000000000..b4c9051729 --- /dev/null +++ b/src/features/shell-sync/main/compute-shell-environment.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 computeShellEnvironmentInjectable from "./compute-shell-environment.injectable"; + +export default getGlobalOverride(computeShellEnvironmentInjectable, () => async () => ({ + callWasSuccessful: true, +})); diff --git a/src/features/shell-sync/main/setup-shell.injectable.ts b/src/features/shell-sync/main/setup-shell.injectable.ts index a534411a1f..82d623ec28 100644 --- a/src/features/shell-sync/main/setup-shell.injectable.ts +++ b/src/features/shell-sync/main/setup-shell.injectable.ts @@ -68,7 +68,6 @@ const setupShellInjectable = getInjectable({ }, injectionToken: onLoadOfApplicationInjectionToken, - causesSideEffects: true, }); export default setupShellInjectable; diff --git a/src/jest.setup.ts b/src/jest.setup.ts index 1e7d0a8ce3..4e9b86ba22 100644 --- a/src/jest.setup.ts +++ b/src/jest.setup.ts @@ -10,6 +10,16 @@ import glob from "glob"; import path from "path"; import { enableMapSet, setAutoFreeze } from "immer"; +declare global { + interface InjectablePaths { + paths: string[]; + globalOverridePaths: string[]; + } + + // eslint-disable-next-line no-var + var injectablePaths: Record<"main" | "renderer", InjectablePaths>; +} + configure({ // Needed because we want to use jest.spyOn() // ref https://github.com/mobxjs/mobx/issues/2784 @@ -57,7 +67,13 @@ const getInjectables = (environment: "renderer" | "main", filePathGlob: string) }), ].map(x => path.resolve(__dirname, x)); -(global as any).rendererInjectablePaths = getInjectables("renderer", "*.{injectable,injectable.testing-env}.{ts,tsx}"); -(global as any).rendererGlobalOverridePaths = getInjectables("renderer", "*.global-override-for-injectable.{ts,tsx}"); -(global as any).mainInjectablePaths = getInjectables("main", "*.{injectable,injectable.testing-env}.{ts,tsx}"); -(global as any).mainGlobalOverridePaths = getInjectables("main", "*.global-override-for-injectable.{ts,tsx}"); +global.injectablePaths = { + renderer: { + globalOverridePaths: getInjectables("renderer", "*.global-override-for-injectable.{ts,tsx}"), + paths: getInjectables("renderer", "*.{injectable,injectable.testing-env}.{ts,tsx}"), + }, + main: { + globalOverridePaths: getInjectables("main", "*.global-override-for-injectable.{ts,tsx}"), + paths: getInjectables("main", "*.{injectable,injectable.testing-env}.{ts,tsx}"), + }, +}; diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts index d9e48fb3d9..be19790a23 100644 --- a/src/main/__test__/cluster.test.ts +++ b/src/main/__test__/cluster.test.ts @@ -3,7 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable"; -import { Console } from "console"; import type { Cluster } from "../../common/cluster/cluster"; import { Kubectl } from "../kubectl/kubectl"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; @@ -25,8 +24,6 @@ import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable"; import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable"; -console = new Console(process.stdout, process.stderr); // fix mockFS - describe("create clusters", () => { let cluster: Cluster; let createCluster: CreateCluster; diff --git a/src/main/__test__/kube-auth-proxy.test.ts b/src/main/__test__/kube-auth-proxy.test.ts index d667ad93c3..c37a7f254d 100644 --- a/src/main/__test__/kube-auth-proxy.test.ts +++ b/src/main/__test__/kube-auth-proxy.test.ts @@ -12,28 +12,21 @@ import type { DeepMockProxy } from "jest-mock-extended"; import { mockDeep, mock } from "jest-mock-extended"; import type { Readable } from "stream"; import { EventEmitter } from "stream"; -import { Console } from "console"; -import { stdout, stderr } from "process"; -import mockFs from "mock-fs"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import type { CreateCluster } from "../../common/cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token"; -import path from "path"; import spawnInjectable from "../child-process/spawn.injectable"; -import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable"; import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable"; import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable"; import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable"; -import pathExistsInjectable from "../../common/fs/path-exists.injectable"; -import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable"; import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable"; -import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable"; - -console = new Console(stdout, stderr); +import ensureDirInjectable from "../../common/fs/ensure-dir.injectable"; +import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; +import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; const clusterServerUrl = "https://192.168.64.3:8443"; @@ -43,44 +36,42 @@ describe("kube auth proxy tests", () => { let spawnMock: jest.Mock; let waitUntilPortIsUsedMock: jest.Mock; let broadcastMessageMock: jest.Mock; + let getBasenameOfPath: GetBasenameOfPath; beforeEach(async () => { - jest.clearAllMocks(); - - const mockMinikubeConfig = { - "minikube-config.yml": JSON.stringify({ - apiVersion: "v1", - clusters: [{ - name: "minikube", - cluster: { - server: clusterServerUrl, - }, - }], - "current-context": "minikube", - contexts: [{ - context: { - cluster: "minikube", - user: "minikube", - }, - name: "minikube", - }], - users: [{ - name: "minikube", - }], - kind: "Config", - preferences: {}, - }), - "tmp": {}, - }; - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - di.override(directoryForTempInjectable, () => "some-directory-for-temp"); - di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); }); - di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); }); - di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); }); - di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); }); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); + di.override(directoryForTempInjectable, () => "/some-directory-for-temp"); + + const writeJsonSync = di.inject(writeJsonSyncInjectable); + const ensureDir = di.inject(ensureDirInjectable); + + getBasenameOfPath = di.inject(getBasenameOfPathInjectable); + + writeJsonSync("/minikube-config.yml", { + apiVersion: "v1", + clusters: [{ + name: "minikube", + cluster: { + server: clusterServerUrl, + }, + }], + "current-context": "minikube", + contexts: [{ + context: { + cluster: "minikube", + user: "minikube", + }, + name: "minikube", + }], + users: [{ + name: "minikube", + }], + kind: "Config", + preferences: {}, + }); + await ensureDir("/tmp"); spawnMock = jest.fn(); di.override(spawnInjectable, () => spawnMock); @@ -95,19 +86,10 @@ describe("kube auth proxy tests", () => { di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); di.override(normalizedPlatformInjectable, () => "darwin"); - di.permitSideEffects(getConfigurationFileModelInjectable); - - mockFs(mockMinikubeConfig); - createCluster = di.inject(createClusterInjectionToken); - createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); }); - afterEach(() => { - mockFs.restore(); - }); - it("calling exit multiple times shouldn't throw", async () => { const cluster = createCluster({ id: "foobar", @@ -197,7 +179,7 @@ describe("kube auth proxy tests", () => { return stdout; }); spawnMock.mockImplementationOnce((command: string): ChildProcess => { - expect(path.basename(command).split(".")[0]).toBe("lens-k8s-proxy"); + expect(getBasenameOfPath(command).split(".")[0]).toBe("lens-k8s-proxy"); return mockedCP; }); diff --git a/src/main/__test__/static-file-route.test.ts b/src/main/__test__/static-file-route.test.ts index 4880009017..abbff48a4e 100644 --- a/src/main/__test__/static-file-route.test.ts +++ b/src/main/__test__/static-file-route.test.ts @@ -7,22 +7,6 @@ import type { LensApiRequest, Route } from "../router/route"; import staticFileRouteInjectable from "../routes/files/static-file-route.injectable"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; -jest.mock("electron", () => ({ - app: { - getVersion: () => "99.99.99", - getName: () => "lens", - setName: jest.fn(), - setPath: jest.fn(), - getPath: () => "tmp", - getLocale: () => "en", - setLoginItemSettings: jest.fn(), - }, - ipcMain: { - on: jest.fn(), - handle: jest.fn(), - }, -})); - describe("static-file-route", () => { let handleStaticFileRoute: Route; diff --git a/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.global-override-for-injectable.ts b/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.global-override-for-injectable.ts new file mode 100644 index 0000000000..f4e7b5674e --- /dev/null +++ b/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * 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 directoryForIntegrationTestingInjectable from "./directory-for-integration-testing.injectable"; + +export default getGlobalOverride(directoryForIntegrationTestingInjectable, () => undefined); diff --git a/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable.ts b/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable.ts index 73647a3270..7a98699fee 100644 --- a/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable.ts +++ b/src/main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable.ts @@ -3,16 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import environmentVariablesInjectable from "../../../common/utils/environment-variables.injectable"; const directoryForIntegrationTestingInjectable = getInjectable({ id: "directory-for-integration-testing", - - instantiate: (di) => { - const environmentVariables = di.inject(environmentVariablesInjectable); - - return environmentVariables.CICD; - }, + instantiate: () => process.env.CICD, + causesSideEffects: true, }); export default directoryForIntegrationTestingInjectable; diff --git a/src/main/app-paths/get-electron-app-path/get-electron-app-path.injectable.ts b/src/main/app-paths/get-electron-app-path/get-electron-app-path.injectable.ts index 057c22c860..b4423f5367 100644 --- a/src/main/app-paths/get-electron-app-path/get-electron-app-path.injectable.ts +++ b/src/main/app-paths/get-electron-app-path/get-electron-app-path.injectable.ts @@ -3,15 +3,29 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import type { PathName } from "../../../common/app-paths/app-path-names"; import electronAppInjectable from "../../electron-app/electron-app.injectable"; -import { getElectronAppPath } from "./get-electron-app-path"; + +export type GetElectronAppPath = (name: PathName | "currentApp") => string; const getElectronAppPathInjectable = getInjectable({ id: "get-electron-app-path", - instantiate: (di) => getElectronAppPath({ - app: di.inject(electronAppInjectable), - }), + instantiate: (di): GetElectronAppPath => { + const electronApp = di.inject(electronAppInjectable); + + return (name) => { + try { + if (name === "currentApp") { + return electronApp.getAppPath(); + } + + return electronApp.getPath(name); + } catch (e) { + return ""; + } + }; + }, }); export default getElectronAppPathInjectable; diff --git a/src/main/app-paths/get-electron-app-path/get-electron-app-path.ts b/src/main/app-paths/get-electron-app-path/get-electron-app-path.ts deleted file mode 100644 index 45a6058d61..0000000000 --- a/src/main/app-paths/get-electron-app-path/get-electron-app-path.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { App } from "electron"; -import type { PathName } from "../../../common/app-paths/app-path-names"; - -interface Dependencies { - app: App; -} - -export type GetElectronAppPath = (name: PathName | "currentApp") => string; - -export const getElectronAppPath = ({ - app, -}: Dependencies): GetElectronAppPath => ( - (name) => { - try { - if (name === "currentApp") { - return app.getAppPath(); - } - - return app.getPath(name); - } catch (e) { - return ""; - } - } -); diff --git a/src/main/app-paths/set-electron-app-path/set-electron-app-path.injectable.ts b/src/main/app-paths/set-electron-app-path/set-electron-app-path.injectable.ts index 73090956d3..2a4bac2330 100644 --- a/src/main/app-paths/set-electron-app-path/set-electron-app-path.injectable.ts +++ b/src/main/app-paths/set-electron-app-path/set-electron-app-path.injectable.ts @@ -6,11 +6,16 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { PathName } from "../../../common/app-paths/app-path-names"; import electronAppInjectable from "../../electron-app/electron-app.injectable"; +export type SetElectronAppPath = (name: PathName, path: string) => void; + const setElectronAppPathInjectable = getInjectable({ id: "set-electron-app-path", - instantiate: (di) => (name: PathName, path: string) : void => - di.inject(electronAppInjectable).setPath(name, path), + instantiate: (di): SetElectronAppPath => { + const electronApp = di.inject(electronAppInjectable); + + return (name, path) => electronApp.setPath(name, path); + }, }); export default setElectronAppPathInjectable; diff --git a/src/main/app-paths/setup-app-paths.injectable.ts b/src/main/app-paths/setup-app-paths.injectable.ts index c48b35fba9..0b3c67b60f 100644 --- a/src/main/app-paths/setup-app-paths.injectable.ts +++ b/src/main/app-paths/setup-app-paths.injectable.ts @@ -21,7 +21,7 @@ const setupAppPathsInjectable = getInjectable({ instantiate: (di) => { const setElectronAppPath = di.inject(setElectronAppPathInjectable); const appName = di.inject(appNameInjectable); - const getAppPath = di.inject(getElectronAppPathInjectable); + const getElectronAppPath = di.inject(getElectronAppPathInjectable); const appPathsState = di.inject(appPathsStateInjectable); const directoryForIntegrationTesting = di.inject(directoryForIntegrationTestingInjectable); const joinPaths = di.inject(joinPathsInjectable); @@ -33,13 +33,13 @@ const setupAppPathsInjectable = getInjectable({ setElectronAppPath("appData", directoryForIntegrationTesting); } - const appDataPath = getAppPath("appData"); + const appDataPath = getElectronAppPath("appData"); setElectronAppPath("userData", joinPaths(appDataPath, appName)); const appPaths = pipeline( pathNames, - map(name => [name, getAppPath(name)]), + map(name => [name, getElectronAppPath(name)]), fromPairs, ) as AppPaths; diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts deleted file mode 100644 index 7802a0455d..0000000000 --- a/src/main/catalog-pusher.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { reaction } from "mobx"; -import { broadcastMessage, ipcMainOn } from "../common/ipc"; -import type { CatalogEntityRegistry } from "./catalog"; -import "../common/catalog-entities/kubernetes-cluster"; -import { disposer, toJS } from "../common/utils"; -import { debounce } from "lodash"; -import type { CatalogEntity } from "../common/catalog"; -import { catalogInitChannel, catalogItemsChannel } from "../common/ipc/catalog"; - -const broadcaster = debounce((items: CatalogEntity[]) => { - broadcastMessage(catalogItemsChannel, items); -}, 100, { leading: true, trailing: true }); - -export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) { - broadcaster(toJS(catalog.items)); -} - -export function startCatalogSyncToRenderer(catalog: CatalogEntityRegistry) { - return disposer( - ipcMainOn(catalogInitChannel, () => broadcaster(toJS(catalog.items))), - reaction(() => toJS(catalog.items), (items) => { - broadcaster(items); - }, { - fireImmediately: true, - }), - ); -} diff --git a/src/main/catalog-sync-to-renderer/broadcaster.global-override-for-injectable.ts b/src/main/catalog-sync-to-renderer/broadcaster.global-override-for-injectable.ts new file mode 100644 index 0000000000..fce2e7d2eb --- /dev/null +++ b/src/main/catalog-sync-to-renderer/broadcaster.global-override-for-injectable.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { debounce } from "lodash"; +import { getGlobalOverride } from "../../common/test-utils/get-global-override"; +import catalogSyncBroadcasterInjectable from "./broadcaster.injectable"; + +export default getGlobalOverride(catalogSyncBroadcasterInjectable, () => debounce(() => {})); diff --git a/src/main/catalog-sync-to-renderer/broadcaster.injectable.ts b/src/main/catalog-sync-to-renderer/broadcaster.injectable.ts new file mode 100644 index 0000000000..cab7ff86f0 --- /dev/null +++ b/src/main/catalog-sync-to-renderer/broadcaster.injectable.ts @@ -0,0 +1,29 @@ +/** + * 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 { debounce } from "lodash"; +import type { CatalogEntity } from "../../common/catalog"; +import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable"; +import { catalogItemsChannel } from "../../common/ipc/catalog"; + +const catalogSyncBroadcasterInjectable = getInjectable({ + id: "catalog-sync-broadcaster", + instantiate: (di) => { + const broadcastMessage = di.inject(broadcastMessageInjectable); + + return debounce( + (items: CatalogEntity[]) => { + broadcastMessage(catalogItemsChannel, items); + }, + 100, + { + leading: true, + trailing: true, + }, + ); + }, +}); + +export default catalogSyncBroadcasterInjectable; diff --git a/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts b/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts index a9ba676bf9..6be9826c98 100644 --- a/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts +++ b/src/main/catalog-sync-to-renderer/catalog-sync-to-renderer.injectable.ts @@ -3,23 +3,40 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { startCatalogSyncToRenderer } from "../catalog-pusher"; +import { reaction } from "mobx"; +import { catalogInitChannel } from "../../common/ipc/catalog"; +import { disposer, toJS } from "../../common/utils"; import { getStartableStoppable } from "../../common/utils/get-startable-stoppable"; import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; +import ipcMainInjectable from "../utils/channel/ipc-main/ipc-main.injectable"; +import catalogSyncBroadcasterInjectable from "./broadcaster.injectable"; const catalogSyncToRendererInjectable = getInjectable({ id: "catalog-sync-to-renderer", instantiate: (di) => { const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const ipcMain = di.inject(ipcMainInjectable); + const catalogSyncBroadcaster = di.inject(catalogSyncBroadcasterInjectable); return getStartableStoppable( "catalog-sync", - () => startCatalogSyncToRenderer(catalogEntityRegistry), + () => { + const initChannelHandler = () => catalogSyncBroadcaster(toJS(catalogEntityRegistry.items)); + + ipcMain.on(catalogInitChannel, initChannelHandler); + + return disposer( + () => ipcMain.off(catalogInitChannel, initChannelHandler), + reaction(() => toJS(catalogEntityRegistry.items), (items) => { + catalogSyncBroadcaster(items); + }, { + fireImmediately: true, + }), + ); + }, ); }, - - causesSideEffects: true, }); export default catalogSyncToRendererInjectable; diff --git a/src/main/catalog-sync-to-renderer/push-catalog-to-renderer.injectable.ts b/src/main/catalog-sync-to-renderer/push-catalog-to-renderer.injectable.ts new file mode 100644 index 0000000000..647922fa53 --- /dev/null +++ b/src/main/catalog-sync-to-renderer/push-catalog-to-renderer.injectable.ts @@ -0,0 +1,20 @@ +/** + * 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 { toJS } from "../../common/utils"; +import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; +import catalogSyncBroadcasterInjectable from "./broadcaster.injectable"; + +const pushCatalogToRendererInjectable = getInjectable({ + id: "push-catalog-to-renderer", + instantiate: (di) => { + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const catalogSyncBroadcaster = di.inject(catalogSyncBroadcasterInjectable); + + return () => catalogSyncBroadcaster(toJS(catalogEntityRegistry.items)); + }, +}); + +export default pushCatalogToRendererInjectable; diff --git a/src/main/catalog/catalog-entity-registry.ts b/src/main/catalog/catalog-entity-registry.ts deleted file mode 100644 index 0329a56e74..0000000000 --- a/src/main/catalog/catalog-entity-registry.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import catalogEntityRegistryInjectable from "./entity-registry.injectable"; - -/** - * @deprecated use `di.inject(catalogEntityRegistryInjectable)` instead - */ -export const catalogEntityRegistry = asLegacyGlobalForExtensionApi(catalogEntityRegistryInjectable); diff --git a/src/main/catalog/index.ts b/src/main/catalog/index.ts index 306d26317d..9142b2b824 100644 --- a/src/main/catalog/index.ts +++ b/src/main/catalog/index.ts @@ -3,5 +3,4 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -export * from "./catalog-entity-registry"; export * from "./entity-registry"; diff --git a/src/main/cluster/manager.injectable.ts b/src/main/cluster/manager.injectable.ts index e4c22f0e38..69c1bea47c 100644 --- a/src/main/cluster/manager.injectable.ts +++ b/src/main/cluster/manager.injectable.ts @@ -4,6 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable"; +import loggerInjectable from "../../common/logger.injectable"; import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable"; import { ClusterManager } from "./manager"; @@ -17,6 +18,7 @@ const clusterManagerInjectable = getInjectable({ catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable), visibleCluster: di.inject(visibleClusterInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/main/cluster/manager.ts b/src/main/cluster/manager.ts index 83270941d4..6888a44f5f 100644 --- a/src/main/cluster/manager.ts +++ b/src/main/cluster/manager.ts @@ -4,13 +4,10 @@ */ import "../../common/ipc/cluster"; -import type http from "http"; import type { IObservableValue, ObservableSet } from "mobx"; import { action, makeObservable, observe, reaction, toJS } from "mobx"; import type { Cluster } from "../../common/cluster/cluster"; -import logger from "../logger"; -import { apiKubePrefix } from "../../common/vars"; -import { getClusterIdFromHost, isErrnoException } from "../../common/utils"; +import { isErrnoException } from "../../common/utils"; import type { KubernetesClusterPrometheusMetrics } from "../../common/catalog-entities/kubernetes-cluster"; import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../../common/catalog-entities/kubernetes-cluster"; import { ipcMainOn } from "../../common/ipc"; @@ -18,6 +15,7 @@ import { once } from "lodash"; import type { ClusterStore } from "../../common/cluster-store/cluster-store"; import type { ClusterId } from "../../common/cluster-types"; import type { CatalogEntityRegistry } from "../catalog"; +import type { Logger } from "../../common/logger"; const logPrefix = "[CLUSTER-MANAGER]:"; @@ -28,6 +26,7 @@ interface Dependencies { readonly catalogEntityRegistry: CatalogEntityRegistry; readonly clustersThatAreBeingDeleted: ObservableSet; readonly visibleCluster: IObservableValue; + readonly logger: Logger; } export class ClusterManager { @@ -80,7 +79,7 @@ export class ClusterManager { @action protected updateCatalog(clusters: Cluster[]) { - logger.debug("[CLUSTER-MANAGER]: updating catalog from cluster store"); + this.dependencies.logger.debug("[CLUSTER-MANAGER]: updating catalog from cluster store"); for (const cluster of clusters) { this.updateEntityFromCluster(cluster); @@ -146,31 +145,31 @@ export class ClusterManager { } else { entity.status.phase = (() => { if (!cluster) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`); return LensKubernetesClusterStatus.DISCONNECTED; } if (cluster.accessible) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`); return LensKubernetesClusterStatus.CONNECTED; } if (!cluster.disconnected) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`); return LensKubernetesClusterStatus.CONNECTING; } // Extensions are not allowed to use the Lens specific status phases if (!lensSpecificClusterStatuses.has(entity?.status?.phase)) { - logger.debug(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`); + this.dependencies.logger.debug(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`); return entity.status.phase; } - logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`); return LensKubernetesClusterStatus.DISCONNECTED; })(); @@ -200,9 +199,9 @@ export class ClusterManager { this.dependencies.store.addCluster(model); } catch (error) { if (isErrnoException(error) && error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) { - logger.warn(`${logPrefix} kubeconfig file disappeared`, model); + this.dependencies.logger.warn(`${logPrefix} kubeconfig file disappeared`, model); } else { - logger.error(`${logPrefix} failed to add cluster: ${error}`, model); + this.dependencies.logger.error(`${logPrefix} failed to add cluster: ${error}`, model); } } } else { @@ -235,7 +234,7 @@ export class ClusterManager { } protected onNetworkOffline = () => { - logger.info(`${logPrefix} network is offline`); + this.dependencies.logger.info(`${logPrefix} network is offline`); this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.online = false; @@ -246,7 +245,7 @@ export class ClusterManager { }; protected onNetworkOnline = () => { - logger.info(`${logPrefix} network is online`); + this.dependencies.logger.info(`${logPrefix} network is online`); this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.refreshConnectionStatus().catch((e) => e); @@ -259,27 +258,6 @@ export class ClusterManager { cluster.disconnect(); }); } - - getClusterForRequest = (req: http.IncomingMessage): Cluster | undefined => { - if (!req.headers.host) { - return undefined; - } - - // lens-server is connecting to 127.0.0.1:/ - if (req.url && req.headers.host.startsWith("127.0.0.1")) { - const clusterId = req.url.split("/")[1]; - const cluster = this.dependencies.store.getById(clusterId); - - if (cluster) { - // we need to swap path prefix so that request is proxied to kube api - req.url = req.url.replace(`/${clusterId}`, apiKubePrefix); - } - - return cluster; - } - - return this.dependencies.store.getById(getClusterIdFromHost(req.headers.host)); - }; } export function catalogEntityFromCluster(cluster: Cluster) { diff --git a/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts b/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts index 410bb23457..9b8bb83e2b 100644 --- a/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts +++ b/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts @@ -7,7 +7,7 @@ // convert file path cluster icons to their base64 encoded versions import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; import type { ClusterModel } from "../../../common/cluster-types"; import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable"; import { loadConfigFromString } from "../../../common/kube-helpers"; @@ -27,7 +27,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({ id: "v3.6.0-beta.1-cluster-store-migration", instantiate: (di) => { const userDataPath = di.inject(directoryForUserDataInjectable); - const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable); + const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigFilePathInjectable); const readFileSync = di.inject(readFileSyncInjectable); const readFileBufferSync = di.inject(readFileBufferSyncInjectable); const joinPaths = di.inject(joinPathsInjectable); @@ -44,8 +44,8 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({ for (const clusterModel of storedClusters) { /** - * migrate kubeconfig - */ + * migrate kubeconfig + */ try { const absPath = getCustomKubeConfigDirectory(clusterModel.id); @@ -80,6 +80,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({ delete clusterModel.preferences?.icon; } } catch (error) { + console.log(error); logger.info(`Failed to migrate cluster icon for cluster "${clusterModel.id}"`, error); delete clusterModel.preferences?.icon; } diff --git a/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts b/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts index 51309cebb9..0199e57c97 100644 --- a/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts +++ b/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts @@ -49,10 +49,17 @@ const v500Beta10ClusterStoreMigrationInjectable = getInjectable({ store.set("clusters", clusters); } catch (error) { - if (isErrnoException(error) && !(error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json"))) { - // ignore lens-workspace-store.json being missing - throw error; + // KLUDGE: remove after https://github.com/streamich/memfs/pull/893 is released + if (process.env.JEST_WORKER_ID && (error as any).code === "ENOENT") { + return; } + + if (isErrnoException(error) && error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json")) { + // ignore lens-workspace-store.json being missing + return; + } + + throw error; } }, }; diff --git a/src/main/electron-app/electron-app.global-override-for-injectable.ts b/src/main/electron-app/electron-app.global-override-for-injectable.ts index c7cc065cb2..e225745ca3 100644 --- a/src/main/electron-app/electron-app.global-override-for-injectable.ts +++ b/src/main/electron-app/electron-app.global-override-for-injectable.ts @@ -3,17 +3,44 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { kebabCase } from "lodash"; import { getGlobalOverride } from "../../common/test-utils/get-global-override"; import electronAppInjectable from "./electron-app.injectable"; -export default getGlobalOverride(electronAppInjectable, () => ({ - getVersion: () => "6.0.0", - setLoginItemSettings: () => {}, - commandLine: { - appendArgument: () => {}, - appendSwitch: () => {}, - getSwitchValue: () => "", - hasSwitch: () => false, - removeSwitch: () => {}, - }, -} as Partial as Electron.App)); +export default getGlobalOverride(electronAppInjectable, () => { + const commandLineArgs: string[] = []; + const chromiumArgs = new Map(); + const appPaths = new Map(); + + const app = ({ + getVersion: () => "6.0.0", + setLoginItemSettings: () => { }, + on: () => app, + whenReady: async () => {}, + getPath: (name) => appPaths.get(name) ?? `/some-directory-for-${kebabCase(name)}`, + setPath: (name, value) => appPaths.set(name, value), + getAppPath: () => "/some-path-to-the-applcation-binary", + focus: () => {}, + commandLine: { + appendArgument: (value) => commandLineArgs.push(value), + appendSwitch: (key, value) => chromiumArgs.set(key, value), + getSwitchValue: (key) => chromiumArgs.get(key), + hasSwitch: (key) => chromiumArgs.has(key), + removeSwitch: (key) => chromiumArgs.delete(key), + }, + disableHardwareAcceleration: () => {}, + requestSingleInstanceLock: () => true, + getLoginItemSettings: () => ({ + executableWillLaunchAtLogin: false, + openAtLogin: false, + openAsHidden: false, + wasOpenedAtLogin: false, + wasOpenedAsHidden: false, + restoreState: false, + launchItems: [], + }), + exit: () => {}, + } as Partial as Electron.App); + + return app; +}); diff --git a/src/main/electron-app/features/electron-dialog.global-override-for-injectable.ts b/src/main/electron-app/features/electron-dialog.global-override-for-injectable.ts new file mode 100644 index 0000000000..8ccafcfe7a --- /dev/null +++ b/src/main/electron-app/features/electron-dialog.global-override-for-injectable.ts @@ -0,0 +1,26 @@ +/** + * 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 electronDialogInjectable from "./electron-dialog.injectable"; + +export default getGlobalOverride(electronDialogInjectable, () => ({ + showCertificateTrustDialog: async () => {}, + showErrorBox: () => {}, + showMessageBox: async () => ({ + checkboxChecked: false, + response: 0, + }), + showMessageBoxSync: () => 0, + showOpenDialog: async () => ({ + canceled: true, + filePaths: [], + }), + showOpenDialogSync: () => [], + showSaveDialog: async () => ({ + canceled: true, + }), + showSaveDialogSync: () => "", +})); diff --git a/src/main/electron-app/features/focus-application.injectable.ts b/src/main/electron-app/features/focus-application.injectable.ts index 4d3afd146f..3e4c4c1b5c 100644 --- a/src/main/electron-app/features/focus-application.injectable.ts +++ b/src/main/electron-app/features/focus-application.injectable.ts @@ -15,8 +15,6 @@ const focusApplicationInjectable = getInjectable({ electronApp.focus({ steal: true }); }; }, - - causesSideEffects: true, }); export default focusApplicationInjectable; diff --git a/src/main/electron-app/features/native-theme.global-override-for-injectable.ts b/src/main/electron-app/features/native-theme.global-override-for-injectable.ts new file mode 100644 index 0000000000..47c957c722 --- /dev/null +++ b/src/main/electron-app/features/native-theme.global-override-for-injectable.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import EventEmitter from "events"; +import { getGlobalOverride } from "../../../common/test-utils/get-global-override"; +import nativeThemeInjectable from "./native-theme.injectable"; + +export default getGlobalOverride(nativeThemeInjectable, () => Object.assign(new EventEmitter(), { + shouldUseDarkColors: true, + inForcedColorsMode: true, + shouldUseHighContrastColors: false, + shouldUseInvertedColorScheme: false, + themeSource: "dark" as const, +})); diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index d3a604177d..b8a869f7fe 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -7,10 +7,10 @@ import { setupIpcMainHandlers } from "./setup-ipc-main-handlers"; import loggerInjectable from "../../../../common/logger.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token"; -import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable"; import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable"; import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import pushCatalogToRendererInjectable from "../../../catalog-sync-to-renderer/push-catalog-to-renderer.injectable"; const setupIpcMainHandlersInjectable = getInjectable({ id: "setup-ipc-main-handlers", @@ -18,7 +18,7 @@ const setupIpcMainHandlersInjectable = getInjectable({ instantiate: (di) => { const logger = di.inject(loggerInjectable); const applicationMenuItemComposite = di.inject(applicationMenuItemCompositeInjectable); - const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const pushCatalogToRenderer = di.inject(pushCatalogToRendererInjectable); const clusterStore = di.inject(clusterStoreInjectable); const emitAppEvent = di.inject(emitAppEventInjectable); const getClusterById = di.inject(getClusterByIdInjectable); @@ -30,7 +30,7 @@ const setupIpcMainHandlersInjectable = getInjectable({ setupIpcMainHandlers({ applicationMenuItemComposite, - catalogEntityRegistry, + pushCatalogToRenderer, clusterStore, emitAppEvent, getClusterById, diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index ea079cd42f..4ae020c42d 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -9,8 +9,6 @@ import { clusterActivateHandler, clusterSetFrameIdHandler, clusterDisconnectHand import type { ClusterId } from "../../../../common/cluster-types"; import type { ClusterStore } from "../../../../common/cluster-store/cluster-store"; import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc"; -import type { CatalogEntityRegistry } from "../../../catalog"; -import { pushCatalogToRenderer } from "../../../catalog-pusher"; import type { IComputedValue } from "mobx"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; @@ -22,18 +20,18 @@ import type { EmitAppEvent } from "../../../../common/app-event-bus/emit-event.i import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; interface Dependencies { applicationMenuItemComposite: IComputedValue>; - catalogEntityRegistry: CatalogEntityRegistry; clusterStore: ClusterStore; emitAppEvent: EmitAppEvent; getClusterById: GetClusterById; + pushCatalogToRenderer: () => void; } export const setupIpcMainHandlers = ({ applicationMenuItemComposite, - catalogEntityRegistry, clusterStore, emitAppEvent, getClusterById, + pushCatalogToRenderer, }: Dependencies) => { ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { return getClusterById(clusterId) @@ -45,8 +43,7 @@ export const setupIpcMainHandlers = ({ if (cluster) { clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId }); - - pushCatalogToRenderer(catalogEntityRegistry); + pushCatalogToRenderer(); } }); diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 51c99b9ca3..1698997e36 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -3,76 +3,35 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { kebabCase, noop, chunk } from "lodash/fp"; +import { chunk } from "lodash/fp"; import type { DiContainer, Injectable } from "@ogre-tools/injectable"; import { createContainer, isInjectable, getInjectable } from "@ogre-tools/injectable"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import writeJsonFileInjectable from "../common/fs/write-json-file.injectable"; -import readJsonFileInjectable from "../common/fs/read-json-file.injectable"; -import readFileInjectable from "../common/fs/read-file.injectable"; -import loggerInjectable from "../common/logger.injectable"; import spawnInjectable from "./child-process/spawn.injectable"; -import commandLineArgumentsInjectable from "./utils/command-line-arguments.injectable"; import initializeExtensionsInjectable from "./start-main-application/runnables/initialize-extensions.injectable"; -import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable"; -import environmentVariablesInjectable from "../common/utils/environment-variables.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable"; -import setupShellInjectable from "../features/shell-sync/main/setup-shell.injectable"; import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable"; -import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable"; -import isDevelopmentInjectable from "../common/vars/is-development.injectable"; -import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable"; import setupDeepLinkingInjectable from "./electron-app/runnables/setup-deep-linking.injectable"; -import exitAppInjectable from "./electron-app/features/exit-app.injectable"; -import getCommandLineSwitchInjectable from "./electron-app/features/get-command-line-switch.injectable"; -import requestSingleInstanceLockInjectable from "./electron-app/features/request-single-instance-lock.injectable"; -import disableHardwareAccelerationInjectable from "./electron-app/features/disable-hardware-acceleration.injectable"; -import shouldStartHiddenInjectable from "./electron-app/features/should-start-hidden.injectable"; -import getElectronAppPathInjectable from "./app-paths/get-electron-app-path/get-electron-app-path.injectable"; -import setElectronAppPathInjectable from "./app-paths/set-electron-app-path/set-electron-app-path.injectable"; import setupMainWindowVisibilityAfterActivationInjectable from "./electron-app/runnables/setup-main-window-visibility-after-activation.injectable"; import setupDeviceShutdownInjectable from "./electron-app/runnables/setup-device-shutdown.injectable"; import setupApplicationNameInjectable from "./electron-app/runnables/setup-application-name.injectable"; import setupRunnablesBeforeClosingOfApplicationInjectable from "./electron-app/runnables/setup-runnables-before-closing-of-application.injectable"; -import showMessagePopupInjectable from "./electron-app/features/show-message-popup.injectable"; import clusterFramesInjectable from "../common/cluster-frames.injectable"; import type { ClusterFrameInfo } from "../common/cluster-frames"; import { observable, runInAction } from "mobx"; -import waitForElectronToBeReadyInjectable from "./electron-app/features/wait-for-electron-to-be-ready.injectable"; -import setupRunnablesAfterWindowIsOpenedInjectable from "./electron-app/runnables/setup-runnables-after-window-is-opened.injectable"; import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable"; -import getElectronThemeInjectable from "./electron-app/features/get-electron-theme.injectable"; -import syncThemeFromOperatingSystemInjectable from "./electron-app/features/sync-theme-from-operating-system.injectable"; -import platformInjectable from "../common/vars/platform.injectable"; import electronQuitAndInstallUpdateInjectable from "./electron-app/features/electron-quit-and-install-update.injectable"; import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable"; -import baseBundledBinariesDirectoryInjectable from "../common/vars/base-bundled-binaries-dir.injectable"; import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-quit.injectable"; -import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable"; -import startKubeConfigSyncInjectable from "./start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable"; -import getRandomIdInjectable from "../common/utils/get-random-id.injectable"; -import execFileInjectable from "../common/fs/exec-file.injectable"; -import normalizedPlatformArchitectureInjectable from "../common/vars/normalized-platform-architecture.injectable"; -import getHelmChartVersionsInjectable from "./helm/helm-service/get-helm-chart-versions.injectable"; -import getHelmChartValuesInjectable from "./helm/helm-service/get-helm-chart-values.injectable"; -import listHelmChartsInjectable from "./helm/helm-service/list-helm-charts.injectable"; -import deleteHelmReleaseInjectable from "./helm/helm-service/delete-helm-release.injectable"; -import getHelmReleaseHistoryInjectable from "./helm/helm-service/get-helm-release-history.injectable"; -import getHelmReleaseValuesInjectable from "./helm/helm-service/get-helm-release-values.injectable"; -import installHelmChartInjectable from "./helm/helm-service/install-helm-chart.injectable"; -import listHelmReleasesInjectable from "./helm/helm-service/list-helm-releases.injectable"; -import rollbackHelmReleaseInjectable from "./helm/helm-service/rollback-helm-release.injectable"; import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable"; import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; -import focusApplicationInjectable from "./electron-app/features/focus-application.injectable"; -import kubectlDownloadingNormalizedArchInjectable from "./kubectl/normalized-arch.injectable"; import initializeClusterManagerInjectable from "./cluster/initialize-manager.injectable"; -import addKubeconfigSyncAsEntitySourceInjectable from "./start-main-application/runnables/kube-config-sync/add-source.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; import applicationInformationInjectable from "../common/vars/application-information-injectable"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; +import { getOverrideFsWithFakes } from "../test-utils/override-fs-with-fakes"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { @@ -83,7 +42,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) di.register(getInjectable({ id: "node-env", - instantiate: () => "test", + instantiate: () => "production", injectionToken: nodeEnvInjectionToken, })); @@ -91,13 +50,12 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) di.preventSideEffects(); - const injectables: Injectable[] = ( - global as any - ).mainInjectablePaths.flatMap((filePath: string) => - Object.values(require(filePath)).filter( - (maybeInjectable: any) => isInjectable(maybeInjectable), - ), - ); + const injectables = ( + global.injectablePaths.main.paths + .map(path => require(path)) + .flatMap(Object.values) + .filter(isInjectable) + ) as Injectable[]; runInAction(() => { registerMobX(di); @@ -109,52 +67,24 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) }); if (doGeneralOverrides) { - const globalOverrides: GlobalOverride[] = (global as any).mainGlobalOverridePaths.map( - (filePath: string) => require(filePath).default, - ); + for (const globalOverridePath of global.injectablePaths.main.globalOverridePaths) { + const globalOverride = require(globalOverridePath).default as GlobalOverride; - globalOverrides.forEach(globalOverride => { di.override(globalOverride.injectable, globalOverride.overridingInstantiate); - }); + } di.override(electronInjectable, () => ({})); di.override(waitUntilBundledExtensionsAreLoadedInjectable, () => async () => {}); - di.override(getRandomIdInjectable, () => () => "some-irrelevant-random-id"); - di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); - overrideOperatingSystem(di); overrideRunnablesHavingSideEffects(di); overrideElectronFeatures(di); - - di.override(isDevelopmentInjectable, () => false); - di.override(environmentVariablesInjectable, () => ({})); - di.override(commandLineArgumentsInjectable, () => []); + getOverrideFsWithFakes()(di); di.override(clusterFramesInjectable, () => observable.map()); - di.override(stopServicesAndExitAppInjectable, () => () => {}); - di.override(lensResourcesDirInjectable, () => "/irrelevant"); - - overrideFunctionalInjectables(di, [ - getHelmChartVersionsInjectable, - getHelmChartValuesInjectable, - listHelmChartsInjectable, - deleteHelmReleaseInjectable, - getHelmReleaseHistoryInjectable, - getHelmReleaseValuesInjectable, - installHelmChartInjectable, - listHelmReleasesInjectable, - rollbackHelmReleaseInjectable, - writeJsonFileInjectable, - readJsonFileInjectable, - readFileInjectable, - execFileInjectable, - ]); - di.override(broadcastMessageInjectable, () => (channel) => { throw new Error(`Tried to broadcast message to channel "${channel}" over IPC without explicit override.`); }); - di.override(baseBundledBinariesDirectoryInjectable, () => "some-bin-directory"); di.override(spawnInjectable, () => () => { return { stderr: { on: jest.fn(), removeAllListeners: jest.fn() }, @@ -162,14 +92,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) on: jest.fn(), } as never; }); - - di.override(loggerInjectable, () => ({ - warn: noop, - debug: noop, - error: noop, - info: noop, - silly: noop, - })); } return di; @@ -180,15 +102,9 @@ const overrideRunnablesHavingSideEffects = (di: DiContainer) => { [ initializeExtensionsInjectable, initializeClusterManagerInjectable, - addKubeconfigSyncAsEntitySourceInjectable, setupIpcMainHandlersInjectable, setupLensProxyInjectable, - setupShellInjectable, setupSyncingOfWeblinksInjectable, - setupSystemCaInjectable, - setupRunnablesAfterWindowIsOpenedInjectable, - startCatalogSyncInjectable, - startKubeConfigSyncInjectable, ].forEach((injectable) => { di.override(injectable, () => ({ id: injectable.id, @@ -197,11 +113,6 @@ const overrideRunnablesHavingSideEffects = (di: DiContainer) => { }); }; -const overrideOperatingSystem = (di: DiContainer) => { - di.override(platformInjectable, () => "darwin"); - di.override(normalizedPlatformArchitectureInjectable, () => "arm64"); -}; - const overrideElectronFeatures = (di: DiContainer) => { [ setupMainWindowVisibilityAfterActivationInjectable, @@ -216,32 +127,7 @@ const overrideElectronFeatures = (di: DiContainer) => { })); }); - di.override(exitAppInjectable, () => () => {}); - di.override(getCommandLineSwitchInjectable, () => () => "irrelevant"); - di.override(requestSingleInstanceLockInjectable, () => () => true); - di.override(disableHardwareAccelerationInjectable, () => () => {}); - di.override(shouldStartHiddenInjectable, () => false); - di.override(showMessagePopupInjectable, () => () => {}); - di.override(waitForElectronToBeReadyInjectable, () => () => Promise.resolve()); - di.override(getElectronThemeInjectable, () => () => "dark"); - di.override(syncThemeFromOperatingSystemInjectable, () => ({ start: () => {}, stop: () => {} })); di.override(electronQuitAndInstallUpdateInjectable, () => () => {}); di.override(setUpdateOnQuitInjectable, () => () => {}); - di.override(focusApplicationInjectable, () => () => {}); - - di.override( - getElectronAppPathInjectable, - () => (name: string) => `/some-electron-app-path-for-${kebabCase(name)}`, - ); - - di.override(setElectronAppPathInjectable, () => () => {}); di.override(electronUpdaterIsActiveInjectable, () => false); }; - -const overrideFunctionalInjectables = (di: DiContainer, injectables: Injectable[]) => { - injectables.forEach(injectable => { - di.override(injectable, () => () => { - throw new Error(`Tried to run "${injectable.id}" without explicit override.`); - }); - }); -}; diff --git a/src/main/helm/delete-helm-release.injectable.ts b/src/main/helm/delete-helm-release.injectable.ts new file mode 100644 index 0000000000..a4c08af96f --- /dev/null +++ b/src/main/helm/delete-helm-release.injectable.ts @@ -0,0 +1,37 @@ +/** + * 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 execHelmInjectable from "./exec-helm/exec-helm.injectable"; + +export interface DeleteHelmReleaseData { + name: string; + namespace: string; +} + +export type DeleteHelmRelease = (kubeconfigPath: string, data: DeleteHelmReleaseData) => Promise; + +const deleteHelmReleaseInjectable = getInjectable({ + id: "delete-helm-release", + instantiate: (di): DeleteHelmRelease => { + const execHelm = di.inject(execHelmInjectable); + + return async (kubeconfigPath, { name, namespace }) => { + const result = await execHelm([ + "delete", + name, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]); + + if (result.callWasSuccessful) { + return result.response; + } + + throw result.error; + }; + }, +}); + +export default deleteHelmReleaseInjectable; diff --git a/src/main/helm/get-helm-release-history.injectable.ts b/src/main/helm/get-helm-release-history.injectable.ts new file mode 100644 index 0000000000..5f8f4e5a6e --- /dev/null +++ b/src/main/helm/get-helm-release-history.injectable.ts @@ -0,0 +1,39 @@ +/** + * 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 type { HelmReleaseRevision } from "../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable"; +import execHelmInjectable from "./exec-helm/exec-helm.injectable"; + +export interface GetHelmReleaseHistoryData { + name: string; + namespace: string; +} + +export type GetHelmReleaseHistory = (kubeconfigPath: string, data: GetHelmReleaseHistoryData) => Promise; + +const getHelmReleaseHistoryInjectable = getInjectable({ + id: "get-helm-release-history", + instantiate: (di): GetHelmReleaseHistory => { + const execHelm = di.inject(execHelmInjectable); + + return async (kubeconfigPath, { name, namespace }) => { + const result = await execHelm([ + "history", + name, + "--output", "json", + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]); + + if (result.callWasSuccessful) { + return JSON.parse(result.response); + } + + throw result.error; + }; + }, +}); + +export default getHelmReleaseHistoryInjectable; diff --git a/src/main/helm/get-helm-release-values.injectable.ts b/src/main/helm/get-helm-release-values.injectable.ts new file mode 100644 index 0000000000..1ef74d5820 --- /dev/null +++ b/src/main/helm/get-helm-release-values.injectable.ts @@ -0,0 +1,49 @@ +/** + * 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 execHelmInjectable from "./exec-helm/exec-helm.injectable"; + +export interface GetHelmReleaseValuesData { + name: string; + namespace: string; + all?: boolean; +} + +export type GetHelmReleaseValues = (kubeconfigPath: string, data: GetHelmReleaseValuesData) => Promise; + +const getHelmReleaseValuesInjectable = getInjectable({ + id: "get-helm-release-values", + instantiate: (di): GetHelmReleaseValues => { + const execHelm = di.inject(execHelmInjectable); + + return async (kubeconfigPath, { name, namespace, all = false }) => { + const args = [ + "get", + "values", + name, + ]; + + if (all) { + args.push("--all"); + } + + args.push( + "--output", "yaml", + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ); + + const result = await execHelm(args); + + if (result.callWasSuccessful) { + return result.response; + } + + throw result.error; + }; + }, +}); + +export default getHelmReleaseValuesInjectable; diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts deleted file mode 100644 index 0d98b6ec44..0000000000 --- a/src/main/helm/helm-release-manager.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import tempy from "tempy"; -import fse from "fs-extra"; -import * as yaml from "js-yaml"; -import { toCamelCase } from "../../common/utils/camelCase"; -import type { JsonValue } from "type-fest"; -import { isObject, json } from "../../common/utils"; -import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import execHelmInjectable from "./exec-helm/exec-helm.injectable"; - -const execHelm = asLegacyGlobalFunctionForExtensionApi(execHelmInjectable); - -export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise[]> { - const args = [ - "ls", - "--all", - "--output", "json", - ]; - - if (namespace) { - args.push("-n", namespace); - } else { - args.push("--all-namespaces"); - } - - args.push("--kubeconfig", pathToKubeconfig); - - const result = await execHelm(args); - - if (!result.callWasSuccessful) { - throw result.error; - } - - const output = json.parse(result.response); - - if (!Array.isArray(output) || output.length == 0) { - return []; - } - - return output.filter(isObject).map(toCamelCase); -} - - -export async function installChart(chart: string, values: JsonValue, name: string | undefined = "", namespace: string, version: string, kubeconfigPath: string) { - const valuesFilePath = tempy.file({ name: "values.yaml" }); - - await fse.writeFile(valuesFilePath, yaml.dump(values)); - - const args = ["install"]; - - if (name) { - args.push(name); - } - - args.push( - chart, - "--version", version, - "--values", valuesFilePath, - "--namespace", namespace, - "--kubeconfig", kubeconfigPath, - ); - - if (!name) { - args.push("--generate-name"); - } - - try { - const result = await execHelm(args); - - if (!result.callWasSuccessful) { - throw result.error; - } - - const output = result.response; - const releaseName = output.split("\n")[0].split(" ")[1].trim(); - - return { - log: output, - release: { - name: releaseName, - namespace, - }, - }; - } finally { - await fse.unlink(valuesFilePath); - } -} - -export async function deleteRelease(name: string, namespace: string, kubeconfigPath: string): Promise { - const result = await execHelm([ - "delete", - name, - "--namespace", namespace, - "--kubeconfig", kubeconfigPath, - ]); - - if (result.callWasSuccessful) { - return result.response; - } - - throw result.error; -} - -interface GetValuesOptions { - namespace: string; - all?: boolean; - kubeconfigPath: string; -} - -export async function getValues(name: string, { namespace, all = false, kubeconfigPath }: GetValuesOptions): Promise { - const args = [ - "get", - "values", - name, - ]; - - if (all) { - args.push("--all"); - } - - args.push( - "--output", "yaml", - "--namespace", namespace, - "--kubeconfig", kubeconfigPath, - ); - - const result = await execHelm(args); - - if (result.callWasSuccessful) { - return result.response; - } - - throw result.error; -} - -export async function getHistory(name: string, namespace: string, kubeconfigPath: string): Promise { - const result = await execHelm([ - "history", - name, - "--output", "json", - "--namespace", namespace, - "--kubeconfig", kubeconfigPath, - ]); - - if (result.callWasSuccessful) { - return json.parse(result.response); - } - - throw result.error; -} - -export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string): Promise { - const result = await execHelm([ - "rollback", - name, - `${revision}`, - "--namespace", namespace, - "--kubeconfig", kubeconfigPath, - ]); - - if (!result.callWasSuccessful) { - throw result.error; - } -} diff --git a/src/main/helm/helm-service/delete-helm-release.injectable.ts b/src/main/helm/helm-service/delete-helm-release.injectable.ts index d03cd480dc..fa27980355 100644 --- a/src/main/helm/helm-service/delete-helm-release.injectable.ts +++ b/src/main/helm/helm-service/delete-helm-release.injectable.ts @@ -4,25 +4,25 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../../common/cluster/cluster"; -import { deleteRelease } from "../helm-release-manager"; import loggerInjectable from "../../../common/logger.injectable"; +import type { DeleteHelmReleaseData } from "../delete-helm-release.injectable"; +import deleteHelmReleaseInjectable from "../delete-helm-release.injectable"; -const deleteHelmReleaseInjectable = getInjectable({ - id: "delete-helm-release", +const deleteClusterHelmReleaseInjectable = getInjectable({ + id: "delete-cluster-helm-release", instantiate: (di) => { const logger = di.inject(loggerInjectable); + const deleteHelmRelease = di.inject(deleteHelmReleaseInjectable); - return async (cluster: Cluster, releaseName: string, namespace: string) => { + return async (cluster: Cluster, data: DeleteHelmReleaseData) => { const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - logger.debug("Delete release"); + logger.debug(`[CLUSTER]: Delete helm release`, data); - return deleteRelease(releaseName, namespace, proxyKubeconfig); + return deleteHelmRelease(proxyKubeconfig, data); }; }, - - causesSideEffects: true, }); -export default deleteHelmReleaseInjectable; +export default deleteClusterHelmReleaseInjectable; diff --git a/src/main/helm/helm-service/get-helm-chart-values.injectable.ts b/src/main/helm/helm-service/get-helm-chart-values.injectable.ts index 489f8b8521..d131c93b66 100644 --- a/src/main/helm/helm-service/get-helm-chart-values.injectable.ts +++ b/src/main/helm/helm-service/get-helm-chart-values.injectable.ts @@ -24,8 +24,6 @@ const getHelmChartValuesInjectable = getInjectable({ return getChartManager(repo).getValues(chartName, version); }; }, - - causesSideEffects: true, }); export default getHelmChartValuesInjectable; diff --git a/src/main/helm/helm-service/get-helm-chart-versions.injectable.ts b/src/main/helm/helm-service/get-helm-chart-versions.injectable.ts index 8c55ea7921..0eedc25a44 100644 --- a/src/main/helm/helm-service/get-helm-chart-versions.injectable.ts +++ b/src/main/helm/helm-service/get-helm-chart-versions.injectable.ts @@ -24,8 +24,6 @@ const getHelmChartVersionsInjectable = getInjectable({ return getChartManager(repo).chartVersions(chartName); }; }, - - causesSideEffects: true, }); export default getHelmChartVersionsInjectable; diff --git a/src/main/helm/helm-service/get-helm-release-history.injectable.ts b/src/main/helm/helm-service/get-helm-release-history.injectable.ts index a642c2e265..fe4e3448ac 100644 --- a/src/main/helm/helm-service/get-helm-release-history.injectable.ts +++ b/src/main/helm/helm-service/get-helm-release-history.injectable.ts @@ -4,25 +4,25 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../../common/cluster/cluster"; -import { getHistory } from "../helm-release-manager"; import loggerInjectable from "../../../common/logger.injectable"; +import type { GetHelmReleaseHistoryData } from "../get-helm-release-history.injectable"; +import getHelmReleaseHistoryInjectable from "../get-helm-release-history.injectable"; -const getHelmReleaseHistoryInjectable = getInjectable({ - id: "get-helm-release-history", +const getClusterHelmReleaseHistoryInjectable = getInjectable({ + id: "get-cluster-helm-release-history", instantiate: (di) => { const logger = di.inject(loggerInjectable); + const getHelmReleaseHistory = di.inject(getHelmReleaseHistoryInjectable); - return async (cluster: Cluster, releaseName: string, namespace: string) => { + return async (cluster: Cluster, data: GetHelmReleaseHistoryData) => { const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - logger.debug("Fetch release history"); + logger.debug(`[CLUSTER]: Fetch release history for clusterId=${cluster.id}`, data); - return getHistory(releaseName, namespace, proxyKubeconfig); + return getHelmReleaseHistory(proxyKubeconfig, data); }; }, - - causesSideEffects: true, }); -export default getHelmReleaseHistoryInjectable; +export default getClusterHelmReleaseHistoryInjectable; diff --git a/src/main/helm/helm-service/get-helm-release-values.injectable.ts b/src/main/helm/helm-service/get-helm-release-values.injectable.ts index 5bca2d5723..8070ed366e 100644 --- a/src/main/helm/helm-service/get-helm-release-values.injectable.ts +++ b/src/main/helm/helm-service/get-helm-release-values.injectable.ts @@ -3,39 +3,26 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { getValues } from "../helm-release-manager"; import loggerInjectable from "../../../common/logger.injectable"; import type { Cluster } from "../../../common/cluster/cluster"; +import type { GetHelmReleaseValuesData } from "../get-helm-release-values.injectable"; +import getHelmReleaseValuesInjectable from "../get-helm-release-values.injectable"; -interface GetReleaseValuesArgs { - cluster: Cluster; - namespace: string; - all: boolean; -} - -const getHelmReleaseValuesInjectable = getInjectable({ - id: "get-helm-release-values", +const getClusterHelmReleaseValuesInjectable = getInjectable({ + id: "get-cluster-helm-release-values", instantiate: (di) => { const logger = di.inject(loggerInjectable); + const getHelmReleaseValues = di.inject(getHelmReleaseValuesInjectable); - return async ( - releaseName: string, - { cluster, namespace, all }: GetReleaseValuesArgs, - ) => { + return async (cluster: Cluster, data: GetHelmReleaseValuesData) => { const pathToKubeconfig = await cluster.getProxyKubeconfigPath(); - logger.debug("Fetch release values"); + logger.debug(`[CLUSTER]: getting helm release values`, data); - return getValues(releaseName, { - namespace, - all, - kubeconfigPath: pathToKubeconfig, - }); + return getHelmReleaseValues(pathToKubeconfig, data); }; }, - - causesSideEffects: true, }); -export default getHelmReleaseValuesInjectable; +export default getClusterHelmReleaseValuesInjectable; diff --git a/src/main/helm/helm-service/install-helm-chart.injectable.ts b/src/main/helm/helm-service/install-helm-chart.injectable.ts index 9ddb4fa599..b81b48cfa6 100644 --- a/src/main/helm/helm-service/install-helm-chart.injectable.ts +++ b/src/main/helm/helm-service/install-helm-chart.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { JsonObject } from "type-fest"; import type { Cluster } from "../../../common/cluster/cluster"; -import { installChart } from "../helm-release-manager"; +import installHelmChartInjectable from "../install-helm-chart.injectable"; export interface InstallChartArgs { chart: string; @@ -15,16 +15,21 @@ export interface InstallChartArgs { version: string; } -const installHelmChartInjectable = getInjectable({ - id: "install-helm-chart", +const installClusterHelmChartInjectable = getInjectable({ + id: "install-cluster-helm-chart", - instantiate: () => async (cluster: Cluster, data: InstallChartArgs) => { - const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + instantiate: (di) => { + const installHelmChart = di.inject(installHelmChartInjectable); - return installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig); + return async (cluster: Cluster, data: InstallChartArgs) => { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + + return installHelmChart({ + ...data, + kubeconfigPath: proxyKubeconfig, + }); + }; }, - - causesSideEffects: true, }); -export default installHelmChartInjectable; +export default installClusterHelmChartInjectable; diff --git a/src/main/helm/helm-service/list-helm-charts.injectable.ts b/src/main/helm/helm-service/list-helm-charts.injectable.ts index c5be54fd67..93a57e7aff 100644 --- a/src/main/helm/helm-service/list-helm-charts.injectable.ts +++ b/src/main/helm/helm-service/list-helm-charts.injectable.ts @@ -36,8 +36,6 @@ const listHelmChartsInjectable = getInjectable({ ); }; }, - - causesSideEffects: true, }); export default listHelmChartsInjectable; diff --git a/src/main/helm/helm-service/list-helm-releases.injectable.ts b/src/main/helm/helm-service/list-helm-releases.injectable.ts index f6250f768d..14a27f6ea6 100644 --- a/src/main/helm/helm-service/list-helm-releases.injectable.ts +++ b/src/main/helm/helm-service/list-helm-releases.injectable.ts @@ -5,24 +5,23 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../../common/cluster/cluster"; import loggerInjectable from "../../../common/logger.injectable"; -import { listReleases } from "../helm-release-manager"; +import listHelmReleasesInjectable from "../list-helm-releases.injectable"; -const listHelmReleasesInjectable = getInjectable({ - id: "list-helm-releases", +const listClusterHelmReleasesInjectable = getInjectable({ + id: "list-cluster-helm-releases", instantiate: (di) => { const logger = di.inject(loggerInjectable); + const listHelmReleases = di.inject(listHelmReleasesInjectable); return async (cluster: Cluster, namespace?: string) => { const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - logger.debug("list releases"); + logger.debug(`[CLUSTER]: listing helm releases for clusterId=${cluster.id}`, { namespace }); - return listReleases(proxyKubeconfig, namespace); + return listHelmReleases(proxyKubeconfig, namespace); }; }, - - causesSideEffects: true, }); -export default listHelmReleasesInjectable; +export default listClusterHelmReleasesInjectable; diff --git a/src/main/helm/helm-service/rollback-helm-release.injectable.ts b/src/main/helm/helm-service/rollback-helm-release.injectable.ts index 62ee05463d..a426de8f1c 100644 --- a/src/main/helm/helm-service/rollback-helm-release.injectable.ts +++ b/src/main/helm/helm-service/rollback-helm-release.injectable.ts @@ -5,28 +5,24 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../../common/cluster/cluster"; import loggerInjectable from "../../../common/logger.injectable"; -import { rollback } from "../helm-release-manager"; +import type { RollbackHelmReleaseData } from "../rollback-helm-release.injectable"; +import rollbackHelmReleaseInjectable from "../rollback-helm-release.injectable"; -const rollbackHelmReleaseInjectable = getInjectable({ - id: "rollback-helm-release", +const rollbackClusterHelmReleaseInjectable = getInjectable({ + id: "rollback-cluster-helm-release", instantiate: (di) => { const logger = di.inject(loggerInjectable); + const rollbackHelmRelease = di.inject(rollbackHelmReleaseInjectable); - return async ( - cluster: Cluster, - releaseName: string, - namespace: string, - revision: number, - ) => { + return async (cluster: Cluster, data: RollbackHelmReleaseData) => { const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - logger.debug("Rollback release"); - await rollback(releaseName, namespace, revision, proxyKubeconfig); + logger.debug(`[CLUSTER]: rolling back helm release for clusterId=${cluster.id}`, data); + + await rollbackHelmRelease(proxyKubeconfig, data); }; }, - - causesSideEffects: true, }); -export default rollbackHelmReleaseInjectable; +export default rollbackClusterHelmReleaseInjectable; diff --git a/src/main/helm/install-helm-chart.injectable.ts b/src/main/helm/install-helm-chart.injectable.ts new file mode 100644 index 0000000000..f566c6a6f1 --- /dev/null +++ b/src/main/helm/install-helm-chart.injectable.ts @@ -0,0 +1,93 @@ +/** + * 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 { dump } from "js-yaml"; +import tempy from "tempy"; +import type { JsonValue } from "type-fest"; +import removePathInjectable from "../../common/fs/remove.injectable"; +import writeFileInjectable from "../../common/fs/write-file.injectable"; +import execHelmInjectable from "./exec-helm/exec-helm.injectable"; + +export interface InstallHelmChartData { + chart: string; + values: JsonValue; + name: string; + namespace: string; + version: string; + kubeconfigPath: string; +} + +export interface InstallHelmChartResult { + log: string; + release: { + name: string; + namespace: string; + }; +} + +export type InstallHelmChart = (data: InstallHelmChartData) => Promise; + +const installHelmChartInjectable = getInjectable({ + id: "install-helm-chart", + instantiate: (di): InstallHelmChart => { + const writeFile = di.inject(writeFileInjectable); + const removePath = di.inject(removePathInjectable); + const execHelm = di.inject(execHelmInjectable); + + return async ({ + chart, + kubeconfigPath, + name, + namespace, + values, + version, + }) => { + const valuesFilePath = tempy.file({ name: "values.yaml" }); + + await writeFile(valuesFilePath, dump(values)); + + const args = ["install"]; + + if (name) { + args.push(name); + } + + args.push( + chart, + "--version", version, + "--values", valuesFilePath, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ); + + if (!name) { + args.push("--generate-name"); + } + + try { + const result = await execHelm(args); + + if (!result.callWasSuccessful) { + throw result.error; + } + + const output = result.response; + const releaseName = output.split("\n")[0].split(" ")[1].trim(); + + return { + log: output, + release: { + name: releaseName, + namespace, + }, + }; + } finally { + await removePath(valuesFilePath); + } + }; + }, +}); + +export default installHelmChartInjectable; diff --git a/src/main/helm/list-helm-releases.injectable.ts b/src/main/helm/list-helm-releases.injectable.ts new file mode 100644 index 0000000000..ff362eb3d2 --- /dev/null +++ b/src/main/helm/list-helm-releases.injectable.ts @@ -0,0 +1,48 @@ +/** + * 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 execHelmInjectable from "./exec-helm/exec-helm.injectable"; +import { isObject, json, toCamelCase } from "../../common/utils"; + +export type ListHelmReleases = (pathToKubeconfig: string, namespace?: string) => Promise[]>; + +const listHelmReleasesInjectable = getInjectable({ + id: "list-helm-releases", + instantiate: (di): ListHelmReleases => { + const execHelm = di.inject(execHelmInjectable); + + return async (pathToKubeconfig, namespace) => { + const args = [ + "ls", + "--all", + "--output", "json", + ]; + + if (namespace) { + args.push("-n", namespace); + } else { + args.push("--all-namespaces"); + } + + args.push("--kubeconfig", pathToKubeconfig); + + const result = await execHelm(args); + + if (!result.callWasSuccessful) { + throw result.error; + } + + const output = json.parse(result.response); + + if (!Array.isArray(output) || output.length == 0) { + return []; + } + + return output.filter(isObject).map(toCamelCase); + }; + }, +}); + +export default listHelmReleasesInjectable; diff --git a/src/main/helm/rollback-helm-release.injectable.ts b/src/main/helm/rollback-helm-release.injectable.ts new file mode 100644 index 0000000000..2b130adfb5 --- /dev/null +++ b/src/main/helm/rollback-helm-release.injectable.ts @@ -0,0 +1,37 @@ +/** + * 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 execHelmInjectable from "./exec-helm/exec-helm.injectable"; + +export interface RollbackHelmReleaseData { + name: string; + namespace: string; + revision: number; +} + +export type RollbackHelmRelease = (kubeconfigPath: string, data: RollbackHelmReleaseData) => Promise; + +const rollbackHelmReleaseInjectable = getInjectable({ + id: "rollback-helm-release", + instantiate: (di): RollbackHelmRelease => { + const execHelm = di.inject(execHelmInjectable); + + return async (kubeconfigPath, { name, namespace, revision }) => { + const result = await execHelm([ + "rollback", + name, + `${revision}`, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]); + + if (!result.callWasSuccessful) { + throw result.error; + } + }; + }, +}); + +export default rollbackHelmReleaseInjectable; diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts index ba64b69c69..4d711160bc 100644 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts +++ b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts @@ -11,6 +11,7 @@ import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.in import loggerInjectable from "../../common/logger.injectable"; import waitUntilPortIsUsedInjectable from "./wait-until-port-is-used/wait-until-port-is-used.injectable"; import lensK8sProxyPathInjectable from "./lens-k8s-proxy-path.injectable"; +import getPortFromStreamInjectable from "../utils/get-port-from-stream.injectable"; export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy; @@ -23,6 +24,7 @@ const createKubeAuthProxyInjectable = getInjectable({ spawn: di.inject(spawnInjectable), logger: di.inject(loggerInjectable), waitUntilPortIsUsed: di.inject(waitUntilPortIsUsedInjectable), + getPortFromStream: di.inject(getPortFromStreamInjectable), }; return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => { diff --git a/src/main/kube-auth-proxy/kube-auth-proxy.ts b/src/main/kube-auth-proxy/kube-auth-proxy.ts index a6067e548b..1d669f28c9 100644 --- a/src/main/kube-auth-proxy/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy/kube-auth-proxy.ts @@ -6,7 +6,7 @@ import type { ChildProcess } from "child_process"; import { randomBytes } from "crypto"; import type { Cluster } from "../../common/cluster/cluster"; -import { getPortFrom } from "../utils/get-port"; +import type { GetPortFromStream } from "../utils/get-port-from-stream.injectable"; import { makeObservable, observable, when } from "mobx"; import type { SelfSignedCert } from "selfsigned"; import assert from "assert"; @@ -23,9 +23,10 @@ const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), export interface KubeAuthProxyDependencies { readonly proxyBinPath: string; readonly proxyCert: SelfSignedCert; - readonly spawn: Spawn; readonly logger: Logger; - readonly waitUntilPortIsUsed: WaitUntilPortIsUsed; + spawn: Spawn; + waitUntilPortIsUsed: WaitUntilPortIsUsed; + getPortFromStream: GetPortFromStream; } export class KubeAuthProxy { @@ -101,7 +102,7 @@ export class KubeAuthProxy { } }); - this._port = await getPortFrom(this.proxyProcess.stdout, { + this._port = await this.dependencies.getPortFromStream(this.proxyProcess.stdout, { lineRegex: startingServeRegex, onFind: () => this.cluster.broadcastConnectUpdate("Authentication proxy started"), }); diff --git a/src/main/kubectl/create-kubectl.injectable.ts b/src/main/kubectl/create-kubectl.injectable.ts index f5a278ab7b..adfce0922f 100644 --- a/src/main/kubectl/create-kubectl.injectable.ts +++ b/src/main/kubectl/create-kubectl.injectable.ts @@ -17,6 +17,7 @@ import kubectlVersionMapInjectable from "./version-map.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable"; import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; +import loggerInjectable from "../../common/logger.injectable"; const createKubectlInjectable = getInjectable({ id: "create-kubectl", @@ -32,6 +33,7 @@ const createKubectlInjectable = getInjectable({ baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable), bundledKubectlVersion: di.inject(bundledKubectlVersionInjectable), kubectlVersionMap: di.inject(kubectlVersionMapInjectable), + logger: di.inject(loggerInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable), joinPaths: di.inject(joinPathsInjectable), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), diff --git a/src/main/kubectl/kubectl.ts b/src/main/kubectl/kubectl.ts index f0b3c45da0..15773a2ef7 100644 --- a/src/main/kubectl/kubectl.ts +++ b/src/main/kubectl/kubectl.ts @@ -5,7 +5,6 @@ import fs from "fs"; import { promiseExecFile } from "../../common/utils/promise-exec"; -import logger from "../logger"; import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; import { SemVer, coerce } from "semver"; @@ -18,6 +17,7 @@ import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; import type { NormalizedPlatform } from "../../common/vars/normalized-platform.injectable"; +import type { Logger } from "../../common/logger"; const initScriptVersionString = "# lens-initscript v3"; @@ -36,6 +36,7 @@ export interface KubectlDependencies { }; readonly bundledKubectlVersion: string; readonly kubectlVersionMap: Map; + readonly logger: Logger; joinPaths: JoinPaths; getDirnameOfPath: GetDirnameOfPath; getBasenameOfPath: GetBasenameOfPath; @@ -67,13 +68,13 @@ export class Kubectl { */ if (fromMajorMinor) { this.kubectlVersion = fromMajorMinor; - logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using version map`); + this.dependencies.logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using version map`); } else { /* this is the version (without possible prelease tag) to get from the download mirror */ const ver = coerce(version.format()) ?? bundledVersion; this.kubectlVersion = ver.format(); - logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); + this.dependencies.logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); } this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${this.dependencies.normalizedDownloadPlatform}/${this.dependencies.normalizedDownloadArch}/${this.dependencies.kubectlBinaryName}`; @@ -115,14 +116,14 @@ export class Kubectl { try { if (!await this.ensureKubectl()) { - logger.error("Failed to ensure kubectl, fallback to the bundled version"); + this.dependencies.logger.error("Failed to ensure kubectl, fallback to the bundled version"); return this.getBundledPath(); } return this.path; } catch (err) { - logger.error("Failed to ensure kubectl, fallback to the bundled version", err); + this.dependencies.logger.error("Failed to ensure kubectl, fallback to the bundled version", err); return this.getBundledPath(); } @@ -135,7 +136,7 @@ export class Kubectl { return this.dirname; } catch (err) { - logger.error("Failed to get biniary directory", err); + this.dependencies.logger.error("Failed to get biniary directory", err); return ""; } @@ -164,13 +165,13 @@ export class Kubectl { } if (version === this.kubectlVersion) { - logger.debug(`Local kubectl is version ${this.kubectlVersion}`); + this.dependencies.logger.debug(`Local kubectl is version ${this.kubectlVersion}`); return true; } - logger.error(`Local kubectl is version ${version}, expected ${this.kubectlVersion}, unlinking`); + this.dependencies.logger.error(`Local kubectl is version ${version}, expected ${this.kubectlVersion}, unlinking`); } catch (error) { - logger.error(`Local kubectl failed to run properly (${error}), unlinking`); + this.dependencies.logger.error(`Local kubectl failed to run properly (${error}), unlinking`); } await fs.promises.unlink(this.path); } @@ -190,7 +191,7 @@ export class Kubectl { return true; } catch (err) { - logger.error(`Could not copy the bundled kubectl to app-data: ${err}`); + this.dependencies.logger.error(`Could not copy the bundled kubectl to app-data: ${err}`); return false; } @@ -205,7 +206,7 @@ export class Kubectl { } if (Kubectl.invalidBundle) { - logger.error(`Detected invalid bundle binary, returning ...`); + this.dependencies.logger.error(`Detected invalid bundle binary, returning ...`); return false; } @@ -215,7 +216,7 @@ export class Kubectl { try { const release = await lockFile.lock(this.dirname); - logger.debug(`Acquired a lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`Acquired a lock for ${this.kubectlVersion}`); const bundled = await this.checkBundled(); let isValid = await this.checkBinary(this.path, !bundled); @@ -223,8 +224,8 @@ export class Kubectl { try { await this.downloadKubectl(); } catch (error) { - logger.error(`[KUBECTL]: failed to download kubectl`, error); - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.error(`[KUBECTL]: failed to download kubectl`, error); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return false; @@ -234,18 +235,18 @@ export class Kubectl { } if (!isValid) { - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return false; } - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return true; } catch (error) { - logger.error(`[KUBECTL]: Failed to get a lock for ${this.kubectlVersion}`, error); + this.dependencies.logger.error(`[KUBECTL]: Failed to get a lock for ${this.kubectlVersion}`, error); return false; } @@ -254,7 +255,7 @@ export class Kubectl { public async downloadKubectl() { await ensureDir(this.dependencies.getDirnameOfPath(this.path), 0o755); - logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); + this.dependencies.logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); const downloadStream = got.stream({ url: this.url, decompress: true }); const fileWriteStream = fs.createWriteStream(this.path, { mode: 0o755 }); @@ -263,7 +264,7 @@ export class Kubectl { try { await pipeline(downloadStream, fileWriteStream); await fs.promises.chmod(this.path, 0o755); - logger.debug("kubectl binary download finished"); + this.dependencies.logger.debug("kubectl binary download finished"); } catch (error) { await fs.promises.unlink(this.path).catch(noop); throw error; diff --git a/src/main/kubectl/normalized-arch.injectable.ts b/src/main/kubectl/normalized-arch.injectable.ts index 88ec6b1067..e68d3da304 100644 --- a/src/main/kubectl/normalized-arch.injectable.ts +++ b/src/main/kubectl/normalized-arch.injectable.ts @@ -3,11 +3,14 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import processArchInjectable from "../../common/vars/process-arch.injectable"; const kubectlDownloadingNormalizedArchInjectable = getInjectable({ id: "kubectl-downloading-normalized-arch", - instantiate: () => { - switch (process.arch) { + instantiate: (di) => { + const processArch = di.inject(processArchInjectable); + + switch (processArch) { case "arm64": return "arm64"; case "x64": @@ -18,10 +21,9 @@ const kubectlDownloadingNormalizedArchInjectable = getInjectable({ case "ia32": return "386"; default: - throw new Error(`arch=${process.arch} is unsupported`); + throw new Error(`arch=${processArch} is unsupported`); } }, - causesSideEffects: true, }); export default kubectlDownloadingNormalizedArchInjectable; diff --git a/src/main/lens-proxy/get-cluster-for-request.injectable.ts b/src/main/lens-proxy/get-cluster-for-request.injectable.ts new file mode 100644 index 0000000000..7d529540d1 --- /dev/null +++ b/src/main/lens-proxy/get-cluster-for-request.injectable.ts @@ -0,0 +1,45 @@ +/** + * 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 getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; +import { getClusterIdFromHost } from "../../common/utils"; +import { apiKubePrefix } from "../../common/vars"; +import type { GetClusterForRequest } from "./lens-proxy"; + +const getClusterForRequestInjectable = getInjectable({ + id: "get-cluster-for-request", + instantiate: (di): GetClusterForRequest => { + const getClusterById = di.inject(getClusterByIdInjectable); + + return (req) => { + if (!req.headers.host) { + return undefined; + } + + // lens-server is connecting to 127.0.0.1:/ + if (req.url && req.headers.host.startsWith("127.0.0.1")) { + const clusterId = req.url.split("/")[1]; + const cluster = getClusterById(clusterId); + + if (cluster) { + // we need to swap path prefix so that request is proxied to kube api + req.url = req.url.replace(`/${clusterId}`, apiKubePrefix); + } + + return cluster; + } + + const clusterId = getClusterIdFromHost(req.headers.host); + + if (!clusterId) { + return undefined; + } + + return getClusterById(clusterId); + }; + }, +}); + +export default getClusterForRequestInjectable; diff --git a/src/main/lens-proxy/lens-proxy.injectable.ts b/src/main/lens-proxy/lens-proxy.injectable.ts index 998312b535..541ef18cc3 100644 --- a/src/main/lens-proxy/lens-proxy.injectable.ts +++ b/src/main/lens-proxy/lens-proxy.injectable.ts @@ -7,13 +7,13 @@ import { LensProxy } from "./lens-proxy"; import { kubeApiUpgradeRequest } from "./proxy-functions"; import routerInjectable from "../router/router.injectable"; import httpProxy from "http-proxy"; -import clusterManagerInjectable from "../cluster/manager.injectable"; -import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable"; +import shellApiRequestInjectable from "./proxy-functions/shell-api-request.injectable"; import lensProxyPortInjectable from "./lens-proxy-port.injectable"; import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable"; import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import loggerInjectable from "../../common/logger.injectable"; import lensProxyCertificateInjectable from "../../common/certificate/lens-proxy-certificate.injectable"; +import getClusterForRequestInjectable from "./get-cluster-for-request.injectable"; const lensProxyInjectable = getInjectable({ id: "lens-proxy", @@ -23,7 +23,7 @@ const lensProxyInjectable = getInjectable({ proxy: httpProxy.createProxy(), kubeApiUpgradeRequest, shellApiRequest: di.inject(shellApiRequestInjectable), - getClusterForRequest: di.inject(clusterManagerInjectable).getClusterForRequest, + getClusterForRequest: di.inject(getClusterForRequestInjectable), lensProxyPort: di.inject(lensProxyPortInjectable), contentSecurityPolicy: di.inject(contentSecurityPolicyInjectable), emitAppEvent: di.inject(emitAppEventInjectable), diff --git a/src/main/lens-proxy/lens-proxy.ts b/src/main/lens-proxy/lens-proxy.ts index 9b1d80270a..1a5acdd9f2 100644 --- a/src/main/lens-proxy/lens-proxy.ts +++ b/src/main/lens-proxy/lens-proxy.ts @@ -19,14 +19,14 @@ import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injecta import type { Logger } from "../../common/logger"; import type { SelfSignedCert } from "selfsigned"; -type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; - +export type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; export type ServerIncomingMessage = SetRequired; +export type LensProxyApiRequest = (args: ProxyApiRequestArgs) => void | Promise; interface Dependencies { getClusterForRequest: GetClusterForRequest; - shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise; - kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise; + shellApiRequest: LensProxyApiRequest; + kubeApiUpgradeRequest: LensProxyApiRequest; emitAppEvent: EmitAppEvent; readonly router: Router; readonly proxy: httpProxy; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts new file mode 100644 index 0000000000..afaf2a870d --- /dev/null +++ b/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts @@ -0,0 +1,42 @@ +/** + * 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 shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; +import openShellSessionInjectable from "../../shell-session/create-shell-session.injectable"; +import type { LensProxyApiRequest } from "../lens-proxy"; +import URLParse from "url-parse"; +import { Server as WebSocketServer } from "ws"; +import loggerInjectable from "../../../common/logger.injectable"; +import getClusterForRequestInjectable from "../get-cluster-for-request.injectable"; + +const shellApiRequestInjectable = getInjectable({ + id: "shell-api-request", + + instantiate: (di): LensProxyApiRequest => { + const openShellSession = di.inject(openShellSessionInjectable); + const authenticateRequest = di.inject(shellRequestAuthenticatorInjectable).authenticate; + const getClusterForRequest = di.inject(getClusterForRequestInjectable); + const logger = di.inject(loggerInjectable); + + return ({ req, socket, head }) => { + const cluster = getClusterForRequest(req); + const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true); + + if (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) { + socket.write("Invalid shell request"); + socket.end(); + } else { + const ws = new WebSocketServer({ noServer: true }); + + ws.handleUpgrade(req, socket, head, (websocket) => { + openShellSession({ websocket, cluster, tabId, nodeName }) + .catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error)); + }); + } + }; + }, +}); + +export default shellApiRequestInjectable; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts deleted file mode 100644 index 550a8d2560..0000000000 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts +++ /dev/null @@ -1,21 +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 { shellApiRequest } from "./shell-api-request"; -import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; -import clusterManagerInjectable from "../../../cluster/manager.injectable"; -import openShellSessionInjectable from "../../../shell-session/create-shell-session.injectable"; - -const shellApiRequestInjectable = getInjectable({ - id: "shell-api-request", - - instantiate: (di) => shellApiRequest({ - openShellSession: di.inject(openShellSessionInjectable), - authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate, - clusterManager: di.inject(clusterManagerInjectable), - }), -}); - -export default shellApiRequestInjectable; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts b/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts deleted file mode 100644 index f4492e96f6..0000000000 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import logger from "../../../logger"; -import { Server as WebSocketServer } from "ws"; -import type { ProxyApiRequestArgs } from "../types"; -import type { ClusterManager } from "../../../cluster/manager"; -import URLParse from "url-parse"; -import type { ClusterId } from "../../../../common/cluster-types"; -import type { OpenShellSession } from "../../../shell-session/create-shell-session.injectable"; - -interface Dependencies { - authenticateRequest: (clusterId: ClusterId, tabId: string, shellToken: string | undefined) => boolean; - openShellSession: OpenShellSession; - clusterManager: ClusterManager; -} - -export const shellApiRequest = ({ openShellSession, authenticateRequest, clusterManager }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => { - const cluster = clusterManager.getClusterForRequest(req); - const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true); - - if (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) { - socket.write("Invalid shell request"); - - return void socket.end(); - } - - const ws = new WebSocketServer({ noServer: true }); - - ws.handleUpgrade(req, socket, head, (websocket) => { - openShellSession({ websocket, cluster, tabId, nodeName }) - .catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error)); - }); -}; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.injectable.ts similarity index 100% rename from src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.injectable.ts rename to src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.injectable.ts diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts similarity index 89% rename from src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts rename to src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts index fe0a8f643d..ab5e46eb77 100644 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts +++ b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts @@ -2,9 +2,9 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getOrInsertMap } from "../../../../../common/utils"; -import type { ClusterId } from "../../../../../common/cluster-types"; -import { ipcMainHandle } from "../../../../../common/ipc"; +import { getOrInsertMap } from "../../../../common/utils"; +import type { ClusterId } from "../../../../common/cluster-types"; +import { ipcMainHandle } from "../../../../common/ipc"; import crypto from "crypto"; import { promisify } from "util"; diff --git a/src/main/logger.ts b/src/main/logger.ts deleted file mode 100644 index 8434c6e4d8..0000000000 --- a/src/main/logger.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import logger from "../common/logger"; - -export default logger; diff --git a/src/main/router/router.test.ts b/src/main/router/router.test.ts index a557eaa6c1..482cafb0d5 100644 --- a/src/main/router/router.test.ts +++ b/src/main/router/router.test.ts @@ -13,14 +13,12 @@ import type { AsyncFnMock } from "@async-fn/jest"; import asyncFn from "@async-fn/jest"; import parseRequestInjectable from "./parse-request.injectable"; import { contentTypes } from "./router-content-types"; -import mockFs from "mock-fs"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import type { Route } from "./route"; import type { SetRequired } from "type-fest"; import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable"; import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable"; -import fsInjectable from "../../common/fs/fs.injectable"; import { runInAction } from "mobx"; describe("router", () => { @@ -32,14 +30,11 @@ describe("router", () => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); - mockFs(); - di.permitSideEffects(fsInjectable); - di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload", mime: "some-mime", })); - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); di.override(kubectlBinaryNameInjectable, () => "kubectl"); di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); di.override(normalizedPlatformInjectable, () => "darwin"); @@ -63,10 +58,6 @@ describe("router", () => { router = di.inject(routerInjectable); }); - afterEach(() => { - mockFs.restore(); - }); - describe("when navigating to the route", () => { let actualPromise: Promise; let clusterStub: Cluster; diff --git a/src/main/routes/helm/releases/delete-release-route.injectable.ts b/src/main/routes/helm/releases/delete-release-route.injectable.ts index 2da28fd591..79d3195643 100644 --- a/src/main/routes/helm/releases/delete-release-route.injectable.ts +++ b/src/main/routes/helm/releases/delete-release-route.injectable.ts @@ -5,19 +5,19 @@ import { apiPrefix } from "../../../../common/vars"; import { getRouteInjectable } from "../../../router/router.injectable"; import { clusterRoute } from "../../../router/route"; -import deleteHelmReleaseInjectable from "../../../helm/helm-service/delete-helm-release.injectable"; +import deleteClusterHelmReleaseInjectable from "../../../helm/helm-service/delete-helm-release.injectable"; const deleteReleaseRouteInjectable = getRouteInjectable({ id: "delete-release-route", instantiate: (di) => { - const deleteHelmRelease = di.inject(deleteHelmReleaseInjectable); + const deleteHelmRelease = di.inject(deleteClusterHelmReleaseInjectable); return clusterRoute({ method: "delete", - path: `${apiPrefix}/v2/releases/{namespace}/{release}`, - })(async ({ cluster, params: { release, namespace }}) => ({ - response: await deleteHelmRelease(cluster, release, namespace), + path: `${apiPrefix}/v2/releases/{namespace}/{name}`, + })(async ({ cluster, params }) => ({ + response: await deleteHelmRelease(cluster, params), })); }, }); diff --git a/src/main/routes/helm/releases/get-release-history-route.injectable.ts b/src/main/routes/helm/releases/get-release-history-route.injectable.ts index 142adfc0d5..2ba1349de0 100644 --- a/src/main/routes/helm/releases/get-release-history-route.injectable.ts +++ b/src/main/routes/helm/releases/get-release-history-route.injectable.ts @@ -5,23 +5,19 @@ import { apiPrefix } from "../../../../common/vars"; import { getRouteInjectable } from "../../../router/router.injectable"; import { clusterRoute } from "../../../router/route"; -import getHelmReleaseHistoryInjectable from "../../../helm/helm-service/get-helm-release-history.injectable"; +import getClusterHelmReleaseHistoryInjectable from "../../../helm/helm-service/get-helm-release-history.injectable"; const getReleaseRouteHistoryInjectable = getRouteInjectable({ id: "get-release-history-route", instantiate: (di) => { - const getHelmReleaseHistory = di.inject(getHelmReleaseHistoryInjectable); + const getHelmReleaseHistory = di.inject(getClusterHelmReleaseHistoryInjectable); return clusterRoute({ method: "get", - path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`, + path: `${apiPrefix}/v2/releases/{namespace}/{name}/history`, })(async ({ cluster, params }) => ({ - response: await getHelmReleaseHistory( - cluster, - params.release, - params.namespace, - ), + response: await getHelmReleaseHistory(cluster, params), })); }, }); diff --git a/src/main/routes/helm/releases/get-release-values-route.injectable.ts b/src/main/routes/helm/releases/get-release-values-route.injectable.ts index d68e5c53d1..78799f3e58 100644 --- a/src/main/routes/helm/releases/get-release-values-route.injectable.ts +++ b/src/main/routes/helm/releases/get-release-values-route.injectable.ts @@ -7,20 +7,20 @@ import { getRouteInjectable } from "../../../router/router.injectable"; import { getBoolean } from "../../../utils/parse-query"; import { contentTypes } from "../../../router/router-content-types"; import { clusterRoute } from "../../../router/route"; -import getHelmReleaseValuesInjectable from "../../../helm/helm-service/get-helm-release-values.injectable"; +import getClusterHelmReleaseValuesInjectable from "../../../helm/helm-service/get-helm-release-values.injectable"; const getReleaseRouteValuesInjectable = getRouteInjectable({ id: "get-release-values-route", instantiate: (di) => { - const getHelmReleaseValues = di.inject(getHelmReleaseValuesInjectable); + const getClusterHelmReleaseValues = di.inject(getClusterHelmReleaseValuesInjectable); return clusterRoute({ method: "get", - path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`, - })(async ({ cluster, params: { namespace, release }, query }) => ({ - response: await getHelmReleaseValues(release, { - cluster, + path: `${apiPrefix}/v2/releases/{namespace}/{name}/values`, + })(async ({ cluster, params: { namespace, name }, query }) => ({ + response: await getClusterHelmReleaseValues(cluster, { + name, namespace, all: getBoolean(query, "all"), }), diff --git a/src/main/routes/helm/releases/install-chart-route.injectable.ts b/src/main/routes/helm/releases/install-chart-route.injectable.ts index 75969c4a2f..e34d54db23 100644 --- a/src/main/routes/helm/releases/install-chart-route.injectable.ts +++ b/src/main/routes/helm/releases/install-chart-route.injectable.ts @@ -7,7 +7,7 @@ import { getRouteInjectable } from "../../../router/router.injectable"; import Joi from "joi"; import { payloadValidatedClusterRoute } from "../../../router/route"; import type { InstallChartArgs } from "../../../helm/helm-service/install-helm-chart.injectable"; -import installHelmChartInjectable from "../../../helm/helm-service/install-helm-chart.injectable"; +import installClusterHelmChartInjectable from "../../../helm/helm-service/install-helm-chart.injectable"; const installChartArgsValidator = Joi.object({ chart: Joi @@ -31,7 +31,7 @@ const installChartRouteInjectable = getRouteInjectable({ id: "install-chart-route", instantiate: (di) => { - const installHelmChart = di.inject(installHelmChartInjectable); + const installHelmChart = di.inject(installClusterHelmChartInjectable); return payloadValidatedClusterRoute({ method: "post", diff --git a/src/main/routes/helm/releases/list-releases-route.injectable.ts b/src/main/routes/helm/releases/list-releases-route.injectable.ts index 8e9ea1e305..53a4dcb179 100644 --- a/src/main/routes/helm/releases/list-releases-route.injectable.ts +++ b/src/main/routes/helm/releases/list-releases-route.injectable.ts @@ -5,13 +5,13 @@ import { apiPrefix } from "../../../../common/vars"; import { getRouteInjectable } from "../../../router/router.injectable"; import { clusterRoute } from "../../../router/route"; -import listHelmReleasesInjectable from "../../../helm/helm-service/list-helm-releases.injectable"; +import listClusterHelmReleasesInjectable from "../../../helm/helm-service/list-helm-releases.injectable"; const listReleasesRouteInjectable = getRouteInjectable({ id: "list-releases-route", instantiate: (di) => { - const listHelmReleases = di.inject(listHelmReleasesInjectable); + const listHelmReleases = di.inject(listClusterHelmReleasesInjectable); return clusterRoute({ method: "get", diff --git a/src/main/routes/helm/releases/rollback-release-route.injectable.ts b/src/main/routes/helm/releases/rollback-release-route.injectable.ts index eb27b675c4..49a8fddb88 100644 --- a/src/main/routes/helm/releases/rollback-release-route.injectable.ts +++ b/src/main/routes/helm/releases/rollback-release-route.injectable.ts @@ -6,7 +6,7 @@ import { apiPrefix } from "../../../../common/vars"; import { getRouteInjectable } from "../../../router/router.injectable"; import Joi from "joi"; import { payloadValidatedClusterRoute } from "../../../router/route"; -import rollbackHelmReleaseInjectable from "../../../helm/helm-service/rollback-helm-release.injectable"; +import rollbackClusterHelmReleaseInjectable from "../../../helm/helm-service/rollback-helm-release.injectable"; interface RollbackReleasePayload { revision: number; @@ -22,14 +22,14 @@ const rollbackReleaseRouteInjectable = getRouteInjectable({ id: "rollback-release-route", instantiate: (di) => { - const rollbackRelease = di.inject(rollbackHelmReleaseInjectable); + const rollbackRelease = di.inject(rollbackClusterHelmReleaseInjectable); return payloadValidatedClusterRoute({ method: "put", - path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`, + path: `${apiPrefix}/v2/releases/{namespace}/{name}/rollback`, payloadValidator: rollbackReleasePayloadValidator, - })(async ({ cluster, params: { release, namespace }, payload }) => { - await rollbackRelease(cluster, release, namespace, payload.revision); + })(async ({ cluster, params, payload }) => { + await rollbackRelease(cluster, { ...params, ...payload }); }); }, }); diff --git a/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts b/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts index ab2a5e8c42..e2bfb8a517 100644 --- a/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts +++ b/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts @@ -3,22 +3,25 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import type { PortForwardArgs } from "./port-forward"; +import type { PortForwardArgs, PortForwardDependencies } from "./port-forward"; import { PortForward } from "./port-forward"; import bundledKubectlInjectable from "../../../kubectl/bundled-kubectl.injectable"; +import getPortFromStreamInjectable from "../../../utils/get-port-from-stream.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; + +export type CreatePortForward = (pathToKubeConfig: string, args: PortForwardArgs) => PortForward; const createPortForwardInjectable = getInjectable({ id: "create-port-forward", - instantiate: (di) => { - const bundledKubectl = di.inject(bundledKubectlInjectable); - - const dependencies = { - getKubectlBinPath: bundledKubectl.getPath, + instantiate: (di): CreatePortForward => { + const dependencies: PortForwardDependencies = { + getKubectlBinPath: di.inject(bundledKubectlInjectable).getPath, + getPortFromStream: di.inject(getPortFromStreamInjectable), + logger: di.inject(loggerInjectable), }; - return (pathToKubeConfig: string, args: PortForwardArgs) => - new PortForward(dependencies, pathToKubeConfig, args); + return (pathToKubeConfig, args) => new PortForward(dependencies, pathToKubeConfig, args); }, }); diff --git a/src/main/routes/port-forward/functionality/port-forward.ts b/src/main/routes/port-forward/functionality/port-forward.ts index 43d8f9d425..0419024f2f 100644 --- a/src/main/routes/port-forward/functionality/port-forward.ts +++ b/src/main/routes/port-forward/functionality/port-forward.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import logger from "../../../logger"; -import { getPortFrom } from "../../../utils/get-port"; +import type { GetPortFromStream } from "../../../utils/get-port-from-stream.injectable"; import type { ChildProcessWithoutNullStreams } from "child_process"; import { spawn } from "child_process"; import * as tcpPortUsed from "tcp-port-used"; import { TypedRegEx } from "typed-regex"; +import type { Logger } from "../../../../common/logger"; const internalPortMatcher = "^forwarding from (?
.+) ->"; const internalPortRegex = Object.assign(TypedRegEx(internalPortMatcher, "i"), { @@ -23,8 +23,10 @@ export interface PortForwardArgs { forwardPort: number; } -interface Dependencies { +export interface PortForwardDependencies { + readonly logger: Logger; getKubectlBinPath: (bundled: boolean) => Promise; + getPortFromStream: GetPortFromStream; } export class PortForward { @@ -48,7 +50,7 @@ export class PortForward { public port: number; public forwardPort: number; - constructor(private dependencies: Dependencies, public pathToKubeConfig: string, args: PortForwardArgs) { + constructor(private dependencies: PortForwardDependencies, public pathToKubeConfig: string, args: PortForwardArgs) { this.clusterId = args.clusterId; this.kind = args.kind; this.namespace = args.namespace; @@ -80,10 +82,10 @@ export class PortForward { }); this.process.stderr.on("data", (data) => { - logger.debug(`[PORT-FORWARD-ROUTE]: kubectl port-forward process stderr: ${data}`); + this.dependencies.logger.debug(`[PORT-FORWARD-ROUTE]: kubectl port-forward process stderr: ${data}`); }); - const internalPort = await getPortFrom(this.process.stdout, { + const internalPort = await this.dependencies.getPortFromStream(this.process.stdout, { lineRegex: internalPortRegex, }); diff --git a/src/main/shell-session/node-shell-session/node-shell-session.ts b/src/main/shell-session/node-shell-session/node-shell-session.ts index 6a8af4c09e..492c70d73d 100644 --- a/src/main/shell-session/node-shell-session/node-shell-session.ts +++ b/src/main/shell-session/node-shell-session/node-shell-session.ts @@ -9,9 +9,10 @@ import type { KubeConfig } from "@kubernetes/client-node"; import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session"; import { ShellOpenError, ShellSession } from "../shell-session"; import { get, once } from "lodash"; -import { Node, NodeApi } from "../../../common/k8s-api/endpoints"; +import { NodeApi } from "../../../common/k8s-api/endpoints"; import { TerminalChannels } from "../../../common/terminal/channels"; import type { CreateKubeJsonApiForCluster } from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; +import type { CreateKubeApi } from "../../../common/k8s-api/create-kube-api.injectable"; export interface NodeShellSessionArgs extends ShellSessionArgs { nodeName: string; @@ -19,6 +20,7 @@ export interface NodeShellSessionArgs extends ShellSessionArgs { export interface NodeShellSessionDependencies extends ShellSessionDependencies { createKubeJsonApiForCluster: CreateKubeJsonApiForCluster; + createKubeApi: CreateKubeApi; } export class NodeShellSession extends ShellSession { @@ -67,8 +69,7 @@ export class NodeShellSession extends ShellSession { const env = await this.getCachedShellEnv(); const args = ["exec", "-i", "-t", "-n", "kube-system", this.podName, "--"]; - const nodeApi = new NodeApi({ - objectConstructor: Node, + const nodeApi = this.dependencies.createKubeApi(NodeApi, { request: this.dependencies.createKubeJsonApiForCluster(this.cluster.id), }); const node = await nodeApi.get({ name: this.nodeName }); diff --git a/src/main/shell-session/node-shell-session/open.injectable.ts b/src/main/shell-session/node-shell-session/open.injectable.ts index 47b97994b7..cce3fa5f36 100644 --- a/src/main/shell-session/node-shell-session/open.injectable.ts +++ b/src/main/shell-session/node-shell-session/open.injectable.ts @@ -19,6 +19,7 @@ import appNameInjectable from "../../../common/vars/app-name.injectable"; import buildVersionInjectable from "../../vars/build-version/build-version.injectable"; import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import statInjectable from "../../../common/fs/stat.injectable"; +import createKubeApiInjectable from "../../../common/k8s-api/create-kube-api.injectable"; export interface NodeShellSessionArgs { websocket: WebSocket; @@ -45,6 +46,7 @@ const openNodeShellSessionInjectable = getInjectable({ spawnPty: di.inject(spawnPtyInjectable), emitAppEvent: di.inject(emitAppEventInjectable), stat: di.inject(statInjectable), + createKubeApi: di.inject(createKubeApiInjectable), }; return async (args) => { diff --git a/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts b/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts index ad0e446565..1dbadc4246 100644 --- a/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts +++ b/src/main/start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable.ts @@ -28,8 +28,6 @@ const startKubeConfigSyncInjectable = getInjectable({ }; }, - causesSideEffects: true, - injectionToken: afterApplicationIsLoadedInjectionToken, }); diff --git a/src/main/start-main-application/runnables/setup-hardware-acceleration.injectable.ts b/src/main/start-main-application/runnables/setup-hardware-acceleration.injectable.ts index 905cb9e130..ffcff08434 100644 --- a/src/main/start-main-application/runnables/setup-hardware-acceleration.injectable.ts +++ b/src/main/start-main-application/runnables/setup-hardware-acceleration.injectable.ts @@ -3,15 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import environmentVariablesInjectable from "../../../common/utils/environment-variables.injectable"; import disableHardwareAccelerationInjectable from "../../electron-app/features/disable-hardware-acceleration.injectable"; +import hardwareAccelerationShouldBeDisabledInjectable from "../../vars/hardware-acceleration-should-be-disabled.injectable"; import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token"; const setupHardwareAccelerationInjectable = getInjectable({ id: "setup-hardware-acceleration", instantiate: (di) => { - const { LENS_DISABLE_GPU: hardwareAccelerationShouldBeDisabled } = di.inject(environmentVariablesInjectable); + const hardwareAccelerationShouldBeDisabled = di.inject(hardwareAccelerationShouldBeDisabledInjectable); const disableHardwareAcceleration = di.inject(disableHardwareAccelerationInjectable); return { diff --git a/src/main/utils/command-line-arguments.global-override-for-injectable.ts b/src/main/utils/command-line-arguments.global-override-for-injectable.ts new file mode 100644 index 0000000000..edd33d824a --- /dev/null +++ b/src/main/utils/command-line-arguments.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * 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 commandLineArgumentsInjectable from "./command-line-arguments.injectable"; + +export default getGlobalOverride(commandLineArgumentsInjectable, () => []); diff --git a/src/main/utils/get-port-from-stream.injectable.ts b/src/main/utils/get-port-from-stream.injectable.ts new file mode 100644 index 0000000000..983ed66313 --- /dev/null +++ b/src/main/utils/get-port-from-stream.injectable.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { Readable } from "stream"; +import URLParse from "url-parse"; +import { getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../../common/logger.injectable"; + +export interface GetPortFromStreamArgs { + /** + * Should be case insensitive + * Must have a named matching group called `address` + */ + lineRegex: { + match: (line: string) => { + matched: boolean; + groups?: { + address?: string; + }; + raw?: RegExpExecArray; + }; + rawMatcher: string; + }; + /** + * Called when the port is found + */ + onFind?: () => void; + /** + * Timeout for how long to wait for the port. + * Default: 15s + */ + timeout?: number; +} + +export type GetPortFromStream = (stream: Readable, args: GetPortFromStreamArgs) => Promise; + +const getPortFromStreamInjectable = getInjectable({ + id: "get-port-from-stream", + instantiate: (di): GetPortFromStream => { + const logger = di.inject(loggerInjectable); + + return (stream, args) => { + const logLines: string[] = []; + + return new Promise((resolve, reject) => { + const handler = (data: unknown) => { + const logItem = String(data); + const match = args.lineRegex.match(logItem); + + logLines.push(logItem); + + if (match.matched) { + // use unknown protocol so that there is no default port + const addr = new URLParse(`s://${match.groups?.address?.trim()}`); + + args.onFind?.(); + stream.off("data", handler); + clearTimeout(timeoutID); + resolve(+addr.port); + } + }; + const timeoutID = setTimeout(() => { + stream.off("data", handler); + logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.rawMatcher}`, logLines); + reject(new Error("failed to retrieve port from stream")); + }, args.timeout ?? 15000); + + stream.on("data", handler); + }); + }; + }, +}); + +export default getPortFromStreamInjectable; + diff --git a/src/main/utils/get-port.ts b/src/main/utils/get-port.ts deleted file mode 100644 index b0d7f5615f..0000000000 --- a/src/main/utils/get-port.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Readable } from "stream"; -import URLParse from "url-parse"; -import logger from "../logger"; - -interface GetPortArgs { - /** - * Should be case insensitive - * Must have a named matching group called `address` - */ - lineRegex: { - match: (line: string) => { - matched: boolean; - groups?: { - address?: string; - }; - raw?: RegExpExecArray; - }; - rawMatcher: string; - }; - /** - * Called when the port is found - */ - onFind?: () => void; - /** - * Timeout for how long to wait for the port. - * Default: 15s - */ - timeout?: number; -} - -/** - * Parse lines from `stream` (assumes data comes in lines) to find the port - * which the source of the stream is watching on. - * @param stream A readable stream to match lines against - * @param args The args concerning the stream - * @returns A Promise for port number - */ -export function getPortFrom(stream: Readable, args: GetPortArgs): Promise { - const logLines: string[] = []; - - return new Promise((resolve, reject) => { - const handler = (data: unknown) => { - const logItem = String(data); - const match = args.lineRegex.match(logItem); - - logLines.push(logItem); - - if (match.matched) { - // use unknown protocol so that there is no default port - const addr = new URLParse(`s://${match.groups?.address?.trim()}`); - - args.onFind?.(); - stream.off("data", handler); - clearTimeout(timeoutID); - resolve(+addr.port); - } - }; - const timeoutID = setTimeout(() => { - stream.off("data", handler); - logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.rawMatcher}`, logLines); - reject(new Error("failed to retrieve port from stream")); - }, args.timeout ?? 15000); - - stream.on("data", handler); - }); -} diff --git a/src/main/vars/application-information.global-override-for-injectable.ts b/src/main/vars/application-information.global-override-for-injectable.ts deleted file mode 100644 index acc47ce83d..0000000000 --- a/src/main/vars/application-information.global-override-for-injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 applicationInformationInjectable from "../../common/vars/application-information-injectable"; - -export default getGlobalOverride(applicationInformationInjectable, () => ({ - name: "some-product-name", - productName: "some-product-name", - version: "6.0.0", - build: {} as any, - config: { - k8sProxyVersion: "0.2.1", - bundledKubectlVersion: "1.23.3", - bundledHelmVersion: "3.7.2", - sentryDsn: "", - contentSecurityPolicy: "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", - welcomeRoute: "/welcome", - }, - copyright: "some-copyright-information", - description: "some-descriptive-text", -})); diff --git a/src/main/vars/hardware-acceleration-should-be-disabled.global-override-for-injectable.ts b/src/main/vars/hardware-acceleration-should-be-disabled.global-override-for-injectable.ts new file mode 100644 index 0000000000..2f66f9fc18 --- /dev/null +++ b/src/main/vars/hardware-acceleration-should-be-disabled.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * 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 hardwareAccelerationShouldBeDisabledInjectable from "./hardware-acceleration-should-be-disabled.injectable"; + +export default getGlobalOverride(hardwareAccelerationShouldBeDisabledInjectable, () => false); diff --git a/src/main/vars/hardware-acceleration-should-be-disabled.injectable.ts b/src/main/vars/hardware-acceleration-should-be-disabled.injectable.ts new file mode 100644 index 0000000000..2801b1051d --- /dev/null +++ b/src/main/vars/hardware-acceleration-should-be-disabled.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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"; + +const hardwareAccelerationShouldBeDisabledInjectable = getInjectable({ + id: "hardware-acceleration-should-be-disabled", + instantiate: () => Boolean(process.env.LENS_DISABLE_GPU), + causesSideEffects: true, +}); + +export default hardwareAccelerationShouldBeDisabledInjectable; diff --git a/src/renderer/api/__tests__/websocket-api.test.ts b/src/renderer/api/__tests__/websocket-api.test.ts index 5dda62b60c..8b0c4e27b7 100644 --- a/src/renderer/api/__tests__/websocket-api.test.ts +++ b/src/renderer/api/__tests__/websocket-api.test.ts @@ -3,6 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import defaultWebsocketApiParamsInjectable from "../default-websocket-api-params.injectable"; import type { WebSocketEvents } from "../websocket-api"; import { WebSocketApi } from "../websocket-api"; @@ -16,7 +18,11 @@ describe("WebsocketApi tests", () => { let api: TestWebSocketApi; beforeEach(() => { - api = new TestWebSocketApi({}); + const di = getDiForUnitTesting({ doGeneralOverrides: true }); + + api = new TestWebSocketApi({ + defaultParams: di.inject(defaultWebsocketApiParamsInjectable), + }, {}); }); describe("before connection", () => { diff --git a/src/renderer/api/catalog-category-registry.ts b/src/renderer/api/catalog-category-registry.ts deleted file mode 100644 index 9023c5a755..0000000000 --- a/src/renderer/api/catalog-category-registry.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; -import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; - -export type { CategoryFilter } from "../../common/catalog"; - -export const catalogCategoryRegistry = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable); diff --git a/src/renderer/api/create-terminal-api.injectable.ts b/src/renderer/api/create-terminal-api.injectable.ts index 255fa396f3..63581623bf 100644 --- a/src/renderer/api/create-terminal-api.injectable.ts +++ b/src/renderer/api/create-terminal-api.injectable.ts @@ -4,8 +4,10 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; +import loggerInjectable from "../../common/logger.injectable"; import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-id.injectable"; -import type { TerminalApiQuery } from "./terminal-api"; +import defaultWebsocketApiParamsInjectable from "./default-websocket-api-params.injectable"; +import type { TerminalApiDependencies, TerminalApiQuery } from "./terminal-api"; import { TerminalApi } from "./terminal-api"; export type CreateTerminalApi = (query: TerminalApiQuery) => TerminalApi; @@ -14,12 +16,17 @@ const createTerminalApiInjectable = getInjectable({ id: "create-terminal-api", instantiate: (di): CreateTerminalApi => { const hostedClusterId = di.inject(hostedClusterIdInjectable); + const deps: Omit = { + logger: di.inject(loggerInjectable), + defaultParams: di.inject(defaultWebsocketApiParamsInjectable), + }; return (query) => { assert(hostedClusterId, "Can only create terminal APIs within a cluster frame"); return new TerminalApi({ hostedClusterId, + ...deps, }, query); }; }, diff --git a/src/renderer/api/default-websocket-api-params.injectable.ts b/src/renderer/api/default-websocket-api-params.injectable.ts new file mode 100644 index 0000000000..2ff3284a27 --- /dev/null +++ b/src/renderer/api/default-websocket-api-params.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 type { TerminalMessage } from "../../common/terminal/channels"; +import { TerminalChannels } from "../../common/terminal/channels"; +import isDevelopmentInjectable from "../../common/vars/is-development.injectable"; + +export type DefaultWebsocketApiParams = ReturnType; + +const defaultWebsocketApiParamsInjectable = getInjectable({ + id: "default-websocket-api-params", + instantiate: (di) => ({ + logging: di.inject(isDevelopmentInjectable), + reconnectDelay: 10, + flushOnOpen: true, + pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage), + }), +}); + +export default defaultWebsocketApiParamsInjectable; diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 75d907616c..fadd544bb6 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -3,13 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { WebSocketEvents } from "./websocket-api"; +import type { WebSocketApiDependencies, WebSocketEvents } from "./websocket-api"; import { WebSocketApi } from "./websocket-api"; import isEqual from "lodash/isEqual"; import url from "url"; import { makeObservable, observable } from "mobx"; import { ipcRenderer } from "electron"; -import logger from "../../common/logger"; +import type { Logger } from "../../common/logger"; import { once } from "lodash"; import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels"; @@ -36,8 +36,9 @@ export interface TerminalEvents extends WebSocketEvents { connected: () => void; } -export interface TerminalApiDependencies { +export interface TerminalApiDependencies extends WebSocketApiDependencies { readonly hostedClusterId: string; + readonly logger: Logger; } export class TerminalApi extends WebSocketApi { @@ -46,7 +47,7 @@ export class TerminalApi extends WebSocketApi { @observable public isReady = false; constructor(protected readonly dependencies: TerminalApiDependencies, protected readonly query: TerminalApiQuery) { - super({ + super(dependencies, { flushOnOpen: false, pingInterval: 30, }); @@ -145,11 +146,11 @@ export class TerminalApi extends WebSocketApi { this.emit("connected"); break; default: - logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message); + this.dependencies.logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message); break; } } catch (error) { - logger.error(`[TERMINAL-API]: failed to handle message`, error); + this.dependencies.logger.error(`[TERMINAL-API]: failed to handle message`, error); } } diff --git a/src/renderer/api/websocket-api.ts b/src/renderer/api/websocket-api.ts index 74c0e5067a..b172655dab 100644 --- a/src/renderer/api/websocket-api.ts +++ b/src/renderer/api/websocket-api.ts @@ -7,9 +7,8 @@ import { observable, makeObservable } from "mobx"; import EventEmitter from "events"; import type TypedEventEmitter from "typed-emitter"; import type { Arguments } from "typed-emitter"; -import { isDevelopment } from "../../common/vars"; import type { Defaulted } from "../utils"; -import { TerminalChannels, type TerminalMessage } from "../../common/terminal/channels"; +import type { DefaultWebsocketApiParams } from "./default-websocket-api-params.injectable"; interface WebsocketApiParams { /** @@ -64,27 +63,24 @@ export interface WebSocketEvents { close: () => void; } +export interface WebSocketApiDependencies { + readonly defaultParams: DefaultWebsocketApiParams; +} + export class WebSocketApi extends (EventEmitter as { new(): TypedEventEmitter }) { protected socket: WebSocket | null = null; protected pendingCommands: string[] = []; protected reconnectTimer?: number; protected pingTimer?: number; - protected params: Defaulted; + protected params: Defaulted; @observable readyState = WebSocketApiState.PENDING; - private static readonly defaultParams = { - logging: isDevelopment, - reconnectDelay: 10, - flushOnOpen: true, - pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage), - }; - - constructor(params: WebsocketApiParams) { + constructor(protected readonly dependencies: WebSocketApiDependencies, params: WebsocketApiParams) { super(); makeObservable(this); this.params = { - ...WebSocketApi.defaultParams, + ...this.dependencies.defaultParams, ...params, }; const { pingInterval } = this.params; diff --git a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts index ecfac3136a..64bc9dee58 100644 --- a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts +++ b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts @@ -7,11 +7,14 @@ import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manag import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resource.store"; import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints"; +import type { KubeApiDependencies } from "../../../common/k8s-api/kube-api"; import { KubeApi } from "../../../common/k8s-api/kube-api"; import { KubeObject } from "../../../common/k8s-api/kube-object"; import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens"; import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable"; const setupAutoRegistrationInjectable = getInjectable({ id: "setup-auto-registration", @@ -21,8 +24,13 @@ const setupAutoRegistrationInjectable = getInjectable({ const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable); const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = []; const beforeApiManagerInitializationApis: KubeApi[] = []; - const deps: KubeObjectStoreDependencies = { + const kubeApiDependencies: KubeApiDependencies = { + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }; + const kubeObjectStoreDependencies: KubeObjectStoreDependencies = { context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }; let initialized = false; @@ -40,7 +48,7 @@ const setupAutoRegistrationInjectable = getInjectable({ return rawApi; } - const api = new KubeApi({ objectConstructor }); + const api = new KubeApi(kubeApiDependencies, { objectConstructor }); apiManager.registerApi(api); @@ -48,7 +56,7 @@ const setupAutoRegistrationInjectable = getInjectable({ })(); if (!apiManager.getStore(api)) { - apiManager.registerStore(new CustomResourceStore(deps, api)); + apiManager.registerStore(new CustomResourceStore(kubeObjectStoreDependencies, api)); } }; const autoInitKubeApi = (api: KubeApi) => { diff --git a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts index 73cc988e2c..99a3bfa9f3 100644 --- a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts +++ b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts @@ -42,7 +42,7 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({ if (result.error) { logger.error("[KUBERNETES-CLUSTER]: failed to parse kubeconfig file", result.error); } else { - openDeleteClusterDialog({ cluster, config: result.config }); + openDeleteClusterDialog(result.config, cluster); } }, }); diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index d37f561c05..f509847c74 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -13,7 +13,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable"; import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable"; import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; -import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable"; +import initRootFrameInjectable from "./frames/root-frame/init-root-frame.injectable"; import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable"; import { Router } from "react-router"; import historyInjectable from "./navigation/history.injectable"; diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 5af1e13b48..92fa0b7a03 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -16,17 +16,19 @@ import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers" import { docsUrl } from "../../../common/vars"; import { isDefined, iter } from "../../utils"; import { Button } from "../button"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { SettingLayout } from "../layout/setting-layout"; import { MonacoEditor } from "../monaco-editor"; import { withInjectables } from "@ogre-tools/injectable-react"; -import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import type { EmitAppEvent } from "../../../common/app-event-bus/emit-event.injectable"; import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable"; import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; +import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; interface Option { config: KubeConfig; @@ -38,6 +40,8 @@ interface Dependencies { navigateToCatalog: NavigateToCatalog; getDirnameOfPath: GetDirnameOfPath; emitAppEvent: EmitAppEvent; + showSuccessNotification: ShowNotification; + showErrorNotification: ShowNotification; } function getContexts(config: KubeConfig): Map { @@ -97,11 +101,11 @@ class NonInjectedAddCluster extends React.Component { await fse.ensureDir(this.props.getDirnameOfPath(absPath)); await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 }); - Notifications.ok(`Successfully added ${this.kubeContexts.size} new cluster(s)`); + this.props.showSuccessNotification(`Successfully added ${this.kubeContexts.size} new cluster(s)`); return this.props.navigateToCatalog(); } catch (error) { - Notifications.error(`Failed to add clusters: ${error}`); + this.props.showErrorNotification(`Failed to add clusters: ${error}`); } }); @@ -159,9 +163,11 @@ class NonInjectedAddCluster extends React.Component { export const AddCluster = withInjectables(NonInjectedAddCluster, { getProps: (di) => ({ - getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigDirectoryInjectable), + getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigFilePathInjectable), navigateToCatalog: di.inject(navigateToCatalogInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable), emitAppEvent: di.inject(emitAppEventInjectable), + showSuccessNotification: di.inject(showSuccessNotificationInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+catalog/catalog-add-button.tsx b/src/renderer/components/+catalog/catalog-add-button.tsx index 227dd811b5..ee7ca30757 100644 --- a/src/renderer/components/+catalog/catalog-add-button.tsx +++ b/src/renderer/components/+catalog/catalog-add-button.tsx @@ -10,10 +10,13 @@ import { Icon } from "../icon"; import { observer } from "mobx-react"; import { observable, makeObservable, action } from "mobx"; import { autoBind } from "../../../common/utils"; -import type { CatalogCategory, CatalogEntityAddMenuContext, CatalogEntityAddMenu } from "../../api/catalog-entity"; +import type { CatalogCategory, CatalogEntityAddMenu } from "../../api/catalog-entity"; import { EventEmitter } from "events"; -import { navigate } from "../../navigation"; -import { catalogCategoryRegistry } from "../../api/catalog-category-registry"; +import type { CatalogCategoryRegistry } from "../../../common/catalog"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable"; +import type { Navigate } from "../../navigation/navigate.injectable"; +import navigateInjectable from "../../navigation/navigate.injectable"; export interface CatalogAddButtonProps { category: CatalogCategory; @@ -21,12 +24,17 @@ export interface CatalogAddButtonProps { type CategoryId = string; +interface Dependencies { + catalogCategoryRegistry: CatalogCategoryRegistry; + navigate: Navigate; +} + @observer -export class CatalogAddButton extends React.Component { +class NonInjectedCatalogAddButton extends React.Component { @observable protected isOpen = false; @observable menuItems = new Map(); - constructor(props: CatalogAddButtonProps) { + constructor(props: CatalogAddButtonProps & Dependencies) { super(props); makeObservable(this); autoBind(this); @@ -43,7 +51,7 @@ export class CatalogAddButton extends React.Component { } get categories() { - return catalogCategoryRegistry.filteredItems; + return this.props.catalogCategoryRegistry.filteredItems; } @action @@ -61,12 +69,11 @@ export class CatalogAddButton extends React.Component { updateCategoryItems = action((category: CatalogCategory) => { if (category instanceof EventEmitter) { const menuItems: CatalogEntityAddMenu[] = []; - const context: CatalogEntityAddMenuContext = { - navigate: (url: string) => navigate(url), - menuItems, - }; - category.emit("catalogAddMenu", context); + category.emit("catalogAddMenu", { + navigate: this.props.navigate, + menuItems, + }); this.menuItems.set(category.getId(), menuItems); } }); @@ -133,3 +140,11 @@ export class CatalogAddButton extends React.Component { ); } } + +export const CatalogAddButton = withInjectables(NonInjectedCatalogAddButton, { + getProps: (di, props) => ({ + ...props, + catalogCategoryRegistry: di.inject(catalogCategoryRegistryInjectable), + navigate: di.inject(navigateInjectable), + }), +}); diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index a574874d61..829d7c5c54 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -23,7 +23,7 @@ import type { EventStore } from "../+events/store"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import eventStoreInjectable from "../+events/store.injectable"; import nodeStoreInjectable from "../+nodes/store.injectable"; -import type { PageParam } from "../../navigation"; +import type { PageParam } from "../../navigation/page-param"; import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details.injectable"; import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable"; import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable"; diff --git a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts index a4666adcef..2d6ece0dfd 100644 --- a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts +++ b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts @@ -14,6 +14,7 @@ import assert from "assert"; import nodeStoreInjectable from "../../+nodes/store.injectable"; import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const clusterOverviewStoreInjectable = getInjectable({ id: "cluster-overview-store", @@ -34,6 +35,7 @@ const clusterOverviewStoreInjectable = getInjectable({ nodeStore: di.inject(nodeStoreInjectable), requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, clusterApi); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index ab7ea2c726..4bd5f12a66 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -16,17 +16,19 @@ import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTa import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface HpaDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { apiManager: ApiManager; + logger: Logger; getDetailsUrl: GetDetailsUrl; } @@ -104,7 +106,7 @@ class NonInjectedHpaDetails extends React.Component(NonInje ...props, apiManager: di.inject(apiManagerInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+config-autoscalers/store.injectable.ts b/src/renderer/components/+config-autoscalers/store.injectable.ts index 4d4a3adc21..47a0f25680 100644 --- a/src/renderer/components/+config-autoscalers/store.injectable.ts +++ b/src/renderer/components/+config-autoscalers/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import horizontalPodAutoscalerApiInjectable from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { HorizontalPodAutoscalerStore } from "./store"; @@ -19,6 +20,7 @@ const horizontalPodAutoscalerStoreInjectable = getInjectable({ return new HorizontalPodAutoscalerStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-leases/store.injectable.ts b/src/renderer/components/+config-leases/store.injectable.ts index eb9676ce90..df91a1df54 100644 --- a/src/renderer/components/+config-leases/store.injectable.ts +++ b/src/renderer/components/+config-leases/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import leaseApiInjectable from "../../../common/k8s-api/endpoints/lease.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { LeaseStore } from "./store"; @@ -19,6 +20,7 @@ const leaseStoreInjectable = getInjectable({ return new LeaseStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx index 2da12a3d31..79c3038a5d 100644 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx @@ -12,7 +12,9 @@ import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-ran import { LimitPart, LimitRange, Resource } from "../../../common/k8s-api/endpoints/limit-range.api"; import { DrawerItem } from "../drawer/drawer-item"; import { Badge } from "../badge"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface LimitRangeDetailsProps extends KubeObjectDetailsProps { } @@ -51,10 +53,14 @@ function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) { )); } +interface Dependencies { + logger: Logger; +} + @observer -export class LimitRangeDetails extends React.Component { +class NonInjectedLimitRangeDetails extends React.Component { render() { - const { object: limitRange } = this.props; + const { object: limitRange, logger } = this.props; if (!limitRange) { return null; @@ -97,3 +103,10 @@ export class LimitRangeDetails extends React.Component { ); } } + +export const LimitRangeDetails = withInjectables(NonInjectedLimitRangeDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-limit-ranges/store.injectable.ts b/src/renderer/components/+config-limit-ranges/store.injectable.ts index 494968b60c..f22aadaec1 100644 --- a/src/renderer/components/+config-limit-ranges/store.injectable.ts +++ b/src/renderer/components/+config-limit-ranges/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import limitRangeApiInjectable from "../../../common/k8s-api/endpoints/limit-range.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { LimitRangeStore } from "./store"; @@ -19,6 +20,7 @@ const limitRangeStoreInjectable = getInjectable({ return new LimitRangeStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-maps/store.injectable.ts b/src/renderer/components/+config-maps/store.injectable.ts index 7cbd59e4a5..44945120f0 100644 --- a/src/renderer/components/+config-maps/store.injectable.ts +++ b/src/renderer/components/+config-maps/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import configMapApiInjectable from "../../../common/k8s-api/endpoints/config-map.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { ConfigMapStore } from "./store"; @@ -19,6 +20,7 @@ const configMapStoreInjectable = getInjectable({ return new ConfigMapStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx index b58a42b0d9..a6b6e48342 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx @@ -11,13 +11,19 @@ import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class PodDisruptionBudgetDetails extends React.Component { +class NonInjectedPodDisruptionBudgetDetails extends React.Component { render() { const { object: pdb } = this.props; @@ -27,7 +33,7 @@ export class PodDisruptionBudgetDetails extends React.Component(NonInjectedPodDisruptionBudgetDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts index 697fd5c444..f6bb813455 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts +++ b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import podDisruptionBudgetApiInjectable from "../../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { PodDisruptionBudgetStore } from "./store"; @@ -19,6 +20,7 @@ const podDisruptionBudgetStoreInjectable = getInjectable({ return new PodDisruptionBudgetStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-priority-classes/store.injectable.ts b/src/renderer/components/+config-priority-classes/store.injectable.ts index b01e69602c..acd97aee5b 100644 --- a/src/renderer/components/+config-priority-classes/store.injectable.ts +++ b/src/renderer/components/+config-priority-classes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { PriorityClassStore } from "./store"; @@ -19,6 +20,7 @@ const priorityClassStoreInjectable = getInjectable({ return new PriorityClassStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts new file mode 100644 index 0000000000..76af08fc88 --- /dev/null +++ b/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import addQuotaDialogOpenStateInjectable from "./open-state.injectable"; + +const closeAddQuotaDialogInjectable = getInjectable({ + id: "close-add-quota-dialog", + instantiate: (di) => { + const state = di.inject(addQuotaDialogOpenStateInjectable); + + return action(() => state.set(false)); + }, +}); + +export default closeAddQuotaDialogInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts new file mode 100644 index 0000000000..3f91737e72 --- /dev/null +++ b/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { computed } from "mobx"; +import addQuotaDialogOpenStateInjectable from "./open-state.injectable"; + +const isAddQuotaDialogOpenInjectable = getInjectable({ + id: "is-add-quota-dialog-open", + instantiate: (di) => { + const state = di.inject(addQuotaDialogOpenStateInjectable); + + return computed(() => state.get()); + }, +}); + +export default isAddQuotaDialogOpenInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts new file mode 100644 index 0000000000..11af7e8d38 --- /dev/null +++ b/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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 { observable } from "mobx"; + +const addQuotaDialogOpenStateInjectable = getInjectable({ + id: "add-quota-dialog-open-state", + instantiate: () => observable.box(false), +}); + +export default addQuotaDialogOpenStateInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts new file mode 100644 index 0000000000..243f6b0fd9 --- /dev/null +++ b/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import addQuotaDialogOpenStateInjectable from "./open-state.injectable"; + +const openAddQuotaDialogInjectable = getInjectable({ + id: "open-add-quota-dialog", + instantiate: (di) => { + const state = di.inject(addQuotaDialogOpenStateInjectable); + + return action(() => state.set(true)); + }, +}); + +export default openAddQuotaDialogInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-quota-dialog.scss b/src/renderer/components/+config-resource-quotas/add-dialog/view.scss similarity index 100% rename from src/renderer/components/+config-resource-quotas/add-quota-dialog.scss rename to src/renderer/components/+config-resource-quotas/add-dialog/view.scss diff --git a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx b/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx similarity index 63% rename from src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx rename to src/renderer/components/+config-resource-quotas/add-dialog/view.tsx index 70e0dcba1f..21ee1bde3c 100644 --- a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx +++ b/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx @@ -3,54 +3,63 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./add-quota-dialog.scss"; +import "./view.scss"; import React from "react"; +import type { IComputedValue } from "mobx"; import { computed, observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; -import type { DialogProps } from "../dialog"; -import { Dialog } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import { Input } from "../input"; -import { systemName } from "../input/input_validators"; -import type { IResourceQuotaValues } from "../../../common/k8s-api/endpoints"; -import { resourceQuotaApi } from "../../../common/k8s-api/endpoints"; -import { Select } from "../select"; -import { Icon } from "../icon"; -import { Button } from "../button"; -import { Notifications } from "../notifications"; -import { NamespaceSelect } from "../+namespaces/namespace-select"; -import { SubTitle } from "../layout/sub-title"; +import type { DialogProps } from "../../dialog"; +import { Dialog } from "../../dialog"; +import { Wizard, WizardStep } from "../../wizard"; +import { Input } from "../../input"; +import { systemName } from "../../input/input_validators"; +import type { IResourceQuotaValues, ResourceQuotaApi } from "../../../../common/k8s-api/endpoints"; +import { Select } from "../../select"; +import { Icon } from "../../icon"; +import { Button } from "../../button"; +import { NamespaceSelect } from "../../+namespaces/namespace-select"; +import { SubTitle } from "../../layout/sub-title"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeAddQuotaDialogInjectable from "./close.injectable"; +import isAddQuotaDialogOpenInjectable from "./is-open.injectable"; +import resourceQuotaApiInjectable from "../../../../common/k8s-api/endpoints/resource-quota.api.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface AddQuotaDialogProps extends DialogProps { } -const dialogState = observable.object({ - isOpen: false, -}); +interface Dependencies { + resourceQuotaApi: ResourceQuotaApi; + isAddQuotaDialogOpen: IComputedValue; + closeAddQuotaDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; +} + +const defaultQuotas = JSON.stringify({ + "limits.cpu": "", + "limits.memory": "", + "requests.cpu": "", + "requests.memory": "", + "requests.storage": "", + "persistentvolumeclaims": "", + "count/pods": "", + "count/persistentvolumeclaims": "", + "count/services": "", + "count/secrets": "", + "count/configmaps": "", + "count/replicationcontrollers": "", + "count/deployments.apps": "", + "count/replicasets.apps": "", + "count/statefulsets.apps": "", + "count/jobs.batch": "", + "count/cronjobs.batch": "", + "count/deployments.extensions": "", +} as IResourceQuotaValues); @observer -export class AddQuotaDialog extends React.Component { - static defaultQuotas: IResourceQuotaValues = { - "limits.cpu": "", - "limits.memory": "", - "requests.cpu": "", - "requests.memory": "", - "requests.storage": "", - "persistentvolumeclaims": "", - "count/pods": "", - "count/persistentvolumeclaims": "", - "count/services": "", - "count/secrets": "", - "count/configmaps": "", - "count/replicationcontrollers": "", - "count/deployments.apps": "", - "count/replicasets.apps": "", - "count/statefulsets.apps": "", - "count/jobs.batch": "", - "count/cronjobs.batch": "", - "count/deployments.extensions": "", - }; +class NonInjectedAddQuotaDialog extends React.Component { public defaultNamespace = "default"; @@ -58,21 +67,13 @@ export class AddQuotaDialog extends React.Component { @observable quotaSelectValue: string | null = null; @observable quotaInputValue = ""; @observable namespace: string | null = this.defaultNamespace; - @observable quotas = AddQuotaDialog.defaultQuotas; + @observable quotas: IResourceQuotaValues = JSON.parse(defaultQuotas); - constructor(props: AddQuotaDialogProps) { + constructor(props: AddQuotaDialogProps & Dependencies) { super(props); makeObservable(this); } - static open() { - dialogState.isOpen = true; - } - - static close() { - dialogState.isOpen = false; - } - @computed get quotaEntries() { return Object.entries(this.quotas) .filter(([, value]) => !!value?.trim()); @@ -101,7 +102,7 @@ export class AddQuotaDialog extends React.Component { }; close = () => { - AddQuotaDialog.close(); + this.props.closeAddQuotaDialog(); }; reset = () => { @@ -109,7 +110,7 @@ export class AddQuotaDialog extends React.Component { this.quotaSelectValue = ""; this.quotaInputValue = ""; this.namespace = this.defaultNamespace; - this.quotas = AddQuotaDialog.defaultQuotas; + this.quotas = JSON.parse(defaultQuotas); }; addQuota = async () => { @@ -122,14 +123,14 @@ export class AddQuotaDialog extends React.Component { try { const quotas = Object.fromEntries(this.quotaEntries); - await resourceQuotaApi.create({ namespace, name: quotaName }, { + await this.props.resourceQuotaApi.create({ namespace, name: quotaName }, { spec: { hard: quotas, }, }); this.close(); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while creating ResourceQuota"); + this.props.showCheckedErrorNotification(err, "Unknown error occured while creating ResourceQuota"); } }; @@ -143,14 +144,14 @@ export class AddQuotaDialog extends React.Component { }; render() { - const { ...dialogProps } = this.props; + const { closeAddQuotaDialog, isAddQuotaDialogOpen, resourceQuotaApi, ...dialogProps } = this.props; const header =
Create ResourceQuota
; return ( @@ -244,3 +245,13 @@ export class AddQuotaDialog extends React.Component { ); } } + +export const AddQuotaDialog = withInjectables(NonInjectedAddQuotaDialog, { + getProps: (di, props) => ({ + ...props, + closeAddQuotaDialog: di.inject(closeAddQuotaDialogInjectable), + isAddQuotaDialogOpen: di.inject(isAddQuotaDialogOpenInjectable), + resourceQuotaApi: di.inject(resourceQuotaApiInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx index c02f9b221b..55ae188cbf 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx @@ -13,7 +13,9 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { ResourceQuota } from "../../../common/k8s-api/endpoints/resource-quota.api"; import { LineProgress } from "../line-progress"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps { } @@ -75,8 +77,12 @@ function renderQuotas(quota: ResourceQuota): JSX.Element[] { }); } +interface Dependencies { + logger: Logger; +} + @observer -export class ResourceQuotaDetails extends React.Component { +class NonInjectedResourceQuotaDetails extends React.Component { render() { const { object: quota } = this.props; @@ -85,7 +91,7 @@ export class ResourceQuotaDetails extends React.Component(NonInjectedResourceQuotaDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx index 06cba945ad..28c3355b43 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx @@ -8,7 +8,7 @@ import "./resource-quotas.scss"; import React from "react"; import { observer } from "mobx-react"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { AddQuotaDialog } from "./add-quota-dialog"; +import { AddQuotaDialog } from "./add-dialog/view"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../kube-object/age"; @@ -18,6 +18,7 @@ import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-m import { withInjectables } from "@ogre-tools/injectable-react"; import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import resourceQuotaStoreInjectable from "./store.injectable"; +import openAddQuotaDialogInjectable from "./add-dialog/open.injectable"; enum columnId { name = "name", @@ -28,6 +29,7 @@ enum columnId { interface Dependencies { resourceQuotaStore: ResourceQuotaStore; filterByNamespace: FilterByNamespace; + openAddQuotaDialog: () => void; } @observer @@ -69,7 +71,7 @@ class NonInjectedResourceQuotas extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => AddQuotaDialog.open(), + onAdd: this.props.openAddQuotaDialog, addTooltip: "Create new ResourceQuota", }} /> @@ -84,5 +86,6 @@ export const ResourceQuotas = withInjectables(NonInjectedResourceQ ...props, filterByNamespace: di.inject(filterByNamespaceInjectable), resourceQuotaStore: di.inject(resourceQuotaStoreInjectable), + openAddQuotaDialog: di.inject(openAddQuotaDialogInjectable), }), }); diff --git a/src/renderer/components/+config-resource-quotas/store.injectable.ts b/src/renderer/components/+config-resource-quotas/store.injectable.ts index 4999ca9517..77708a1bf4 100644 --- a/src/renderer/components/+config-resource-quotas/store.injectable.ts +++ b/src/renderer/components/+config-resource-quotas/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import resourceQuotaApiInjectable from "../../../common/k8s-api/endpoints/resource-quota.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { ResourceQuotaStore } from "./store"; @@ -19,6 +20,7 @@ const resourceQuotaStoreInjectable = getInjectable({ return new ResourceQuotaStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-runtime-classes/store.injectable.ts b/src/renderer/components/+config-runtime-classes/store.injectable.ts index 63e8d82526..03d4e50a1a 100644 --- a/src/renderer/components/+config-runtime-classes/store.injectable.ts +++ b/src/renderer/components/+config-runtime-classes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { RuntimeClassStore } from "./store"; @@ -19,6 +20,7 @@ const runtimeClassStoreInjectable = getInjectable({ return new RuntimeClassStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts new file mode 100644 index 0000000000..98bf6addcf --- /dev/null +++ b/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import addSecretDialogOpenStateInjectable from "./state.injectable"; + +const closeAddSecretDialogInjectable = getInjectable({ + id: "close-add-secret-dialog", + instantiate: (di) => { + const state = di.inject(addSecretDialogOpenStateInjectable); + + return action(() => state.set(false)); + }, +}); + +export default closeAddSecretDialogInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts new file mode 100644 index 0000000000..2427695923 --- /dev/null +++ b/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { computed } from "mobx"; +import addSecretDialogOpenStateInjectable from "./state.injectable"; + +const isAddSecretDialogOpenInjectable = getInjectable({ + id: "is-add-secret-dialog-open", + instantiate: (di) => { + const state = di.inject(addSecretDialogOpenStateInjectable); + + return computed(() => state.get()); + }, +}); + +export default isAddSecretDialogOpenInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts new file mode 100644 index 0000000000..971cbe4e7a --- /dev/null +++ b/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import addSecretDialogOpenStateInjectable from "./state.injectable"; + +const openAddSecretDialogInjectable = getInjectable({ + id: "open-add-secret-dialog", + instantiate: (di) => { + const state = di.inject(addSecretDialogOpenStateInjectable); + + return action(() => state.set(true)); + }, +}); + +export default openAddSecretDialogInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts new file mode 100644 index 0000000000..de2533d3cd --- /dev/null +++ b/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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 { observable } from "mobx"; + +const addSecretDialogOpenStateInjectable = getInjectable({ + id: "add-secret-dialog-state", + instantiate: () => observable.box(false), +}); + +export default addSecretDialogOpenStateInjectable; diff --git a/src/renderer/components/+config-secrets/add-secret-dialog.scss b/src/renderer/components/+config-secrets/add-dialog/view.scss similarity index 100% rename from src/renderer/components/+config-secrets/add-secret-dialog.scss rename to src/renderer/components/+config-secrets/add-dialog/view.scss diff --git a/src/renderer/components/+config-secrets/add-secret-dialog.tsx b/src/renderer/components/+config-secrets/add-dialog/view.tsx similarity index 68% rename from src/renderer/components/+config-secrets/add-secret-dialog.tsx rename to src/renderer/components/+config-secrets/add-dialog/view.tsx index c839025f02..cf68db93be 100644 --- a/src/renderer/components/+config-secrets/add-secret-dialog.tsx +++ b/src/renderer/components/+config-secrets/add-dialog/view.tsx @@ -3,26 +3,34 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./add-secret-dialog.scss"; +import "./view.scss"; import React from "react"; +import type { IComputedValue } from "mobx"; import { observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; -import type { DialogProps } from "../dialog"; -import { Dialog } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import { Input } from "../input"; -import { systemName } from "../input/input_validators"; -import { reverseSecretTypeMap, secretApi, SecretType } from "../../../common/k8s-api/endpoints"; -import { SubTitle } from "../layout/sub-title"; -import { NamespaceSelect } from "../+namespaces/namespace-select"; -import { Select } from "../select"; -import { Icon } from "../icon"; -import { base64, iter } from "../../utils"; -import { Notifications } from "../notifications"; +import type { DialogProps } from "../../dialog"; +import { Dialog } from "../../dialog"; +import { Wizard, WizardStep } from "../../wizard"; +import { Input } from "../../input"; +import { systemName } from "../../input/input_validators"; +import type { SecretApi } from "../../../../common/k8s-api/endpoints"; +import { reverseSecretTypeMap, SecretType } from "../../../../common/k8s-api/endpoints"; +import { SubTitle } from "../../layout/sub-title"; +import { NamespaceSelect } from "../../+namespaces/namespace-select"; +import { Select } from "../../select"; +import { Icon } from "../../icon"; +import { base64, iter } from "../../../utils"; import upperFirst from "lodash/upperFirst"; -import { showDetails } from "../kube-detail-params"; -import { fromEntries } from "../../../common/utils/objects"; +import { fromEntries } from "../../../../common/utils/objects"; +import type { ShowDetails } from "../../kube-detail-params/show-details.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeAddSecretDialogInjectable from "./close.injectable"; +import secretApiInjectable from "../../../../common/k8s-api/endpoints/secret.api.injectable"; +import showDetailsInjectable from "../../kube-detail-params/show-details.injectable"; +import isAddSecretDialogOpenInjectable from "./is-open.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface AddSecretDialogProps extends Partial { } @@ -42,25 +50,21 @@ interface SecretTemplate { type ISecretField = keyof SecretTemplate; -const dialogState = observable.object({ - isOpen: false, -}); +interface Dependencies { + secretApi: SecretApi; + isAddSecretDialogOpen: IComputedValue; + closeAddSecretDialog: () => void; + showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; +} @observer -export class AddSecretDialog extends React.Component { - constructor(props: AddSecretDialogProps) { +class NonInjectedAddSecretDialog extends React.Component { + constructor(props: AddSecretDialogProps & Dependencies) { super(props); makeObservable(this); } - static open() { - dialogState.isOpen = true; - } - - static close() { - dialogState.isOpen = false; - } - private secretTemplate: Partial> = { [SecretType.Opaque]: {}, [SecretType.ServiceAccountToken]: { @@ -82,7 +86,7 @@ export class AddSecretDialog extends React.Component { }; close = () => { - AddSecretDialog.close(); + this.props.closeAddSecretDialog(); }; private getDataFromFields = (fields: SecretTemplateField[] = [], processValue: (val: string) => string = (val => val)) => { @@ -100,7 +104,7 @@ export class AddSecretDialog extends React.Component { const { data = [], labels = [], annotations = [] } = this.secret[type] ?? {}; try { - const newSecret = await secretApi.create({ namespace, name }, { + const newSecret = await this.props.secretApi.create({ namespace, name }, { type, data: this.getDataFromFields(data, val => val ? base64.encode(val) : ""), metadata: { @@ -111,10 +115,10 @@ export class AddSecretDialog extends React.Component { }, }); - showDetails(newSecret?.selfLink); + this.props.showDetails(newSecret?.selfLink); this.close(); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while creating a Secret"); + this.props.showCheckedErrorNotification(err, "Unknown error occured while creating a Secret"); } }; @@ -183,7 +187,7 @@ export class AddSecretDialog extends React.Component { } render() { - const { ...dialogProps } = this.props; + const { closeAddSecretDialog, isAddSecretDialogOpen, secretApi, showDetails, ...dialogProps } = this.props; const { namespace, name, type } = this; const header =
Create Secret
; @@ -191,7 +195,7 @@ export class AddSecretDialog extends React.Component { @@ -244,3 +248,14 @@ export class AddSecretDialog extends React.Component { ); } } + +export const AddSecretDialog = withInjectables(NonInjectedAddSecretDialog, { + getProps: (di, props) => ({ + ...props, + closeAddSecretDialog: di.inject(closeAddSecretDialogInjectable), + secretApi: di.inject(secretApiInjectable), + showDetails: di.inject(showDetailsInjectable), + isAddSecretDialogOpen: di.inject(isAddSecretDialogOpenInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+config-secrets/secrets.tsx b/src/renderer/components/+config-secrets/secrets.tsx index eda3269fd0..c58d06ce18 100644 --- a/src/renderer/components/+config-secrets/secrets.tsx +++ b/src/renderer/components/+config-secrets/secrets.tsx @@ -7,7 +7,7 @@ import "./secrets.scss"; import React from "react"; import { observer } from "mobx-react"; -import { AddSecretDialog } from "./add-secret-dialog"; +import { AddSecretDialog } from "./add-dialog/view"; import { KubeObjectListLayout } from "../kube-object-list-layout"; import { Badge } from "../badge"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -19,6 +19,7 @@ import type { SecretStore } from "./store"; import { withInjectables } from "@ogre-tools/injectable-react"; import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import secretStoreInjectable from "./store.injectable"; +import openAddSecretDialogInjectable from "./add-dialog/open.injectable"; enum columnId { name = "name", @@ -32,6 +33,7 @@ enum columnId { interface Dependencies { filterByNamespace: FilterByNamespace; secretStore: SecretStore; + openAddSecretDialog: () => void; } @observer @@ -89,7 +91,7 @@ class NonInjectedSecrets extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => AddSecretDialog.open(), + onAdd: this.props.openAddSecretDialog, addTooltip: "Create new Secret", }} /> @@ -104,5 +106,6 @@ export const Secrets = withInjectables(NonInjectedSecrets, { ...props, filterByNamespace: di.inject(filterByNamespaceInjectable), secretStore: di.inject(secretStoreInjectable), + openAddSecretDialog: di.inject(openAddSecretDialogInjectable), }), }); diff --git a/src/renderer/components/+config-secrets/store.injectable.ts b/src/renderer/components/+config-secrets/store.injectable.ts index 2af7bf13f6..d98f3677c1 100644 --- a/src/renderer/components/+config-secrets/store.injectable.ts +++ b/src/renderer/components/+config-secrets/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { SecretStore } from "./store"; @@ -19,6 +20,7 @@ const secretStoreInjectable = getInjectable({ return new SecretStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index d3023a020e..23194dbc73 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -15,13 +15,19 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Table, TableCell, TableHead, TableRow } from "../table"; import { Input } from "../input"; import { MonacoEditor } from "../monaco-editor"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface CRDDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class CRDDetails extends React.Component { +class NonInjectedCRDDetails extends React.Component { render() { const { object: crd } = this.props; @@ -30,7 +36,7 @@ export class CRDDetails extends React.Component { } if (!(crd instanceof CustomResourceDefinition)) { - logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd); + this.props.logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd); return null; } @@ -152,3 +158,10 @@ export class CRDDetails extends React.Component { ); } } + +export const CRDDetails = withInjectables(NonInjectedCRDDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+custom-resources/crd-list.tsx b/src/renderer/components/+custom-resources/crd-list.tsx index a4c10e820d..669ca3e374 100644 --- a/src/renderer/components/+custom-resources/crd-list.tsx +++ b/src/renderer/components/+custom-resources/crd-list.tsx @@ -11,12 +11,11 @@ import { observer } from "mobx-react"; import { Link } from "react-router-dom"; import { iter, stopPropagation } from "../../utils"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { customResourceDefinitionStore } from "./legacy-store"; import { Select } from "../select"; import { Icon } from "../icon"; import { KubeObjectAge } from "../kube-object/age"; import { TabLayout } from "../layout/tab-layout-2"; -import type { PageParam } from "../../navigation"; +import type { PageParam } from "../../navigation/page-param"; import type { CustomResourceDefinitionStore } from "./definition.store"; import { withInjectables } from "@ogre-tools/injectable-react"; import crdGroupsUrlParamInjectable from "./crd-groups-url-param.injectable"; @@ -87,7 +86,7 @@ class NonInjectedCustomResourceDefinitions extends React.Component isConfigurable tableId="crd" className="CrdList" - store={customResourceDefinitionStore} + store={this.props.customResourceDefinitionStore} // Don't subscribe the `customResourceDefinitionStore` because already has and is always mounted subscribeStores={false} items={this.items} diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index 01f8b3a175..149812f663 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -17,7 +17,9 @@ import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/cust import { safeJSONPathValue } from "../../utils/jsonPath"; import type { KubeObjectMetadata, KubeObjectStatus } from "../../../common/k8s-api/kube-object"; import { KubeObject } from "../../../common/k8s-api/kube-object"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface CustomResourceDetailsProps extends KubeObjectDetailsProps { crd?: CustomResourceDefinition; @@ -59,8 +61,12 @@ function convertSpecValue(value: unknown): React.ReactNode { return null; } +interface Dependencies { + logger: Logger; +} + @observer -export class CustomResourceDetails extends React.Component { +class NonInjectedCustomResourceDetails extends React.Component { renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) { return columns.map(({ name, jsonPath }) => ( @@ -106,7 +112,7 @@ export class CustomResourceDetails extends React.Component(NonInjectedCustomResourceDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+custom-resources/definition.store.injectable.ts b/src/renderer/components/+custom-resources/definition.store.injectable.ts index b93f656df9..47c7652f8b 100644 --- a/src/renderer/components/+custom-resources/definition.store.injectable.ts +++ b/src/renderer/components/+custom-resources/definition.store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { CustomResourceDefinitionStore } from "./definition.store"; @@ -21,6 +22,7 @@ const customResourceDefinitionStoreInjectable = getInjectable({ return new CustomResourceDefinitionStore({ autoRegistration: di.inject(autoRegistrationEmitterInjectable), context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+custom-resources/legacy-store.ts b/src/renderer/components/+custom-resources/legacy-store.ts deleted file mode 100644 index 6fcce0b2fb..0000000000 --- a/src/renderer/components/+custom-resources/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import customResourceDefinitionStoreInjectable from "./definition.store.injectable"; - -/** - * @deprecated use `di.inject(customResourceDefinitionStoreInjectable)` instead - */ -export const customResourceDefinitionStore = asLegacyGlobalForExtensionApi(customResourceDefinitionStoreInjectable); diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index 5334744054..07842b6f78 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -14,13 +14,14 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { DurationAbsoluteTimestamp } from "./duration-absolute"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import { cssNames } from "../../utils"; +import loggerInjectable from "../../../common/logger.injectable"; export interface EventDetailsProps extends KubeObjectDetailsProps { } @@ -28,6 +29,7 @@ export interface EventDetailsProps extends KubeObjectDetailsProps { interface Dependencies { getDetailsUrl: GetDetailsUrl; apiManager: ApiManager; + logger: Logger; } const NonInjectedEventDetails = observer(({ @@ -35,6 +37,7 @@ const NonInjectedEventDetails = observer(({ getDetailsUrl, object: event, className, + logger, }: Dependencies & EventDetailsProps) => { if (!event) { return null; @@ -101,5 +104,6 @@ export const EventDetails = withInjectables(Non ...props, apiManager: di.inject(apiManagerInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+events/kube-event-details.tsx b/src/renderer/components/+events/kube-event-details.tsx index 29cb84b856..aef3bf7b10 100644 --- a/src/renderer/components/+events/kube-event-details.tsx +++ b/src/renderer/components/+events/kube-event-details.tsx @@ -12,12 +12,13 @@ import { DrawerItem, DrawerTitle } from "../drawer"; import { cssNames } from "../../utils"; import { LocaleDate } from "../locale-date"; import type { EventStore } from "./store"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import eventStoreInjectable from "./store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface KubeEventDetailsProps { object: KubeObject; @@ -26,6 +27,7 @@ export interface KubeEventDetailsProps { interface Dependencies { subscribeStores: SubscribeStores; eventStore: EventStore; + logger: Logger; } @observer @@ -46,7 +48,7 @@ class NonInjectedKubeEventDetails extends React.Component KubeEvent[]; } +interface Dependencies { + eventStore: EventStore; +} -const defaultProps: Partial = { - showWarningsOnly: true, -}; - -export class KubeEventIcon extends React.Component { - static defaultProps = defaultProps as object; +class NonInjectedKubeEventIcon extends React.Component { + get showWarningsOnly() { + return this.props.showWarningsOnly ?? true; + } render() { - const { object, showWarningsOnly, filterEvents } = this.props; + const { object, filterEvents, eventStore } = this.props; const events = eventStore.getEventsByObject(object); let warnings = events.filter(evt => evt.isWarning()); if (filterEvents) warnings = filterEvents(warnings); - if (!events.length || (showWarningsOnly && !warnings.length)) { + if (!events.length || (this.showWarningsOnly && !warnings.length)) { return null; } const event = [...warnings, ...events][0]; // get latest event @@ -58,3 +61,10 @@ export class KubeEventIcon extends React.Component { ); } } + +export const KubeEventIcon = withInjectables(NonInjectedKubeEventIcon, { + getProps: (di, props) => ({ + ...props, + eventStore: di.inject(eventStoreInjectable), + }), +}); diff --git a/src/renderer/components/+events/legacy-store.ts b/src/renderer/components/+events/legacy-store.ts deleted file mode 100644 index 2a7378e12c..0000000000 --- a/src/renderer/components/+events/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import eventStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(eventStoreInjectable)` instead - */ -export const eventStore = asLegacyGlobalForExtensionApi(eventStoreInjectable); diff --git a/src/renderer/components/+events/store.injectable.ts b/src/renderer/components/+events/store.injectable.ts index a911670935..57bc98e6d2 100644 --- a/src/renderer/components/+events/store.injectable.ts +++ b/src/renderer/components/+events/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getPodByIdInjectable from "../+workloads-pods/get-pod-by-id.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import kubeEventApiInjectable from "../../../common/k8s-api/endpoints/events.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { EventStore } from "./store"; @@ -21,6 +22,7 @@ const eventStoreInjectable = getInjectable({ return new EventStore({ getPodById: di.inject(getPodByIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx b/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx index cb73df7237..47b18a3772 100644 --- a/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx +++ b/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; -import uninstallExtensionInjectable from "../uninstall-extension/uninstall-extension.injectable"; +import uninstallExtensionInjectable from "../uninstall-extension.injectable"; import type { UnpackExtension } from "./unpack-extension.injectable"; import unpackExtensionInjectable from "./unpack-extension.injectable"; import type { GetExtensionDestFolder } from "./get-extension-dest-folder.injectable"; @@ -14,7 +14,7 @@ import createTempFilesAndValidateInjectable from "./create-temp-files-and-valida import extensionInstallationStateStoreInjectable from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import type { Disposer } from "../../../../common/utils"; import { disposer } from "../../../../common/utils"; -import { Notifications } from "../../notifications"; +import type { ShowNotification } from "../../notifications"; import { Button } from "../../button"; import type { ExtensionLoader } from "../../../../extensions/extension-loader"; import type { LensExtensionId } from "../../../../extensions/lens-extension"; @@ -23,6 +23,8 @@ import { remove as removeDir } from "fs-extra"; import { shell } from "electron"; import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store"; import { ExtensionInstallationState } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store"; +import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable"; +import showInfoNotificationInjectable from "../../notifications/show-info-notification.injectable"; export interface InstallRequest { fileName: string; @@ -36,6 +38,8 @@ interface Dependencies { createTempFilesAndValidate: CreateTempFilesAndValidate; getExtensionDestFolder: GetExtensionDestFolder; installStateStore: ExtensionInstallationStateStore; + showErrorNotification: ShowNotification; + showInfoNotification: ShowNotification; } export type AttemptInstall = (request: InstallRequest, cleanup?: Disposer) => Promise; @@ -47,6 +51,8 @@ const attemptInstall = ({ createTempFilesAndValidate, getExtensionDestFolder, installStateStore, + showErrorNotification, + showInfoNotification, }: Dependencies): AttemptInstall => async (request, cleanup) => { const dispose = disposer( @@ -66,7 +72,7 @@ const attemptInstall = ({ if (curState !== ExtensionInstallationState.IDLE) { dispose(); - return void Notifications.error( + return void showErrorNotification(
Extension Install Collision:

@@ -86,7 +92,7 @@ const attemptInstall = ({ const { version: oldVersion } = installedExtension.manifest; // confirm to uninstall old version before installing new version - const removeNotification = Notifications.info( + const removeNotification = showInfoNotification(

@@ -142,6 +148,8 @@ const attemptInstallInjectable = getInjectable({ createTempFilesAndValidate: di.inject(createTempFilesAndValidateInjectable), getExtensionDestFolder: di.inject(getExtensionDestFolderInjectable), installStateStore: di.inject(extensionInstallationStateStoreInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + showInfoNotification: di.inject(showInfoNotificationInjectable), }), }); diff --git a/src/renderer/components/+extensions/confirm-uninstall-extension.injectable.tsx b/src/renderer/components/+extensions/confirm-uninstall-extension.injectable.tsx index 2157d32b21..fd832e8eb4 100644 --- a/src/renderer/components/+extensions/confirm-uninstall-extension.injectable.tsx +++ b/src/renderer/components/+extensions/confirm-uninstall-extension.injectable.tsx @@ -9,7 +9,7 @@ import type { LensExtensionId } from "../../../extensions/lens-extension"; import { extensionDisplayName } from "../../../extensions/lens-extension"; import type { Confirm } from "../confirm-dialog/confirm.injectable"; import confirmInjectable from "../confirm-dialog/confirm.injectable"; -import uninstallExtensionInjectable from "./uninstall-extension/uninstall-extension.injectable"; +import uninstallExtensionInjectable from "./uninstall-extension.injectable"; interface Dependencies { uninstallExtension: (id: LensExtensionId) => Promise; diff --git a/src/renderer/components/+extensions/uninstall-extension.injectable.tsx b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx new file mode 100644 index 0000000000..fa7cf89fb8 --- /dev/null +++ b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx @@ -0,0 +1,84 @@ +/** + * 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 extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; +import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; +import extensionDiscoveryInjectable from "../../../extensions/extension-discovery/extension-discovery.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { LensExtensionId } from "../../../extensions/lens-extension"; +import { extensionDisplayName } from "../../../extensions/lens-extension"; +import React from "react"; +import { when } from "mobx"; +import { getMessageFromError } from "./get-message-from-error/get-message-from-error"; +import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; + +const uninstallExtensionInjectable = getInjectable({ + id: "uninstall-extension", + + instantiate: (di) => { + const extensionLoader = di.inject(extensionLoaderInjectable); + const extensionDiscovery = di.inject(extensionDiscoveryInjectable); + const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable); + const logger = di.inject(loggerInjectable); + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); + const showErrorNotification = di.inject(showErrorNotificationInjectable); + + return async (extensionId: LensExtensionId): Promise => { + const ext = extensionLoader.getExtension(extensionId); + + if (!ext) { + logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`); + + return true; + } + + const { manifest } = ext; + const displayName = extensionDisplayName(manifest.name, manifest.version); + + try { + logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`); + extensionInstallationStateStore.setUninstalling(extensionId); + + await extensionDiscovery.uninstallExtension(extensionId); + + // wait for the ExtensionLoader to actually uninstall the extension + await when(() => !extensionLoader.userExtensions.has(extensionId)); + + showSuccessNotification( +

+ {"Extension "} + {displayName} + {" successfully uninstalled!"} +

, + ); + + return true; + } catch (error) { + const message = getMessageFromError(error); + + logger.info( + `[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`, + { error }, + ); + showErrorNotification( +

+ {"Uninstalling extension "} + {displayName} + {" has failed: "} + {message} +

, + ); + + return false; + } finally { + // Remove uninstall state on uninstall failure + extensionInstallationStateStore.clearUninstalling(extensionId); + } + }; + }, +}); + +export default uninstallExtensionInjectable; diff --git a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts b/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts deleted file mode 100644 index a060ed7e88..0000000000 --- a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts +++ /dev/null @@ -1,24 +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 extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; -import { uninstallExtension } from "./uninstall-extension"; -import extensionInstallationStateStoreInjectable - from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; -import extensionDiscoveryInjectable - from "../../../../extensions/extension-discovery/extension-discovery.injectable"; - -const uninstallExtensionInjectable = getInjectable({ - id: "uninstall-extension", - - instantiate: (di) => - uninstallExtension({ - extensionLoader: di.inject(extensionLoaderInjectable), - extensionDiscovery: di.inject(extensionDiscoveryInjectable), - extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), - }), -}); - -export default uninstallExtensionInjectable; diff --git a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx b/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx deleted file mode 100644 index 3db43813a2..0000000000 --- a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { ExtensionLoader } from "../../../../extensions/extension-loader"; -import type { LensExtensionId } from "../../../../extensions/lens-extension"; -import { extensionDisplayName } from "../../../../extensions/lens-extension"; -import logger from "../../../../main/logger"; -import type { ExtensionDiscovery } from "../../../../extensions/extension-discovery/extension-discovery"; -import { Notifications } from "../../notifications"; -import React from "react"; -import { when } from "mobx"; -import { getMessageFromError } from "../get-message-from-error/get-message-from-error"; -import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store"; - -interface Dependencies { - extensionLoader: ExtensionLoader; - extensionDiscovery: ExtensionDiscovery; - extensionInstallationStateStore: ExtensionInstallationStateStore; -} - -export const uninstallExtension = ({ - extensionLoader, - extensionDiscovery, - extensionInstallationStateStore, -}: Dependencies) => ( - async (extensionId: LensExtensionId): Promise => { - const ext = extensionLoader.getExtension(extensionId); - - if (!ext) { - logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`); - - return true; - } - - const { manifest } = ext; - const displayName = extensionDisplayName(manifest.name, manifest.version); - - try { - logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`); - extensionInstallationStateStore.setUninstalling(extensionId); - - await extensionDiscovery.uninstallExtension(extensionId); - - // wait for the ExtensionLoader to actually uninstall the extension - await when(() => !extensionLoader.userExtensions.has(extensionId)); - - Notifications.ok( -

- {"Extension "} - {displayName} - {" successfully uninstalled!"} -

, - ); - - return true; - } catch (error) { - const message = getMessageFromError(error); - - logger.info( - `[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`, - { error }, - ); - Notifications.error( -

- {"Uninstalling extension "} - {displayName} - {" has failed: "} - {message} -

, - ); - - return false; - } finally { - // Remove uninstall state on uninstall failure - extensionInstallationStateStore.clearUninstalling(extensionId); - } - } -); diff --git a/src/renderer/components/+helm-releases/dialog/dialog.tsx b/src/renderer/components/+helm-releases/dialog/dialog.tsx index 9a7c3373d4..1fa6370cbc 100644 --- a/src/renderer/components/+helm-releases/dialog/dialog.tsx +++ b/src/renderer/components/+helm-releases/dialog/dialog.tsx @@ -14,7 +14,6 @@ import { Dialog } from "../../dialog"; import { Wizard, WizardStep } from "../../wizard"; import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api"; import { Select } from "../../select"; -import { Notifications } from "../../notifications"; import orderBy from "lodash/orderBy"; import { withInjectables } from "@ogre-tools/injectable-react"; import releaseRollbackDialogStateInjectable from "./state.injectable"; @@ -22,6 +21,8 @@ import type { RollbackRelease } from "../rollback-release/rollback-release.injec import rollbackReleaseInjectable from "../rollback-release/rollback-release.injectable"; import type { HelmReleaseRevision, RequestHelmReleaseHistory } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable"; import requestHelmReleaseHistoryInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface ReleaseRollbackDialogProps extends DialogProps { } @@ -30,6 +31,7 @@ interface Dependencies { rollbackRelease: RollbackRelease; state: IObservableValue; requestHelmReleaseHistory: RequestHelmReleaseHistory; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -71,7 +73,7 @@ class NonInjectedReleaseRollbackDialog extends React.Component; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -63,7 +65,7 @@ class NonInjectedAddNamespaceDialog extends React.Component", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + fetchMock = asyncFn(); + di.override(fetchInjectable, () => fetchMock); + const createCluster = di.inject(createClusterInjectable); di.override(hostedClusterInjectable, () => createCluster({ @@ -71,9 +74,6 @@ describe("", () => { cleanup = disposer(subscribeStores([namespaceStore])); - fetchMock = asyncFn(); - di.override(fetchInjectable, () => fetchMock); - const render = renderFor(di); result = render(( diff --git a/src/renderer/components/+namespaces/route.tsx b/src/renderer/components/+namespaces/route.tsx index 1ef8856348..7efbfb738e 100644 --- a/src/renderer/components/+namespaces/route.tsx +++ b/src/renderer/components/+namespaces/route.tsx @@ -29,7 +29,7 @@ interface Dependencies { openAddNamespaceDialog: () => void; } -export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies) => ( +const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies) => ( ("selected_namespaces", undefined), clusterConfiguredAccessibleNamespaces: di.inject(clusterConfiguredAccessibleNamespacesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+namespaces/store.ts b/src/renderer/components/+namespaces/store.ts index 4c178a8a96..78f836e807 100644 --- a/src/renderer/components/+namespaces/store.ts +++ b/src/renderer/components/+namespaces/store.ts @@ -23,12 +23,7 @@ export class NamespaceStore extends KubeObjectStore { makeObservable(this); autoBind(this); - this.init(); - } - - private async init() { - await this.dependencies.storage.whenReady; - + // initialize allowed namespaces const { allowedNamespaces } = this; const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load diff --git a/src/renderer/components/+network-endpoints/endpoint-details.tsx b/src/renderer/components/+network-endpoints/endpoint-details.tsx index 4e014595b7..6e806683e8 100644 --- a/src/renderer/components/+network-endpoints/endpoint-details.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-details.tsx @@ -11,13 +11,19 @@ import { DrawerTitle } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Endpoints } from "../../../common/k8s-api/endpoints"; import { EndpointSubsetList } from "./endpoint-subset-list"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface EndpointsDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class EndpointsDetails extends React.Component { +class NonInjectedEndpointsDetails extends React.Component { render() { const { object: endpoint } = this.props; @@ -26,7 +32,7 @@ export class EndpointsDetails extends React.Component { } if (!(endpoint instanceof Endpoints)) { - logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint); + this.props.logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint); return null; } @@ -47,3 +53,10 @@ export class EndpointsDetails extends React.Component { ); } } + +export const EndpointsDetails = withInjectables(NonInjectedEndpointsDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+network-endpoints/store.injectable.ts b/src/renderer/components/+network-endpoints/store.injectable.ts index 7531b933b3..d376b039a0 100644 --- a/src/renderer/components/+network-endpoints/store.injectable.ts +++ b/src/renderer/components/+network-endpoints/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import endpointsApiInjectable from "../../../common/k8s-api/endpoints/endpoint.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { EndpointsStore } from "./store"; @@ -19,6 +20,7 @@ const endpointsStoreInjectable = getInjectable({ return new EndpointsStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts b/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts index 20b7a4d4bc..9379b4ff59 100644 --- a/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts +++ b/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts @@ -4,15 +4,13 @@ */ import assert from "assert"; import { getInjectable } from "@ogre-tools/injectable"; -import { - kubeObjectStoreInjectionToken, -} from "../../../common/k8s-api/api-manager/manager.injectable"; -import ingressClassApiInjectable - from "../../../common/k8s-api/endpoints/ingress-class.api.injectable"; +import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; +import ingressClassApiInjectable from "../../../common/k8s-api/endpoints/ingress-class.api.injectable"; import { IngressClassStore } from "./ingress-class-store"; -import clusterFrameContextForNamespacedResourcesInjectable - from "../../cluster-frame-context/for-namespaced-resources.injectable"; + import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; +import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const ingressClassStoreInjectable = getInjectable({ id: "ingress-class-store", @@ -23,7 +21,8 @@ const ingressClassStoreInjectable = getInjectable({ const api = di.inject(ingressClassApiInjectable); return new IngressClassStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, diff --git a/src/renderer/components/+network-ingresses/ingress-store.injectable.ts b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts index 909c784b99..8f5f1e6e39 100644 --- a/src/renderer/components/+network-ingresses/ingress-store.injectable.ts +++ b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { IngressStore } from "./ingress-store"; @@ -19,6 +20,7 @@ const ingressStoreInjectable = getInjectable({ return new IngressStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-policies/network-policy-details.tsx b/src/renderer/components/+network-policies/network-policy-details.tsx index 60eec92e91..08b03e7186 100644 --- a/src/renderer/components/+network-policies/network-policy-details.tsx +++ b/src/renderer/components/+network-policies/network-policy-details.tsx @@ -13,15 +13,21 @@ import { Badge } from "../badge"; import { SubTitle } from "../layout/sub-title"; import { observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object"; import { isEmpty } from "lodash"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface NetworkPolicyDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class NetworkPolicyDetails extends React.Component { +class NonInjectedNetworkPolicyDetails extends React.Component { renderIPolicyIpBlock(ipBlock: IPolicyIpBlock | undefined) { if (!ipBlock) { return null; @@ -159,7 +165,7 @@ export class NetworkPolicyDetails extends React.Component(NonInjectedNetworkPolicyDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+network-policies/store.injectable.ts b/src/renderer/components/+network-policies/store.injectable.ts index ad79a89b54..b17c0918d9 100644 --- a/src/renderer/components/+network-policies/store.injectable.ts +++ b/src/renderer/components/+network-policies/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import networkPolicyApiInjectable from "../../../common/k8s-api/endpoints/network-policy.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { NetworkPolicyStore } from "./store"; @@ -19,6 +20,7 @@ const networkPolicyStoreInjectable = getInjectable({ return new NetworkPolicyStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-port-forwards/port-forward-details.tsx b/src/renderer/components/+network-port-forwards/port-forward-details.tsx index dbc3156660..ba59ad7c10 100644 --- a/src/renderer/components/+network-port-forwards/port-forward-details.tsx +++ b/src/renderer/components/+network-port-forwards/port-forward-details.tsx @@ -11,19 +11,33 @@ import type { PortForwardItem } from "../../port-forward"; import { portForwardAddress } from "../../port-forward"; import { Drawer, DrawerItem } from "../drawer"; import { cssNames } from "../../utils"; -import { podApi, serviceApi } from "../../../common/k8s-api/endpoints"; -import { getDetailsUrl } from "../kube-detail-params"; +import type { PodApi, ServiceApi } from "../../../common/k8s-api/endpoints"; import { PortForwardMenu } from "./port-forward-menu"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable"; +import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; +import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; +import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable"; export interface PortForwardDetailsProps { portForward: PortForwardItem; hideDetails(): void; } -export class PortForwardDetails extends React.Component { +interface Dependencies { + serviceApi: ServiceApi; + getDetailsUrl: GetDetailsUrl; + podApi: PodApi; +} +class NonInjectedPortForwardDetails extends React.Component { renderResourceName() { - const { portForward } = this.props; + const { + portForward, + serviceApi, + podApi, + getDetailsUrl, + } = this.props; const name = portForward.getName(); const api = { "service": serviceApi, @@ -37,7 +51,7 @@ export class PortForwardDetails extends React.Component } return ( - + {name} ); @@ -99,3 +113,12 @@ export class PortForwardDetails extends React.Component ); } } + +export const PortForwardDetails = withInjectables(NonInjectedPortForwardDetails, { + getProps: (di, props) => ({ + ...props, + serviceApi: di.inject(serviceApiInjectable), + getDetailsUrl: di.inject(getDetailsUrlInjectable), + podApi: di.inject(podApiInjectable), + }), +}); diff --git a/src/renderer/components/+network-port-forwards/port-forward-menu.tsx b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx index a0edc693a6..5fe46e1994 100644 --- a/src/renderer/components/+network-port-forwards/port-forward-menu.tsx +++ b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx @@ -10,12 +10,13 @@ import type { MenuActionsProps } from "../menu/menu-actions"; import { MenuActions } from "../menu/menu-actions"; import { MenuItem } from "../menu"; import { Icon } from "../icon"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { withInjectables } from "@ogre-tools/injectable-react"; import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable"; import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable"; import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; export interface PortForwardMenuProps extends MenuActionsProps { portForward: PortForwardItem; @@ -26,6 +27,7 @@ interface Dependencies { portForwardStore: PortForwardStore; openPortForwardDialog: (item: PortForwardItem) => void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } class NonInjectedPortForwardMenu extends React.Component { @@ -35,12 +37,12 @@ class NonInjectedPortForwardMenu { - const { portForward } = this.props; + const { portForward, showErrorNotification } = this.props; const pf = await this.portForwardStore.start(portForward); if (pf.status === "Disabled") { const { name, kind, forwardPort } = portForward; - Notifications.error(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`); + showErrorNotification(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`); } }; @@ -134,15 +136,12 @@ class NonInjectedPortForwardMenu( - NonInjectedPortForwardMenu, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const PortForwardMenu = withInjectables(NonInjectedPortForwardMenu, { + getProps: (di, props) => ({ + ...props, + portForwardStore: di.inject(portForwardStoreInjectable), + openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, + openPortForward: di.inject(openPortForwardInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 998a8c6576..a9d5e8a93c 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -15,12 +15,13 @@ import { ServicePortComponent } from "./service-port-component"; import type { EndpointsStore } from "../+network-endpoints/store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import type { PortForwardStore } from "../../port-forward"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import endpointsStoreInjectable from "../+network-endpoints/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface ServiceDetailsProps extends KubeObjectDetailsProps { } @@ -29,6 +30,7 @@ interface Dependencies { subscribeStores: SubscribeStores; portForwardStore: PortForwardStore; endpointsStore: EndpointsStore; + logger: Logger; } @observer @@ -59,7 +61,7 @@ class NonInjectedServiceDetails extends React.Component subscribeStores: di.inject(subscribeStoresInjectable), portForwardStore: di.inject(portForwardStoreInjectable), endpointsStore: di.inject(endpointsStoreInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index 79aeff7ec5..b81ef1350d 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import type { Service, ServicePort } from "../../../common/k8s-api/endpoints"; import { action, makeObservable, observable, reaction } from "mobx"; import { cssNames } from "../../utils"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { Button } from "../button"; import type { ForwardedPort, PortForwardStore } from "../../port-forward"; import { predictProtocol } from "../../port-forward"; @@ -18,11 +18,13 @@ import { Spinner } from "../spinner"; import { withInjectables } from "@ogre-tools/injectable-react"; import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import aboutPortForwardingInjectable from "../../port-forward/about-port-forwarding.injectable"; import notifyErrorPortForwardingInjectable from "../../port-forward/notify-error-port-forwarding.injectable"; import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable"; import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; export interface ServicePortComponentProps { service: Service; @@ -31,10 +33,12 @@ export interface ServicePortComponentProps { interface Dependencies { portForwardStore: PortForwardStore; + logger: Logger; openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void; aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } @observer @@ -127,7 +131,7 @@ class NonInjectedServicePortComponent extends React.Component( - NonInjectedServicePortComponent, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const ServicePortComponent = withInjectables(NonInjectedServicePortComponent, { + getProps: (di, props) => ({ + ...props, + portForwardStore: di.inject(portForwardStoreInjectable), + openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, + aboutPortForwarding: di.inject(aboutPortForwardingInjectable), + notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), + openPortForward: di.inject(openPortForwardInjectable), + logger: di.inject(loggerInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+network-services/store.injectable.ts b/src/renderer/components/+network-services/store.injectable.ts index e54f994162..d705ca0d46 100644 --- a/src/renderer/components/+network-services/store.injectable.ts +++ b/src/renderer/components/+network-services/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { ServiceStore } from "./store"; @@ -19,6 +20,7 @@ const serviceStoreInjectable = getInjectable({ return new ServiceStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+nodes/store.injectable.ts b/src/renderer/components/+nodes/store.injectable.ts index ac149210bd..a397db07e1 100644 --- a/src/renderer/components/+nodes/store.injectable.ts +++ b/src/renderer/components/+nodes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { NodeStore } from "./store"; @@ -19,6 +20,7 @@ const nodeStoreInjectable = getInjectable({ return new NodeStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+pod-security-policies/legacy-store.ts b/src/renderer/components/+pod-security-policies/legacy-store.ts deleted file mode 100644 index 4224463d15..0000000000 --- a/src/renderer/components/+pod-security-policies/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import podSecurityPolicyStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(podSecurityPolicyStoreInjectable)` instead - */ -export const podSecurityPolicyStore = asLegacyGlobalForExtensionApi(podSecurityPolicyStoreInjectable); diff --git a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx index 31d34cf1a5..d39a41e360 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx @@ -8,10 +8,12 @@ import "./pod-security-policies.scss"; import React from "react"; import { observer } from "mobx-react"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { podSecurityPolicyStore } from "./legacy-store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../kube-object/age"; +import type { PodSecurityPolicyStore } from "./store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import podSecurityPolicyStoreInjectable from "./store.injectable"; enum columnId { name = "name", @@ -20,8 +22,12 @@ enum columnId { age = "age", } +interface Dependencies { + podSecurityPolicyStore: PodSecurityPolicyStore; +} + @observer -export class PodSecurityPolicies extends React.Component { +class NonInjectedPodSecurityPolicies extends React.Component { render() { return ( @@ -29,7 +35,7 @@ export class PodSecurityPolicies extends React.Component { isConfigurable tableId="access_pod_security_policies" className="PodSecurityPolicies" - store={podSecurityPolicyStore} + store={this.props.podSecurityPolicyStore} sortingCallbacks={{ [columnId.name]: podSecurityPolicy => podSecurityPolicy.getName(), [columnId.volumes]: podSecurityPolicy => podSecurityPolicy.getVolumes(), @@ -61,3 +67,10 @@ export class PodSecurityPolicies extends React.Component { ); } } + +export const PodSecurityPolicies = withInjectables(NonInjectedPodSecurityPolicies, { + getProps: (di, props) => ({ + ...props, + podSecurityPolicyStore: di.inject(podSecurityPolicyStoreInjectable), + }), +}); diff --git a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx index 03cce618a0..d30ae17898 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx @@ -12,7 +12,9 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodSecurityPolicy } from "../../../common/k8s-api/endpoints"; import { Badge } from "../badge"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps { } @@ -25,8 +27,12 @@ interface RuleGroup { }[]; } +interface Dependencies { + logger: Logger; +} + @observer -export class PodSecurityPolicyDetails extends React.Component { +class NonInjectedPodSecurityPolicyDetails extends React.Component { renderRuleGroup(title: React.ReactNode, group: RuleGroup | undefined) { if (!group) return null; const { rule, ranges } = group; @@ -59,7 +65,7 @@ export class PodSecurityPolicyDetails extends React.Component(NonInjectedPodSecurityPolicyDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+pod-security-policies/store.injectable.ts b/src/renderer/components/+pod-security-policies/store.injectable.ts index 44ab372d70..2e27ad466a 100644 --- a/src/renderer/components/+pod-security-policies/store.injectable.ts +++ b/src/renderer/components/+pod-security-policies/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import podSecurityPolicyApiInjectable from "../../../common/k8s-api/endpoints/pod-security-policy.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { PodSecurityPolicyStore } from "./store"; @@ -19,6 +20,7 @@ const podSecurityPolicyStoreInjectable = getInjectable({ return new PodSecurityPolicyStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+storage-classes/legacy-store.ts b/src/renderer/components/+storage-classes/legacy-store.ts deleted file mode 100644 index 0bc4294e4e..0000000000 --- a/src/renderer/components/+storage-classes/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import storageClassStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(storageClassStoreInjectable)` instead - */ -export const storageClassStore = asLegacyGlobalForExtensionApi(storageClassStoreInjectable); diff --git a/src/renderer/components/+storage-classes/storage-class-details.tsx b/src/renderer/components/+storage-classes/storage-class-details.tsx index 0df28e13f8..14f76cda32 100644 --- a/src/renderer/components/+storage-classes/storage-class-details.tsx +++ b/src/renderer/components/+storage-classes/storage-class-details.tsx @@ -15,12 +15,13 @@ import { StorageClass } from "../../../common/k8s-api/endpoints"; import type { StorageClassStore } from "./store"; import { VolumeDetailsList } from "../+storage-volumes/volume-details-list"; import type { PersistentVolumeStore } from "../+storage-volumes/store"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import storageClassStoreInjectable from "./store.injectable"; import persistentVolumeStoreInjectable from "../+storage-volumes/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface StorageClassDetailsProps extends KubeObjectDetailsProps { } @@ -29,6 +30,7 @@ interface Dependencies { subscribeStores: SubscribeStores; storageClassStore: StorageClassStore; persistentVolumeStore: PersistentVolumeStore; + logger: Logger; } @observer @@ -49,7 +51,7 @@ class NonInjectedStorageClassDetails extends React.Component { render() { return ( @@ -30,7 +36,7 @@ export class StorageClasses extends React.Component { isConfigurable tableId="storage_classes" className="StorageClasses" - store={storageClassStore} + store={this.props.storageClassStore} sortingCallbacks={{ [columnId.name]: storageClass => storageClass.getName(), [columnId.age]: storageClass => -storageClass.getCreationTimestamp(), @@ -63,3 +69,10 @@ export class StorageClasses extends React.Component { ); } } + +export const StorageClasses = withInjectables(NonInjectedStorageClasses, { + getProps: (di, props) => ({ + ...props, + storageClassStore: di.inject(storageClassStoreInjectable), + }), +}); diff --git a/src/renderer/components/+storage-classes/store.injectable.ts b/src/renderer/components/+storage-classes/store.injectable.ts index 17f6da93db..0ae16b0a92 100644 --- a/src/renderer/components/+storage-classes/store.injectable.ts +++ b/src/renderer/components/+storage-classes/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getPersistentVolumesByStorageClassInjectable from "../+storage-volumes/get-persisten-volumes-by-storage-class.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { StorageClassStore } from "./store"; @@ -21,6 +22,7 @@ const storageClassStoreInjectable = getInjectable({ return new StorageClassStore({ getPersistentVolumesByStorageClass: di.inject(getPersistentVolumesByStorageClassInjectable), context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+storage-volume-claims/store.injectable.ts b/src/renderer/components/+storage-volume-claims/store.injectable.ts index 37fdd49a26..ef9b4629fa 100644 --- a/src/renderer/components/+storage-volume-claims/store.injectable.ts +++ b/src/renderer/components/+storage-volume-claims/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { PersistentVolumeClaimStore } from "./store"; @@ -19,6 +20,7 @@ const persistentVolumeClaimStoreInjectable = getInjectable({ return new PersistentVolumeClaimStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+storage-volumes/legacy-store.ts b/src/renderer/components/+storage-volumes/legacy-store.ts deleted file mode 100644 index 68acd228fd..0000000000 --- a/src/renderer/components/+storage-volumes/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import persistentVolumeStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(persistentVolumeStoreInjectable)` instead - */ -export const persistentVolumeStore = asLegacyGlobalForExtensionApi(persistentVolumeStoreInjectable); diff --git a/src/renderer/components/+storage-volumes/store.injectable.ts b/src/renderer/components/+storage-volumes/store.injectable.ts index e316bff399..1432c0ed6c 100644 --- a/src/renderer/components/+storage-volumes/store.injectable.ts +++ b/src/renderer/components/+storage-volumes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import persistentVolumeApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { PersistentVolumeStore } from "./store"; @@ -19,6 +20,7 @@ const persistentVolumeStoreInjectable = getInjectable({ return new PersistentVolumeStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+storage-volumes/volume-details-list.tsx b/src/renderer/components/+storage-volumes/volume-details-list.tsx index e843e410ca..ebc471a933 100644 --- a/src/renderer/components/+storage-volumes/volume-details-list.tsx +++ b/src/renderer/components/+storage-volumes/volume-details-list.tsx @@ -10,14 +10,17 @@ import { observer } from "mobx-react"; import type { PersistentVolume } from "../../../common/k8s-api/endpoints/persistent-volume.api"; import { TableRow } from "../table/table-row"; import { cssNames, prevDefault } from "../../utils"; -import { showDetails } from "../kube-detail-params"; import { TableCell } from "../table/table-cell"; import { Spinner } from "../spinner/spinner"; import { DrawerTitle } from "../drawer/drawer-title"; import { Table } from "../table/table"; import { TableHead } from "../table/table-head"; -import { persistentVolumeStore } from "./legacy-store"; import kebabCase from "lodash/kebabCase"; +import type { PersistentVolumeStore } from "./store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import persistentVolumeStoreInjectable from "./store.injectable"; +import type { ShowDetails } from "../kube-detail-params/show-details.injectable"; +import showDetailsInjectable from "../kube-detail-params/show-details.injectable"; export interface VolumeDetailsListProps { persistentVolumes: PersistentVolume[]; @@ -29,8 +32,13 @@ enum sortBy { capacity = "capacity", } +interface Dependencies { + persistentVolumeStore: PersistentVolumeStore; + showDetails: ShowDetails; +} + @observer -export class VolumeDetailsList extends React.Component { +class NonInjectedVolumeDetailsList extends React.Component { private sortingCallbacks = { [sortBy.name]: (volume: PersistentVolume) => volume.getName(), [sortBy.capacity]: (volume: PersistentVolume) => volume.getCapacity(), @@ -38,7 +46,7 @@ export class VolumeDetailsList extends React.Component { }; getTableRow = (uid: string) => { - const { persistentVolumes } = this.props; + const { persistentVolumes, showDetails } = this.props; const volume = persistentVolumes.find(volume => volume.getId() === uid); if (!volume) { @@ -60,7 +68,7 @@ export class VolumeDetailsList extends React.Component { }; render() { - const { persistentVolumes } = this.props; + const { persistentVolumes, persistentVolumeStore } = this.props; const virtual = persistentVolumes.length > 100; if (!persistentVolumes.length) { @@ -94,3 +102,11 @@ export class VolumeDetailsList extends React.Component { ); } } + +export const VolumeDetailsList = withInjectables(NonInjectedVolumeDetailsList, { + getProps: (di, props) => ({ + ...props, + persistentVolumeStore: di.inject(persistentVolumeStoreInjectable), + showDetails: di.inject(showDetailsInjectable), + }), +}); diff --git a/src/renderer/components/+storage-volumes/volume-details.tsx b/src/renderer/components/+storage-volumes/volume-details.tsx index 2149d200f7..93b3a40195 100644 --- a/src/renderer/components/+storage-volumes/volume-details.tsx +++ b/src/renderer/components/+storage-volumes/volume-details.tsx @@ -11,19 +11,38 @@ import { Link } from "react-router-dom"; import { observer } from "mobx-react"; import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; -import { PersistentVolume, persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints"; +import type { PersistentVolumeClaimApi, StorageClassApi } from "../../../common/k8s-api/endpoints"; +import { PersistentVolume } from "../../../common/k8s-api/endpoints"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { getDetailsUrl } from "../kube-detail-params"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { stopPropagation } from "../../../renderer/utils"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; +import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; +import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; +import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable"; export interface PersistentVolumeDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; + getDetailsUrl: GetDetailsUrl; + storageClassApi: StorageClassApi; + persistentVolumeClaimApi: PersistentVolumeClaimApi; +} + @observer -export class PersistentVolumeDetails extends React.Component { +class NonInjectedPersistentVolumeDetails extends React.Component { render() { - const { object: volume } = this.props; + const { + object: volume, + storageClassApi, + getDetailsUrl, + logger, + persistentVolumeClaimApi, + } = this.props; if (!volume) { return null; @@ -37,7 +56,7 @@ export class PersistentVolumeDetails extends React.Component - + {claimRef.name} @@ -121,3 +140,13 @@ export class PersistentVolumeDetails extends React.Component(NonInjectedPersistentVolumeDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + getDetailsUrl: di.inject(getDetailsUrlInjectable), + persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable), + storageClassApi: di.inject(storageClassApiInjectable), + }), +}); diff --git a/src/renderer/components/+storage-volumes/volumes.tsx b/src/renderer/components/+storage-volumes/volumes.tsx index 4f164453a4..ac3bf08e5a 100644 --- a/src/renderer/components/+storage-volumes/volumes.tsx +++ b/src/renderer/components/+storage-volumes/volumes.tsx @@ -9,13 +9,18 @@ import React from "react"; import { observer } from "mobx-react"; import { Link } from "react-router-dom"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { getDetailsUrl } from "../kube-detail-params"; import { stopPropagation } from "../../utils"; -import { persistentVolumeStore } from "./legacy-store"; -import { persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints"; +import type { PersistentVolumeClaimApi, StorageClassApi } from "../../../common/k8s-api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../kube-object/age"; +import type { PersistentVolumeStore } from "./store"; +import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; +import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; +import persistentVolumeStoreInjectable from "./store.injectable"; +import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable"; enum columnId { name = "name", @@ -26,9 +31,23 @@ enum columnId { age = "age", } +interface Dependencies { + storageClassApi: StorageClassApi; + persistentVolumeStore: PersistentVolumeStore; + persistentVolumeClaimApi: PersistentVolumeClaimApi; + getDetailsUrl: GetDetailsUrl; +} + @observer -export class PersistentVolumes extends React.Component { +class NonInjectedPersistentVolumes extends React.Component { render() { + const { + getDetailsUrl, + persistentVolumeStore, + storageClassApi, + persistentVolumeClaimApi, + } = this.props; + return ( { const { claimRef, storageClassName } = volume.spec; - const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ + const storageClassDetailsUrl = getDetailsUrl(storageClassApi.formatUrlForNotListing({ name: storageClassName, })); @@ -75,7 +94,7 @@ export class PersistentVolumes extends React.Component { , volume.getCapacity(), claimRef && ( - + {claimRef.name} ), @@ -88,3 +107,13 @@ export class PersistentVolumes extends React.Component { ); } } + +export const PersistentVolumes = withInjectables(NonInjectedPersistentVolumes, { + getProps: (di, props) => ({ + ...props, + getDetailsUrl: di.inject(getDetailsUrlInjectable), + persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable), + persistentVolumeStore: di.inject(persistentVolumeStoreInjectable), + storageClassApi: di.inject(storageClassApiInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx index 1bbcf98a56..36d0cce310 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx @@ -4,7 +4,7 @@ */ import React from "react"; -import { ClusterRoleBindingDialog } from "../dialog"; +import { ClusterRoleBindingDialog } from "../dialog/view"; import { ClusterRole } from "../../../../../common/k8s-api/endpoints"; import userEvent from "@testing-library/user-event"; import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; @@ -16,9 +16,15 @@ import directoryForUserDataInjectable from "../../../../../common/app-paths/dire import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable"; import createClusterInjectable from "../../../../cluster/create-cluster.injectable"; +import type { CloseClusterRoleBindingDialog } from "../dialog/close.injectable"; +import closeClusterRoleBindingDialogInjectable from "../dialog/close.injectable"; +import type { OpenClusterRoleBindingDialog } from "../dialog/open.injectable"; +import openClusterRoleBindingDialogInjectable from "../dialog/open.injectable"; describe("ClusterRoleBindingDialog tests", () => { let render: DiRender; + let closeClusterRoleBindingDialog: CloseClusterRoleBindingDialog; + let openClusterRoleBindingDialog: OpenClusterRoleBindingDialog; beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); @@ -27,6 +33,9 @@ describe("ClusterRoleBindingDialog tests", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + closeClusterRoleBindingDialog = di.inject(closeClusterRoleBindingDialogInjectable); + openClusterRoleBindingDialog = di.inject(openClusterRoleBindingDialogInjectable); + const createCluster = di.inject(createClusterInjectable); di.override(hostedClusterInjectable, () => createCluster({ @@ -56,7 +65,7 @@ describe("ClusterRoleBindingDialog tests", () => { }); afterEach(() => { - ClusterRoleBindingDialog.close(); + closeClusterRoleBindingDialog(); jest.resetAllMocks(); }); @@ -67,7 +76,7 @@ describe("ClusterRoleBindingDialog tests", () => { }); it("clusterrole select should be searchable", async () => { - ClusterRoleBindingDialog.open(); + openClusterRoleBindingDialog(); const res = render(); userEvent.keyboard("a"); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx index d78bae4fb1..87de375bed 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx @@ -15,18 +15,22 @@ import { AddRemoveButtons } from "../../add-remove-buttons"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; import { Table, TableCell, TableHead, TableRow } from "../../table"; -import { ClusterRoleBindingDialog } from "./dialog"; -import { clusterRoleBindingStore } from "./legacy-store"; import { hashSubject } from "../hashers"; import type { OpenConfirmDialog } from "../../confirm-dialog/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import openConfirmDialogInjectable from "../../confirm-dialog/open.injectable"; +import type { ClusterRoleBindingStore } from "./store"; +import type { OpenClusterRoleBindingDialog } from "./dialog/open.injectable"; +import openClusterRoleBindingDialogInjectable from "./dialog/open.injectable"; +import clusterRoleBindingStoreInjectable from "./store.injectable"; export interface ClusterRoleBindingDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { openConfirmDialog: OpenConfirmDialog; + openClusterRoleBindingDialog: OpenClusterRoleBindingDialog; + clusterRoleBindingStore: ClusterRoleBindingStore; } @observer @@ -47,7 +51,7 @@ class NonInjectedClusterRoleBindingDetails extends React.Component ClusterRoleBindingDialog.open(clusterRoleBinding)} + onAdd={() => openClusterRoleBindingDialog(clusterRoleBinding)} onRemove={selectedSubjects.size ? this.removeSelectedSubjects : undefined} addTooltip={`Add bindings to ${roleRef.name}`} removeTooltip={`Remove selected bindings from ${roleRef.name}`} @@ -135,5 +139,7 @@ export const ClusterRoleBindingDetails = withInjectables ({ ...props, openConfirmDialog: di.inject(openConfirmDialogInjectable), + openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable), + clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable), }), }); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog/close.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/close.injectable.ts new file mode 100644 index 0000000000..32971c37d7 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/close.injectable.ts @@ -0,0 +1,27 @@ +/** + * 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 { action } from "mobx"; +import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable"; +import clusterRoleBindingDialogStateInjectable from "./state.injectable"; + +export type CloseClusterRoleBindingDialog = () => void; + +const closeClusterRoleBindingDialogInjectable = getInjectable({ + id: "close-cluster-role-binding-dialog", + instantiate: (di): CloseClusterRoleBindingDialog => { + const state = di.inject(clusterRoleBindingDialogStateInjectable); + const editNameState = di.inject(editClusterRoleBindingNameStateInjectable); + + return action(() => { + state.set({ + isOpen: false, + }); + editNameState.set(""); + }); + }, +}); + +export default closeClusterRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog/edit-name-state.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/edit-name-state.injectable.ts new file mode 100644 index 0000000000..b5a3b768ad --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/edit-name-state.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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 { observable } from "mobx"; + +const editClusterRoleBindingNameStateInjectable = getInjectable({ + id: "edit-cluster-role-binding-name-state", + instantiate: () => observable.box(""), +}); + +export default editClusterRoleBindingNameStateInjectable; diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog/open.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/open.injectable.ts new file mode 100644 index 0000000000..44fff0ba43 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/open.injectable.ts @@ -0,0 +1,29 @@ +/** + * 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 { action } from "mobx"; +import type { ClusterRoleBinding } from "../../../../../common/k8s-api/endpoints"; +import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable"; +import clusterRoleBindingDialogStateInjectable from "./state.injectable"; + +export type OpenClusterRoleBindingDialog = (clusterRoleBinding?: ClusterRoleBinding) => void; + +const openClusterRoleBindingDialogInjectable = getInjectable({ + id: "open-cluster-role-binding-dialog", + instantiate: (di): OpenClusterRoleBindingDialog => { + const state = di.inject(clusterRoleBindingDialogStateInjectable); + const editNameState = di.inject(editClusterRoleBindingNameStateInjectable); + + return action((clusterRoleBinding) => { + state.set({ + isOpen: true, + clusterRoleBinding, + }); + editNameState.set(clusterRoleBinding?.getName() ?? ""); + }); + }, +}); + +export default openClusterRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog/state.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/state.injectable.ts new file mode 100644 index 0000000000..6fe2444e33 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/state.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 { observable } from "mobx"; +import type { ClusterRoleBinding } from "../../../../../common/k8s-api/endpoints"; + +export type ClusterRoleBindingDialogState = { + isOpen: false; + clusterRoleBinding?: undefined; +} | { + isOpen: true; + clusterRoleBinding: ClusterRoleBinding | undefined; +}; + +const clusterRoleBindingDialogStateInjectable = getInjectable({ + id: "cluster-role-binding-dialog-state", + instantiate: () => observable.box({ + isOpen: false, + }), +}); + +export default clusterRoleBindingDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog.scss b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+cluster-role-bindings/dialog.scss rename to src/renderer/components/+user-management/+cluster-role-bindings/dialog/view.scss diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/dialog.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/view.tsx similarity index 52% rename from src/renderer/components/+user-management/+cluster-role-bindings/dialog.tsx rename to src/renderer/components/+user-management/+cluster-role-bindings/dialog/view.tsx index 4afa930059..3dd8549ca1 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/dialog.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/dialog/view.tsx @@ -3,75 +3,75 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./dialog.scss"; +import "./view.scss"; -import { action, computed, makeObservable, observable, reaction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; +import type { IObservableValue } from "mobx"; +import { action, computed, makeObservable, observable } from "mobx"; +import { observer } from "mobx-react"; import React from "react"; -import { serviceAccountStore } from "../+service-accounts/legacy-store"; -import type { ClusterRole, ClusterRoleBinding, ServiceAccount } from "../../../../common/k8s-api/endpoints"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { EditableList } from "../../editable-list"; -import { Icon } from "../../icon"; -import { showDetails } from "../../kube-detail-params"; -import { SubTitle } from "../../layout/sub-title"; -import { Notifications } from "../../notifications"; -import { onMultiSelectFor, Select } from "../../select"; -import { Wizard, WizardStep } from "../../wizard"; -import { clusterRoleBindingStore } from "./legacy-store"; -import { clusterRoleStore } from "../+cluster-roles/legacy-store"; -import { ObservableHashSet, nFircate } from "../../../utils"; -import { Input } from "../../input"; -import { TooltipPosition } from "../../tooltip"; -import type { Subject } from "../../../../common/k8s-api/endpoints/types/subject"; +import type { ClusterRole, ServiceAccount } from "../../../../../common/k8s-api/endpoints"; +import type { DialogProps } from "../../../dialog"; +import { Dialog } from "../../../dialog"; +import { EditableList } from "../../../editable-list"; +import { Icon } from "../../../icon"; +import { SubTitle } from "../../../layout/sub-title"; +import { onMultiSelectFor, Select } from "../../../select"; +import { Wizard, WizardStep } from "../../../wizard"; +import { ObservableHashSet, nFircate } from "../../../../utils"; +import { Input } from "../../../input"; +import { TooltipPosition } from "../../../tooltip"; +import type { Subject } from "../../../../../common/k8s-api/endpoints/types/subject"; +import type { ClusterRoleBindingDialogState } from "./state.injectable"; +import type { ClusterRoleStore } from "../../+cluster-roles/store"; +import type { ServiceAccountStore } from "../../+service-accounts/store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable"; +import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable"; +import serviceAccountStoreInjectable from "../../+service-accounts/store.injectable"; +import clusterRoleBindingDialogStateInjectable from "./state.injectable"; +import type { CloseClusterRoleBindingDialog } from "./close.injectable"; +import type { OpenClusterRoleBindingDialog } from "./open.injectable"; +import openClusterRoleBindingDialogInjectable from "./open.injectable"; +import closeClusterRoleBindingDialogInjectable from "./close.injectable"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import type { ClusterRoleBindingStore } from "../store"; +import clusterRoleBindingStoreInjectable from "../store.injectable"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; export interface ClusterRoleBindingDialogProps extends Partial { } -interface DialogState { - isOpen: boolean; - data?: ClusterRoleBinding; +interface Dependencies { + state: IObservableValue; + editBindingNameState: IObservableValue; + clusterRoleStore: ClusterRoleStore; + serviceAccountStore: ServiceAccountStore; + clusterRoleBindingStore: ClusterRoleBindingStore; + closeClusterRoleBindingDialog: CloseClusterRoleBindingDialog; + openClusterRoleBindingDialog: OpenClusterRoleBindingDialog; + showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; } -const dialogState = observable.object({ - isOpen: false, -}); - @observer -export class ClusterRoleBindingDialog extends React.Component { - constructor(props: ClusterRoleBindingDialogProps) { +class NonInjectedClusterRoleBindingDialog extends React.Component { + constructor(props: ClusterRoleBindingDialogProps & Dependencies) { super(props); makeObservable(this); } - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.isEditing, () => { - this.bindingName = dialogState.data?.getName() ?? ""; - }), - ]); - } - - static open(roleBinding?: ClusterRoleBinding) { - dialogState.isOpen = true; - dialogState.data = roleBinding; - } - - static close() { - dialogState.isOpen = false; - } - @computed get clusterRoleOptions() { - return clusterRoleStore.items.map(clusterRole => ({ + return this.props.clusterRoleStore.items.map(clusterRole => ({ value: clusterRole, label: clusterRole.getName(), })); } @computed get serviceAccountOptions() { - return serviceAccountStore.items.map(serviceAccount => ({ + return this.props.serviceAccountStore.items.map(serviceAccount => ({ value: serviceAccount, label: `${serviceAccount.getName()} (${serviceAccount.getNs()})`, isSelected: this.selectedAccounts.has(serviceAccount), @@ -79,7 +79,7 @@ export class ClusterRoleBindingDialog extends React.Component([], sa => sa.metadata.uid); selectedUsers = observable.set([]); selectedGroups = observable.set([]); @@ -121,16 +120,15 @@ export class ClusterRoleBindingDialog extends React.Component item.getName() === binding.roleRef.name); - this.bindingName = binding.getName(); const [saSubjects, uSubjects, gSubjects] = nFircate(binding.getSubjects(), "kind", ["ServiceAccount", "User", "Group"]); const accountNames = new Set(saSubjects.map(acc => acc.name)); this.selectedAccounts.replace( - serviceAccountStore.items + this.props.serviceAccountStore.items .filter(sa => accountNames.has(sa.getName())), ); this.selectedUsers.replace(uSubjects.map(user => user.name)); @@ -139,14 +137,19 @@ export class ClusterRoleBindingDialog extends React.Component { this.selectedRoleRef = undefined; - this.bindingName = ""; this.selectedAccounts.clear(); this.selectedUsers.clear(); this.selectedGroups.clear(); }); createBindings = async () => { - const { selectedRoleRef, selectedBindings, bindingName, clusterRoleBinding } = this; + const { + closeClusterRoleBindingDialog, + clusterRoleBindingStore, + editBindingNameState, + showDetails, + } = this.props; + const { selectedRoleRef, selectedBindings, clusterRoleBinding } = this; if (!clusterRoleBinding || !selectedRoleRef) { return; @@ -155,7 +158,7 @@ export class ClusterRoleBindingDialog extends React.Component { this.selectedRoleRef = option?.value; + const bindingName = this.props.editBindingNameState.get(); - if (!this.selectedRoleRef || this.bindingName === this.selectedRoleRef.getName()) { - this.bindingName = option?.value?.getName() ?? ""; + if (!this.selectedRoleRef || bindingName === this.selectedRoleRef.getName()) { + this.props.editBindingNameState.set(option?.value?.getName() ?? ""); } }} /> @@ -209,8 +213,8 @@ export class ClusterRoleBindingDialog extends React.Component this.bindingName = val} + value={this.props.editBindingNameState.get()} + onChange={val => this.props.editBindingNameState.set(val)} /> @@ -252,16 +256,24 @@ export class ClusterRoleBindingDialog extends React.Component @@ -271,7 +283,7 @@ export class ClusterRoleBindingDialog extends React.Component )} - done={ClusterRoleBindingDialog.close} + done={closeClusterRoleBindingDialog} > (NonInjectedClusterRoleBindingDialog, { + getProps: (di, props) => ({ + ...props, + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + editBindingNameState: di.inject(editClusterRoleBindingNameStateInjectable), + serviceAccountStore: di.inject(serviceAccountStoreInjectable), + state: di.inject(clusterRoleBindingDialogStateInjectable), + clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable), + openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable), + closeClusterRoleBindingDialog: di.inject(closeClusterRoleBindingDialogInjectable), + showDetails: di.inject(showDetailsInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/index.ts b/src/renderer/components/+user-management/+cluster-role-bindings/index.ts index 0dd1376b97..59d5a476cb 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/index.ts +++ b/src/renderer/components/+user-management/+cluster-role-bindings/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./dialog"; +export * from "./dialog/view"; diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/legacy-store.ts b/src/renderer/components/+user-management/+cluster-role-bindings/legacy-store.ts deleted file mode 100644 index 6eb3bf50d4..0000000000 --- a/src/renderer/components/+user-management/+cluster-role-bindings/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import clusterRoleBindingStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(clusterRoleBindingStoreInjectable)` instead - */ -export const clusterRoleBindingStore = asLegacyGlobalForExtensionApi(clusterRoleBindingStoreInjectable); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts index a2e3223fa7..ac3eb8a07d 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts +++ b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts @@ -9,6 +9,7 @@ import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoint import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import { ClusterRoleBindingStore } from "./store"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const clusterRoleBindingStoreInjectable = getInjectable({ id: "cluster-role-binding-store", @@ -19,6 +20,7 @@ const clusterRoleBindingStoreInjectable = getInjectable({ return new ClusterRoleBindingStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx index 4daf4c001f..ced8c892e7 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx @@ -9,12 +9,18 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { ClusterRoleBindingDialog } from "./dialog"; -import { clusterRoleBindingStore } from "./legacy-store"; -import { clusterRoleStore } from "../+cluster-roles/legacy-store"; -import { serviceAccountStore } from "../+service-accounts/legacy-store"; +import { ClusterRoleBindingDialog } from "./dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; +import type { ClusterRoleBindingStore } from "./store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import clusterRoleBindingStoreInjectable from "./store.injectable"; +import type { ClusterRoleStore } from "../+cluster-roles/store"; +import type { ServiceAccountStore } from "../+service-accounts/store"; +import clusterRoleStoreInjectable from "../+cluster-roles/store.injectable"; +import serviceAccountStoreInjectable from "../+service-accounts/store.injectable"; +import type { OpenClusterRoleBindingDialog } from "./dialog/open.injectable"; +import openClusterRoleBindingDialogInjectable from "./dialog/open.injectable"; enum columnId { name = "name", @@ -23,9 +29,23 @@ enum columnId { age = "age", } +interface Dependencies { + clusterRoleBindingStore: ClusterRoleBindingStore; + clusterRoleStore: ClusterRoleStore; + serviceAccountStore: ServiceAccountStore; + openClusterRoleBindingDialog: OpenClusterRoleBindingDialog; +} + @observer -export class ClusterRoleBindings extends React.Component { +class NonInjectedClusterRoleBindings extends React.Component { render() { + const { + clusterRoleBindingStore, + clusterRoleStore, + serviceAccountStore, + openClusterRoleBindingDialog, + } = this.props; + return ( , ]} addRemoveButtons={{ - onAdd: () => ClusterRoleBindingDialog.open(), + onAdd: () => openClusterRoleBindingDialog(), addTooltip: "Create new ClusterRoleBinding", }} /> @@ -66,3 +86,13 @@ export class ClusterRoleBindings extends React.Component { ); } } + +export const ClusterRoleBindings = withInjectables(NonInjectedClusterRoleBindings, { + getProps: (di, props) => ({ + ...props, + clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable), + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + serviceAccountStore: di.inject(serviceAccountStoreInjectable), + openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx b/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx deleted file mode 100644 index 01c6cc4831..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import "./add-dialog.scss"; - -import { makeObservable, observable } from "mobx"; -import { observer } from "mobx-react"; -import React from "react"; - -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Input } from "../../input"; -import { showDetails } from "../../kube-detail-params"; -import { SubTitle } from "../../layout/sub-title"; -import { Notifications } from "../../notifications"; -import { Wizard, WizardStep } from "../../wizard"; -import { clusterRoleStore } from "./legacy-store"; - -export interface AddClusterRoleDialogProps extends Partial { -} - -@observer -export class AddClusterRoleDialog extends React.Component { - static isOpen = observable.box(false); - - @observable clusterRoleName = ""; - - constructor(props: AddClusterRoleDialogProps) { - super(props); - makeObservable(this); - } - - static open() { - AddClusterRoleDialog.isOpen.set(true); - } - - static close() { - AddClusterRoleDialog.isOpen.set(false); - } - - reset = () => { - this.clusterRoleName = ""; - }; - - createRole = async () => { - try { - const role = await clusterRoleStore.create({ name: this.clusterRoleName }); - - showDetails(role.selfLink); - this.reset(); - AddClusterRoleDialog.close(); - } catch (error) { - Notifications.checkedError(error, "Unknown error occured while creating the role"); - } - }; - - render() { - const { ...dialogProps } = this.props; - - return ( - - Create ClusterRole} - done={AddClusterRoleDialog.close} - > - - - this.clusterRoleName = v} - /> - - - - ); - } -} diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog/close.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/add-dialog/close.injectable.ts new file mode 100644 index 0000000000..5444403db1 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog/close.injectable.ts @@ -0,0 +1,21 @@ +/** + * 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 { action } from "mobx"; +import addClusterRoleDialogStateInjectable from "./state.injectable"; + +const closeAddClusterRoleDialogInjectable = getInjectable({ + id: "close-add-cluster-role-dialog", + instantiate: (di) => { + const state = di.inject(addClusterRoleDialogStateInjectable); + + return action(() => { + state.isOpen.set(false); + state.clusterRoleName.set(""); + }); + }, +}); + +export default closeAddClusterRoleDialogInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog/open.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/add-dialog/open.injectable.ts new file mode 100644 index 0000000000..2c2a41a0b8 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog/open.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 { action } from "mobx"; +import addClusterRoleDialogStateInjectable from "./state.injectable"; + +export type OpenAddClusterRoleDialog = () => void; + +const openAddClusterRoleDialogInjectable = getInjectable({ + id: "open-add-cluster-role-dialog", + instantiate: (di) => { + const state = di.inject(addClusterRoleDialogStateInjectable); + + return action(() => { + state.isOpen.set(true); + state.clusterRoleName.set(""); + }); + }, +}); + +export default openAddClusterRoleDialogInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog/state.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/add-dialog/state.injectable.ts new file mode 100644 index 0000000000..542d4a5796 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog/state.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 type { IObservableValue } from "mobx"; +import { observable } from "mobx"; + +export interface AddClusterRoleDialogState { + isOpen: IObservableValue; + clusterRoleName: IObservableValue; +} + +const addClusterRoleDialogStateInjectable = getInjectable({ + id: "add-cluster-role-dialog-open-state", + instantiate: (): AddClusterRoleDialogState => ({ + clusterRoleName: observable.box(""), + isOpen: observable.box(false), + }), +}); + +export default addClusterRoleDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss b/src/renderer/components/+user-management/+cluster-roles/add-dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+cluster-roles/add-dialog.scss rename to src/renderer/components/+user-management/+cluster-roles/add-dialog/view.scss diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog/view.tsx b/src/renderer/components/+user-management/+cluster-roles/add-dialog/view.tsx new file mode 100644 index 0000000000..5da4a1fe00 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog/view.tsx @@ -0,0 +1,102 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import "./view.scss"; + +import { observer } from "mobx-react"; +import React from "react"; + +import type { DialogProps } from "../../../dialog"; +import { Dialog } from "../../../dialog"; +import { Input } from "../../../input"; +import { SubTitle } from "../../../layout/sub-title"; +import { Wizard, WizardStep } from "../../../wizard"; +import type { AddClusterRoleDialogState } from "./state.injectable"; +import type { ClusterRoleStore } from "../store"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeAddClusterRoleDialogInjectable from "./close.injectable"; +import clusterRoleStoreInjectable from "../store.injectable"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import addClusterRoleDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; + +export interface AddClusterRoleDialogProps extends Partial { +} + +interface Dependencies { + state: AddClusterRoleDialogState; + clusterRoleStore: ClusterRoleStore; + showDetails: ShowDetails; + closeAddClusterRoleDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; +} + +@observer +class NonInjectedAddClusterRoleDialog extends React.Component { + createRole = async () => { + const { + closeAddClusterRoleDialog, + clusterRoleStore, + showDetails, + state, + showCheckedErrorNotification, + } = this.props; + + try { + const role = await clusterRoleStore.create({ name: state.clusterRoleName.get() }); + + showDetails(role.selfLink); + closeAddClusterRoleDialog(); + } catch (error) { + showCheckedErrorNotification(error, "Unknown error occured while creating the role"); + } + }; + + render() { + const { closeAddClusterRoleDialog, clusterRoleStore, showDetails, state, ...dialogProps } = this.props; + + return ( + + Create ClusterRole} + done={closeAddClusterRoleDialog} + > + + + state.clusterRoleName.set(v)} + /> + + + + ); + } +} + +export const AddClusterRoleDialog = withInjectables(NonInjectedAddClusterRoleDialog, { + getProps: (di, props) => ({ + ...props, + closeAddClusterRoleDialog: di.inject(closeAddClusterRoleDialogInjectable), + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + showDetails: di.inject(showDetailsInjectable), + state: di.inject(addClusterRoleDialogStateInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+cluster-roles/index.ts b/src/renderer/components/+user-management/+cluster-roles/index.ts index 2b770fa1cd..f55fa5914b 100644 --- a/src/renderer/components/+user-management/+cluster-roles/index.ts +++ b/src/renderer/components/+user-management/+cluster-roles/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./add-dialog"; +export * from "./add-dialog/view"; diff --git a/src/renderer/components/+user-management/+cluster-roles/legacy-store.ts b/src/renderer/components/+user-management/+cluster-roles/legacy-store.ts deleted file mode 100644 index 79a9811591..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import clusterRoleStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(clusterRoleStoreInjectable)` instead - */ -export const clusterRoleStore = asLegacyGlobalForExtensionApi(clusterRoleStoreInjectable); diff --git a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts index 9ae877f635..c88b457b1d 100644 --- a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts +++ b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts @@ -9,6 +9,7 @@ import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/clust import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import { ClusterRoleStore } from "./store"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const clusterRoleStoreInjectable = getInjectable({ id: "cluster-role-store", @@ -19,6 +20,7 @@ const clusterRoleStoreInjectable = getInjectable({ return new ClusterRoleStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+cluster-roles/view.tsx b/src/renderer/components/+user-management/+cluster-roles/view.tsx index 86334aebe3..8a64e775b3 100644 --- a/src/renderer/components/+user-management/+cluster-roles/view.tsx +++ b/src/renderer/components/+user-management/+cluster-roles/view.tsx @@ -9,10 +9,14 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { AddClusterRoleDialog } from "./add-dialog"; -import { clusterRoleStore } from "./legacy-store"; +import { AddClusterRoleDialog } from "./add-dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; +import type { ClusterRoleStore } from "./store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import clusterRoleStoreInjectable from "./store.injectable"; +import type { OpenAddClusterRoleDialog } from "./add-dialog/open.injectable"; +import openAddClusterRoleDialogInjectable from "./add-dialog/open.injectable"; enum columnId { name = "name", @@ -20,9 +24,19 @@ enum columnId { age = "age", } +interface Dependencies { + clusterRoleStore: ClusterRoleStore; + openAddClusterRoleDialog: OpenAddClusterRoleDialog; +} + @observer -export class ClusterRoles extends React.Component { +class NonInjectedClusterRoles extends React.Component { render() { + const { + openAddClusterRoleDialog, + clusterRoleStore, + } = this.props; + return ( , ]} addRemoveButtons={{ - onAdd: () => AddClusterRoleDialog.open(), + onAdd: () => openAddClusterRoleDialog(), addTooltip: "Create new ClusterRole", }} /> @@ -58,3 +72,11 @@ export class ClusterRoles extends React.Component { ); } } + +export const ClusterRoles = withInjectables(NonInjectedClusterRoles, { + getProps: (di, props) => ({ + ...props, + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + openAddClusterRoleDialog: di.inject(openAddClusterRoleDialogInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx index 08067ed817..67d3b38fe1 100644 --- a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx +++ b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx @@ -6,7 +6,7 @@ import userEvent from "@testing-library/user-event"; import React from "react"; import { ClusterRole } from "../../../../../common/k8s-api/endpoints"; -import { RoleBindingDialog } from "../dialog"; +import { RoleBindingDialog } from "../dialog/view"; import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; import type { DiRender } from "../../../test-utils/renderFor"; import { renderFor } from "../../../test-utils/renderFor"; @@ -16,9 +16,12 @@ import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be- import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable"; import createClusterInjectable from "../../../../cluster/create-cluster.injectable"; +import type { OpenRoleBindingDialog } from "../dialog/open.injectable"; +import openRoleBindingDialogInjectable from "../dialog/open.injectable"; describe("RoleBindingDialog tests", () => { let render: DiRender; + let openRoleBindingDialog: OpenRoleBindingDialog; beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); @@ -27,6 +30,8 @@ describe("RoleBindingDialog tests", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + openRoleBindingDialog = di.inject(openRoleBindingDialogInjectable); + const createCluster = di.inject(createClusterInjectable); di.override(hostedClusterInjectable, () => createCluster({ @@ -55,11 +60,6 @@ describe("RoleBindingDialog tests", () => { ]); }); - afterEach(() => { - RoleBindingDialog.close(); - jest.resetAllMocks(); - }); - it("should render without any errors", () => { const { container } = render(); @@ -67,7 +67,7 @@ describe("RoleBindingDialog tests", () => { }); it("role select should be searchable", async () => { - RoleBindingDialog.open(); + openRoleBindingDialog(); const res = render(); userEvent.click(await res.findByText("Select role", { exact: false })); diff --git a/src/renderer/components/+user-management/+role-bindings/details.tsx b/src/renderer/components/+user-management/+role-bindings/details.tsx index eeadae080c..c83caddeec 100644 --- a/src/renderer/components/+user-management/+role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+role-bindings/details.tsx @@ -14,19 +14,23 @@ import { AddRemoveButtons } from "../../add-remove-buttons"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; import { Table, TableCell, TableHead, TableRow } from "../../table"; -import { RoleBindingDialog } from "./dialog"; -import { roleBindingStore } from "./legacy-store"; import { ObservableHashSet } from "../../../../common/utils/hash-set"; import { hashSubject } from "../hashers"; import type { OpenConfirmDialog } from "../../confirm-dialog/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import openConfirmDialogInjectable from "../../confirm-dialog/open.injectable"; +import type { OpenRoleBindingDialog } from "./dialog/open.injectable"; +import openRoleBindingDialogInjectable from "./dialog/open.injectable"; +import type { RoleBindingStore } from "./store"; +import roleBindingStoreInjectable from "./store.injectable"; export interface RoleBindingDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { openConfirmDialog: OpenConfirmDialog; + openRoleBindingDialog: OpenRoleBindingDialog; + roleBindingStore: RoleBindingStore; } @observer @@ -42,7 +46,7 @@ class NonInjectedRoleBindingDetails extends React.Component { - const { object: roleBinding, openConfirmDialog } = this.props; + const { object: roleBinding, openConfirmDialog, roleBindingStore } = this.props; const { selectedSubjects } = this; openConfirmDialog({ @@ -60,7 +64,7 @@ class NonInjectedRoleBindingDetails extends React.Component RoleBindingDialog.open(roleBinding)} + onAdd={() => openRoleBindingDialog(roleBinding)} onRemove={selectedSubjects.size ? this.removeSelectedSubjects : undefined} addTooltip={`Edit bindings of ${roleRef.name}`} removeTooltip={`Remove selected bindings from ${roleRef.name}`} @@ -130,5 +134,7 @@ export const RoleBindingDetails = withInjectables ({ ...props, openConfirmDialog: di.inject(openConfirmDialogInjectable), + openRoleBindingDialog: di.inject(openRoleBindingDialogInjectable), + roleBindingStore: di.inject(roleBindingStoreInjectable), }), }); diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts new file mode 100644 index 0000000000..1f42df1818 --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import roleBindingDialogStateInjectable from "./state.injectable"; + +const closeRoleBindingDialogInjectable = getInjectable({ + id: "close-role-binding-dialog", + instantiate: (di) => { + const state = di.inject(roleBindingDialogStateInjectable); + + return action(() => state.set({ isOpen: false })); + }, +}); + +export default closeRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts new file mode 100644 index 0000000000..bda4f5363b --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 { action } from "mobx"; +import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; +import roleBindingDialogStateInjectable from "./state.injectable"; + +export type OpenRoleBindingDialog = (roleBinding?: RoleBinding | undefined) => void; + +const openRoleBindingDialogInjectable = getInjectable({ + id: "open-role-binding-dialog", + instantiate: (di): OpenRoleBindingDialog => { + const state = di.inject(roleBindingDialogStateInjectable); + + return action((roleBinding) => state.set({ + isOpen: true, + roleBinding, + })); + }, +}); + +export default openRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts new file mode 100644 index 0000000000..03b1093a3b --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { observable } from "mobx"; +import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; + +export type RoleBindingDialogState = { + isOpen: false; + roleBinding?: undefined; +} | { + isOpen: true; + roleBinding: RoleBinding | undefined; +}; + +const roleBindingDialogStateInjectable = getInjectable({ + id: "role-binding-dialog-state", + instantiate: () => observable.box({ isOpen: false }), +}); + +export default roleBindingDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog.scss b/src/renderer/components/+user-management/+role-bindings/dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+role-bindings/dialog.scss rename to src/renderer/components/+user-management/+role-bindings/dialog/view.scss diff --git a/src/renderer/components/+user-management/+role-bindings/dialog.tsx b/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx similarity index 60% rename from src/renderer/components/+user-management/+role-bindings/dialog.tsx rename to src/renderer/components/+user-management/+role-bindings/dialog/view.tsx index 49ac40d7d1..77f1724708 100644 --- a/src/renderer/components/+user-management/+role-bindings/dialog.tsx +++ b/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx @@ -3,64 +3,67 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./dialog.scss"; +import "./view.scss"; +import type { IObservableValue } from "mobx"; import { computed, observable, makeObservable, action } from "mobx"; import { observer } from "mobx-react"; import React from "react"; - -import { roleStore } from "../+roles/legacy-store"; -import { serviceAccountStore } from "../+service-accounts/legacy-store"; -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import type { ClusterRole, Role, RoleBinding, ServiceAccount } from "../../../../common/k8s-api/endpoints"; -import { roleApi } from "../../../../common/k8s-api/endpoints"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { EditableList } from "../../editable-list"; -import { Icon } from "../../icon"; -import { showDetails } from "../../kube-detail-params"; -import { SubTitle } from "../../layout/sub-title"; -import { Notifications } from "../../notifications"; -import type { SelectOption } from "../../select"; -import { onMultiSelectFor, Select } from "../../select"; -import { Wizard, WizardStep } from "../../wizard"; -import { roleBindingStore } from "./legacy-store"; -import { clusterRoleStore } from "../+cluster-roles/legacy-store"; -import { Input } from "../../input"; -import { ObservableHashSet, nFircate } from "../../../utils"; -import type { Subject } from "../../../../common/k8s-api/endpoints/types/subject"; +import { NamespaceSelect } from "../../../+namespaces/namespace-select"; +import type { ClusterRole, Role, RoleApi, ServiceAccount } from "../../../../../common/k8s-api/endpoints"; +import type { DialogProps } from "../../../dialog"; +import { Dialog } from "../../../dialog"; +import { EditableList } from "../../../editable-list"; +import { Icon } from "../../../icon"; +import { SubTitle } from "../../../layout/sub-title"; +import type { SelectOption } from "../../../select"; +import { onMultiSelectFor, Select } from "../../../select"; +import { Wizard, WizardStep } from "../../../wizard"; +import { Input } from "../../../input"; +import { ObservableHashSet, nFircate } from "../../../../utils"; +import type { Subject } from "../../../../../common/k8s-api/endpoints/types/subject"; +import type { RoleBindingDialogState } from "./state.injectable"; +import type { RoleBindingStore } from "../store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import roleBindingStoreInjectable from "../store.injectable"; +import roleBindingDialogStateInjectable from "./state.injectable"; +import closeRoleBindingDialogInjectable from "./close.injectable"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import type { RoleStore } from "../../+roles/store"; +import type { ClusterRoleStore } from "../../+cluster-roles/store"; +import type { ServiceAccountStore } from "../../+service-accounts/store"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable"; +import roleStoreInjectable from "../../+roles/store.injectable"; +import serviceAccountStoreInjectable from "../../+service-accounts/store.injectable"; +import roleApiInjectable from "../../../../../common/k8s-api/endpoints/role.api.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; export interface RoleBindingDialogProps extends Partial { } -interface DialogState { - isOpen: boolean; - data?: RoleBinding; +interface Dependencies { + state: IObservableValue; + roleBindingStore: RoleBindingStore; + closeRoleBindingDialog: () => void; + showDetails: ShowDetails; + roleStore: RoleStore; + clusterRoleStore: ClusterRoleStore; + serviceAccountStore: ServiceAccountStore; + roleApi: RoleApi; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer -export class RoleBindingDialog extends React.Component { - static state = observable.object({ - isOpen: false, - }); - - constructor(props: RoleBindingDialogProps) { +class NonInjectedRoleBindingDialog extends React.Component { + constructor(props: RoleBindingDialogProps & Dependencies) { super(props); makeObservable(this); } - static open(roleBinding?: RoleBinding) { - RoleBindingDialog.state.isOpen = true; - RoleBindingDialog.state.data = roleBinding; - } - - static close() { - RoleBindingDialog.state.isOpen = false; - RoleBindingDialog.state.data = undefined; - } - - get roleBinding() { - return RoleBindingDialog.state.data; + @computed get roleBinding() { + return this.props.state.get().roleBinding; } @computed get isEditing() { @@ -97,6 +100,10 @@ export class RoleBindingDialog extends React.Component { } @computed get roleRefOptions(): SelectOption[] { + const { + roleStore, + clusterRoleStore, + } = this.props; const roles = roleStore.items .filter(role => role.getNs() === this.bindingNamespace); const clusterRoles = clusterRoleStore.items; @@ -111,7 +118,7 @@ export class RoleBindingDialog extends React.Component { } @computed get serviceAccountOptions(): SelectOption[] { - return serviceAccountStore.items.map(serviceAccount => ({ + return this.props.serviceAccountStore.items.map(serviceAccount => ({ value: serviceAccount, label: `${serviceAccount.getName()} (${serviceAccount.getNs()})`, isSelected: this.selectedAccounts.has(serviceAccount), @@ -119,18 +126,22 @@ export class RoleBindingDialog extends React.Component { } onOpen = action(() => { + const { + roleStore, + clusterRoleStore, + serviceAccountStore, + roleApi, + } = this.props; const binding = this.roleBinding; if (!binding) { return this.reset(); } - const findByRoleRefName = (item: Role | ClusterRole) => item.getName() === binding.roleRef.name; - this.selectedRoleRef = ( binding.roleRef.kind === roleApi.kind - ? roleStore.items.find(findByRoleRefName) - : clusterRoleStore.items.find(findByRoleRefName) + ? roleStore.items.find(item => item.getName() === binding.roleRef.name) + : clusterRoleStore.items.find(item => item.getName() === binding.roleRef.name) ); this.bindingName = binding.getName(); @@ -157,6 +168,11 @@ export class RoleBindingDialog extends React.Component { }); createBindings = async () => { + const { + roleBindingStore, + showDetails, + showCheckedErrorNotification, + } = this.props; const { selectedRoleRef, bindingNamespace, selectedBindings, roleBinding, bindingName } = this; if (!selectedRoleRef || !roleBinding || !bindingNamespace || !bindingName) { @@ -178,9 +194,9 @@ export class RoleBindingDialog extends React.Component { }); showDetails(newRoleBinding.selfLink); - RoleBindingDialog.close(); + this.props.closeRoleBindingDialog(); } catch (err) { - Notifications.checkedError(err, `Unknown error occured while ${this.isEditing ? "editing" : "creating"} role bindings.`); + showCheckedErrorNotification(err, `Unknown error occured while ${this.isEditing ? "editing" : "creating"} role bindings.`); } }; @@ -260,7 +276,7 @@ export class RoleBindingDialog extends React.Component { } render() { - const { ...dialogProps } = this.props; + const { closeRoleBindingDialog, roleBindingStore, state, ...dialogProps } = this.props; const [action, nextLabel] = this.isEditing ? ["Edit", "Update"] : ["Add", "Create"]; const disableNext = !this.selectedRoleRef || !this.selectedBindings.length || !this.bindingNamespace || !this.bindingName; @@ -268,8 +284,8 @@ export class RoleBindingDialog extends React.Component { @@ -279,7 +295,7 @@ export class RoleBindingDialog extends React.Component { {`${action} RoleBinding`} )} - done={RoleBindingDialog.close} + done={closeRoleBindingDialog} > { ); } } + +export const RoleBindingDialog = withInjectables(NonInjectedRoleBindingDialog, { + getProps: (di, props) => ({ + ...props, + roleBindingStore: di.inject(roleBindingStoreInjectable), + state: di.inject(roleBindingDialogStateInjectable), + closeRoleBindingDialog: di.inject(closeRoleBindingDialogInjectable), + showDetails: di.inject(showDetailsInjectable), + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + roleStore: di.inject(roleStoreInjectable), + serviceAccountStore: di.inject(serviceAccountStoreInjectable), + roleApi: di.inject(roleApiInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+role-bindings/index.ts b/src/renderer/components/+user-management/+role-bindings/index.ts index 0dd1376b97..59d5a476cb 100644 --- a/src/renderer/components/+user-management/+role-bindings/index.ts +++ b/src/renderer/components/+user-management/+role-bindings/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./dialog"; +export * from "./dialog/view"; diff --git a/src/renderer/components/+user-management/+role-bindings/legacy-store.ts b/src/renderer/components/+user-management/+role-bindings/legacy-store.ts deleted file mode 100644 index 5989724d6e..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import roleBindingStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(roleBindingStoreInjectable)` instead - */ -export const roleBindingStore = asLegacyGlobalForExtensionApi(roleBindingStoreInjectable); diff --git a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts index a3f33e76bc..416223b071 100644 --- a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts +++ b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import roleBindingApiInjectable from "../../../../common/k8s-api/endpoints/role-binding.api.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; import { RoleBindingStore } from "./store"; @@ -19,6 +20,7 @@ const roleBindingStoreInjectable = getInjectable({ return new RoleBindingStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+role-bindings/view.tsx b/src/renderer/components/+user-management/+role-bindings/view.tsx index 58afc14a5e..3f5e458453 100644 --- a/src/renderer/components/+user-management/+role-bindings/view.tsx +++ b/src/renderer/components/+user-management/+role-bindings/view.tsx @@ -8,7 +8,7 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { RoleBindingDialog } from "./dialog"; +import { RoleBindingDialog } from "./dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; import type { RoleStore } from "../+roles/store"; @@ -23,6 +23,8 @@ import filterByNamespaceInjectable from "../../+namespaces/namespace-select-filt import roleBindingStoreInjectable from "./store.injectable"; import roleStoreInjectable from "../+roles/store.injectable"; import serviceAccountStoreInjectable from "../+service-accounts/store.injectable"; +import type { OpenRoleBindingDialog } from "./dialog/open.injectable"; +import openRoleBindingDialogInjectable from "./dialog/open.injectable"; enum columnId { name = "name", @@ -37,6 +39,7 @@ interface Dependencies { clusterRoleStore: ClusterRoleStore; serviceAccountStore: ServiceAccountStore; filterByNamespace: FilterByNamespace; + openRoleBindingDialog: OpenRoleBindingDialog; } @observer @@ -48,6 +51,7 @@ class NonInjectedRoleBindings extends React.Component { roleStore, serviceAccountStore, filterByNamespace, + openRoleBindingDialog, } = this.props; return ( @@ -90,7 +94,7 @@ class NonInjectedRoleBindings extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => RoleBindingDialog.open(), + onAdd: () => openRoleBindingDialog(), addTooltip: "Create new RoleBinding", }} /> @@ -108,5 +112,6 @@ export const RoleBindings = withInjectables(NonInjectedRoleBinding roleBindingStore: di.inject(roleBindingStoreInjectable), roleStore: di.inject(roleStoreInjectable), serviceAccountStore: di.inject(serviceAccountStoreInjectable), + openRoleBindingDialog: di.inject(openRoleBindingDialogInjectable), }), }); diff --git a/src/renderer/components/+user-management/+roles/add-dialog.tsx b/src/renderer/components/+user-management/+roles/add-dialog.tsx deleted file mode 100644 index eae10c3dd1..0000000000 --- a/src/renderer/components/+user-management/+roles/add-dialog.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./add-dialog.scss"; - -import React from "react"; -import { observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; - -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Input } from "../../input"; -import { showDetails } from "../../kube-detail-params"; -import { SubTitle } from "../../layout/sub-title"; -import { Notifications } from "../../notifications"; -import { Wizard, WizardStep } from "../../wizard"; -import { roleStore } from "./legacy-store"; - -export interface AddRoleDialogProps extends Partial { -} - -@observer -export class AddRoleDialog extends React.Component { - static isOpen = observable.box(false); - - @observable roleName = ""; - @observable namespace = ""; - - constructor(props: AddRoleDialogProps) { - super(props); - makeObservable(this); - } - - static open() { - AddRoleDialog.isOpen.set(true); - } - - static close() { - AddRoleDialog.isOpen.set(false); - } - - reset = () => { - this.roleName = ""; - this.namespace = ""; - }; - - createRole = async () => { - try { - const role = await roleStore.create({ name: this.roleName, namespace: this.namespace }); - - showDetails(role.selfLink); - this.reset(); - AddRoleDialog.close(); - } catch (err) { - Notifications.checkedError(err, "Unknown error occured while creating role"); - } - }; - - render() { - const { ...dialogProps } = this.props; - const header =
Create Role
; - - return ( - - - - - this.roleName = v} - /> - - this.namespace = option?.value ?? "default"} - /> - - - - ); - } -} diff --git a/src/renderer/components/+user-management/+roles/add-dialog/close.injectable.ts b/src/renderer/components/+user-management/+roles/add-dialog/close.injectable.ts new file mode 100644 index 0000000000..197d6f9b2a --- /dev/null +++ b/src/renderer/components/+user-management/+roles/add-dialog/close.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { action } from "mobx"; +import addRoleDialogStateInjectable from "./state.injectable"; + +const closeAddRoleDialogInjectable = getInjectable({ + id: "close-add-role-dialog", + instantiate: (di) => { + const state = di.inject(addRoleDialogStateInjectable); + + return action(() => { + state.isOpen.set(false); + state.namespace.set(""); + state.roleName.set(""); + }); + }, +}); + +export default closeAddRoleDialogInjectable; diff --git a/src/renderer/components/+user-management/+roles/add-dialog/open.injectable.ts b/src/renderer/components/+user-management/+roles/add-dialog/open.injectable.ts new file mode 100644 index 0000000000..57c53ce70e --- /dev/null +++ b/src/renderer/components/+user-management/+roles/add-dialog/open.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { action } from "mobx"; +import addRoleDialogStateInjectable from "./state.injectable"; + +const openAddRoleDialogInjectable = getInjectable({ + id: "open-add-role-dialog", + instantiate: (di) => { + const state = di.inject(addRoleDialogStateInjectable); + + return action(() => { + state.isOpen.set(true); + state.namespace.set(""); + state.roleName.set(""); + }); + }, +}); + +export default openAddRoleDialogInjectable; diff --git a/src/renderer/components/+user-management/+roles/add-dialog/state.injectable.ts b/src/renderer/components/+user-management/+roles/add-dialog/state.injectable.ts new file mode 100644 index 0000000000..5b76b23cc5 --- /dev/null +++ b/src/renderer/components/+user-management/+roles/add-dialog/state.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 type { IObservableValue } from "mobx"; +import { observable } from "mobx"; + +export interface AddRoleDialogState { + readonly isOpen: IObservableValue; + readonly roleName: IObservableValue; + readonly namespace: IObservableValue; +} + +const addRoleDialogStateInjectable = getInjectable({ + id: "add-role-dialog-state", + instantiate: (): AddRoleDialogState => ({ + isOpen: observable.box(false), + roleName: observable.box(""), + namespace: observable.box(""), + }), +}); + +export default addRoleDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+roles/add-dialog.scss b/src/renderer/components/+user-management/+roles/add-dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+roles/add-dialog.scss rename to src/renderer/components/+user-management/+roles/add-dialog/view.scss diff --git a/src/renderer/components/+user-management/+roles/add-dialog/view.tsx b/src/renderer/components/+user-management/+roles/add-dialog/view.tsx new file mode 100644 index 0000000000..d892d08aa7 --- /dev/null +++ b/src/renderer/components/+user-management/+roles/add-dialog/view.tsx @@ -0,0 +1,112 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./view.scss"; + +import React from "react"; +import { observer } from "mobx-react"; + +import { NamespaceSelect } from "../../../+namespaces/namespace-select"; +import type { DialogProps } from "../../../dialog"; +import { Dialog } from "../../../dialog"; +import { Input } from "../../../input"; +import { SubTitle } from "../../../layout/sub-title"; +import { Wizard, WizardStep } from "../../../wizard"; +import type { AddRoleDialogState } from "./state.injectable"; +import type { RoleStore } from "../store"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeAddRoleDialogInjectable from "./close.injectable"; +import roleStoreInjectable from "../store.injectable"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import addRoleDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; + +export interface AddRoleDialogProps extends Partial { +} + +interface Dependencies { + closeAddRoleDialog: () => void; + showDetails: ShowDetails; + state: AddRoleDialogState; + roleStore: RoleStore; + showCheckedErrorNotification: ShowCheckedErrorNotification; +} + +@observer +class NonInjectedAddRoleDialog extends React.Component { + createRole = async () => { + const { + closeAddRoleDialog, + roleStore, + state, + showDetails, + showCheckedErrorNotification, + } = this.props; + + try { + const role = await roleStore.create({ + name: state.roleName.get(), + namespace: state.namespace.get(), + }); + + showDetails(role.selfLink); + closeAddRoleDialog(); + } catch (err) { + showCheckedErrorNotification(err, "Unknown error occured while creating role"); + } + }; + + render() { + const { closeAddRoleDialog, roleStore, state, ...dialogProps } = this.props; + const header =
Create Role
; + + return ( + + + + + state.roleName.set(v)} + /> + + state.namespace.set(option?.value ?? "default")} + /> + + + + ); + } +} + +export const AddRoleDialog = withInjectables(NonInjectedAddRoleDialog, { + getProps: (di, props) => ({ + ...props, + closeAddRoleDialog: di.inject(closeAddRoleDialogInjectable), + roleStore: di.inject(roleStoreInjectable), + showDetails: di.inject(showDetailsInjectable), + state: di.inject(addRoleDialogStateInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+roles/index.ts b/src/renderer/components/+user-management/+roles/index.ts index 2b770fa1cd..f55fa5914b 100644 --- a/src/renderer/components/+user-management/+roles/index.ts +++ b/src/renderer/components/+user-management/+roles/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./add-dialog"; +export * from "./add-dialog/view"; diff --git a/src/renderer/components/+user-management/+roles/legacy-store.ts b/src/renderer/components/+user-management/+roles/legacy-store.ts deleted file mode 100644 index 84ecdaa588..0000000000 --- a/src/renderer/components/+user-management/+roles/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import roleStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(roleStoreInjectable)` instead - */ -export const roleStore = asLegacyGlobalForExtensionApi(roleStoreInjectable); diff --git a/src/renderer/components/+user-management/+roles/store.injectable.ts b/src/renderer/components/+user-management/+roles/store.injectable.ts index 8b86ddb0ab..f0df652438 100644 --- a/src/renderer/components/+user-management/+roles/store.injectable.ts +++ b/src/renderer/components/+user-management/+roles/store.injectable.ts @@ -9,6 +9,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import { RoleStore } from "./store"; import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const roleStoreInjectable = getInjectable({ id: "role-store", @@ -19,6 +20,7 @@ const roleStoreInjectable = getInjectable({ return new RoleStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+roles/view.tsx b/src/renderer/components/+user-management/+roles/view.tsx index 35b9bdc0d8..bf308b38af 100644 --- a/src/renderer/components/+user-management/+roles/view.tsx +++ b/src/renderer/components/+user-management/+roles/view.tsx @@ -9,7 +9,7 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { AddRoleDialog } from "./add-dialog"; +import { AddRoleDialog } from "./add-dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; import type { RoleStore } from "./store"; @@ -18,6 +18,7 @@ import type { FilterByNamespace } from "../../+namespaces/namespace-select-filte import { withInjectables } from "@ogre-tools/injectable-react"; import filterByNamespaceInjectable from "../../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import roleStoreInjectable from "./store.injectable"; +import openAddRoleDialogInjectable from "./add-dialog/open.injectable"; enum columnId { name = "name", @@ -28,6 +29,7 @@ enum columnId { interface Dependencies { roleStore: RoleStore; filterByNamespace: FilterByNamespace; + openAddRoleDialog: () => void; } @observer @@ -36,6 +38,7 @@ class NonInjectedRoles extends React.Component { const { filterByNamespace, roleStore, + openAddRoleDialog, } = this.props; return ( @@ -73,7 +76,7 @@ class NonInjectedRoles extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => AddRoleDialog.open(), + onAdd: () => openAddRoleDialog(), addTooltip: "Create new Role", }} /> @@ -88,5 +91,6 @@ export const Roles = withInjectables(NonInjectedRoles, { ...props, filterByNamespace: di.inject(filterByNamespaceInjectable), roleStore: di.inject(roleStoreInjectable), + openAddRoleDialog: di.inject(openAddRoleDialogInjectable), }), }); diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx b/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx deleted file mode 100644 index 1df0febeb7..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./create-dialog.scss"; - -import React from "react"; -import { makeObservable, observable } from "mobx"; -import { observer } from "mobx-react"; - -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Input } from "../../input"; -import { systemName } from "../../input/input_validators"; -import { showDetails } from "../../kube-detail-params"; -import { SubTitle } from "../../layout/sub-title"; -import { Notifications } from "../../notifications"; -import { Wizard, WizardStep } from "../../wizard"; -import { serviceAccountStore } from "./legacy-store"; - -export interface CreateServiceAccountDialogProps extends Partial { -} - -@observer -export class CreateServiceAccountDialog extends React.Component { - static isOpen = observable.box(false); - - @observable name = ""; - @observable namespace = "default"; - - constructor(props: CreateServiceAccountDialogProps) { - super(props); - makeObservable(this); - } - - static open() { - CreateServiceAccountDialog.isOpen.set(true); - } - - static close() { - CreateServiceAccountDialog.isOpen.set(false); - } - - createAccount = async () => { - const { name, namespace } = this; - - try { - const serviceAccount = await serviceAccountStore.create({ namespace, name }); - - this.name = ""; - showDetails(serviceAccount.selfLink); - CreateServiceAccountDialog.close(); - } catch (err) { - Notifications.checkedError(err, "Unknown error occured while creating service account"); - } - }; - - render() { - const { ...dialogProps } = this.props; - const { name, namespace } = this; - const header =
Create Service Account
; - - return ( - - - - - this.name = v.toLowerCase()} - /> - - this.namespace = option?.value ?? "default"} - /> - - - - ); - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts new file mode 100644 index 0000000000..7c722c1264 --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { action } from "mobx"; +import createServiceAccountDialogStateInjectable from "./state.injectable"; + +const closeCreateServiceAccountDialogInjectable = getInjectable({ + id: "close-create-service-account-dialog", + instantiate: (di) => { + const state = di.inject(createServiceAccountDialogStateInjectable); + + return action(() => { + state.isOpen.set(false); + state.name.set(""); + state.namespace.set("default"); + }); + }, +}); + +export default closeCreateServiceAccountDialogInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts new file mode 100644 index 0000000000..d1410817c9 --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 { action } from "mobx"; +import createServiceAccountDialogStateInjectable from "./state.injectable"; + +export type OpenCreateServiceAccountDialog = () => void; + +const openCreateServiceAccountDialogInjectable = getInjectable({ + id: "open-create-service-account-dialog", + instantiate: (di) => { + const state = di.inject(createServiceAccountDialogStateInjectable); + + return action(() => { + state.isOpen.set(true); + state.name.set(""); + state.namespace.set("default"); + }); + }, +}); + +export default openCreateServiceAccountDialogInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts new file mode 100644 index 0000000000..8ca5455fbd --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 type { IObservableValue } from "mobx"; +import { observable } from "mobx"; + +export interface CreateServiceAccountDialogState { + readonly isOpen: IObservableValue; + readonly name: IObservableValue; + readonly namespace: IObservableValue; +} + +const createServiceAccountDialogStateInjectable = getInjectable({ + id: "create-service-account-dialog", + instantiate: (): CreateServiceAccountDialogState => ({ + isOpen: observable.box(false), + name: observable.box(""), + namespace: observable.box("default"), + }), +}); + +export default createServiceAccountDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog.scss b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+service-accounts/create-dialog.scss rename to src/renderer/components/+user-management/+service-accounts/create-dialog/view.scss diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx new file mode 100644 index 0000000000..6b112b6ea9 --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx @@ -0,0 +1,112 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./view.scss"; + +import React from "react"; +import { observer } from "mobx-react"; + +import { NamespaceSelect } from "../../../+namespaces/namespace-select"; +import type { DialogProps } from "../../../dialog"; +import { Dialog } from "../../../dialog"; +import { Input } from "../../../input"; +import { systemName } from "../../../input/input_validators"; +import { SubTitle } from "../../../layout/sub-title"; +import { Wizard, WizardStep } from "../../../wizard"; +import type { CreateServiceAccountDialogState } from "./state.injectable"; +import type { ServiceAccountStore } from "../store"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeCreateServiceAccountDialogInjectable from "./close.injectable"; +import serviceAccountStoreInjectable from "../store.injectable"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import createServiceAccountDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; + +export interface CreateServiceAccountDialogProps extends Partial { +} + +interface Dependencies { + state: CreateServiceAccountDialogState; + serviceAccountStore: ServiceAccountStore; + closeCreateServiceAccountDialog: () => void; + showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; +} + +@observer +class NonInjectedCreateServiceAccountDialog extends React.Component { + createAccount = async () => { + const { + closeCreateServiceAccountDialog, + serviceAccountStore, + state, + showDetails, + showCheckedErrorNotification, + } = this.props; + + try { + const serviceAccount = await serviceAccountStore.create({ + namespace: state.namespace.get(), + name: state.name.get(), + }); + + showDetails(serviceAccount.selfLink); + closeCreateServiceAccountDialog(); + } catch (err) { + showCheckedErrorNotification(err, "Unknown error occured while creating service account"); + } + }; + + render() { + const { closeCreateServiceAccountDialog, serviceAccountStore, state, ...dialogProps } = this.props; + + return ( + + Create Service Account} + done={closeCreateServiceAccountDialog} + > + + + state.name.set(v.toLowerCase())} + /> + + state.namespace.set(option?.value ?? "default")} + /> + + + + ); + } +} + +export const CreateServiceAccountDialog = withInjectables(NonInjectedCreateServiceAccountDialog, { + getProps: (di, props) => ({ + ...props, + closeCreateServiceAccountDialog: di.inject(closeCreateServiceAccountDialogInjectable), + serviceAccountStore: di.inject(serviceAccountStoreInjectable), + showDetails: di.inject(showDetailsInjectable), + state: di.inject(createServiceAccountDialogStateInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+service-accounts/index.ts b/src/renderer/components/+user-management/+service-accounts/index.ts index 151712ecb9..e011cad5cd 100644 --- a/src/renderer/components/+user-management/+service-accounts/index.ts +++ b/src/renderer/components/+user-management/+service-accounts/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./create-dialog"; +export * from "./create-dialog/view"; diff --git a/src/renderer/components/+user-management/+service-accounts/legacy-store.ts b/src/renderer/components/+user-management/+service-accounts/legacy-store.ts deleted file mode 100644 index 255407160f..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import serviceAccountStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(serviceAccountStoreInjectable)` instead - */ -export const serviceAccountStore = asLegacyGlobalForExtensionApi(serviceAccountStoreInjectable); diff --git a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts index 98570c9017..fd96db2d65 100644 --- a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts +++ b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts @@ -9,6 +9,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import { ServiceAccountStore } from "./store"; import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const serviceAccountStoreInjectable = getInjectable({ id: "service-account-store", @@ -19,6 +20,7 @@ const serviceAccountStoreInjectable = getInjectable({ return new ServiceAccountStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+service-accounts/view.tsx b/src/renderer/components/+user-management/+service-accounts/view.tsx index 68161112f8..1043250d73 100644 --- a/src/renderer/components/+user-management/+service-accounts/view.tsx +++ b/src/renderer/components/+user-management/+service-accounts/view.tsx @@ -9,7 +9,7 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { CreateServiceAccountDialog } from "./create-dialog"; +import { CreateServiceAccountDialog } from "./create-dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; import { prevDefault } from "../../../utils"; @@ -18,6 +18,8 @@ import type { FilterByNamespace } from "../../+namespaces/namespace-select-filte import { withInjectables } from "@ogre-tools/injectable-react"; import filterByNamespaceInjectable from "../../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import serviceAccountStoreInjectable from "./store.injectable"; +import type { OpenCreateServiceAccountDialog } from "./create-dialog/open.injectable"; +import openCreateServiceAccountDialogInjectable from "./create-dialog/open.injectable"; enum columnId { name = "name", @@ -28,6 +30,7 @@ enum columnId { interface Dependencies { serviceAccountStore: ServiceAccountStore; filterByNamespace: FilterByNamespace; + openCreateServiceAccountDialog: OpenCreateServiceAccountDialog; } @observer @@ -36,6 +39,7 @@ class NonInjectedServiceAccounts extends React.Component { const { filterByNamespace, serviceAccountStore, + openCreateServiceAccountDialog, } = this.props; return ( @@ -73,7 +77,7 @@ class NonInjectedServiceAccounts extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => CreateServiceAccountDialog.open(), + onAdd: () => openCreateServiceAccountDialog(), addTooltip: "Create new Service Account", }} /> @@ -88,5 +92,6 @@ export const ServiceAccounts = withInjectables(NonInjectedServiceA ...props, filterByNamespace: di.inject(filterByNamespaceInjectable), serviceAccountStore: di.inject(serviceAccountStoreInjectable), + openCreateServiceAccountDialog: di.inject(openCreateServiceAccountDialogInjectable), }), }); diff --git a/src/renderer/components/+welcome/__test__/welcome.test.tsx b/src/renderer/components/+welcome/__test__/welcome.test.tsx index 564b8b73f5..2b12f5658f 100644 --- a/src/renderer/components/+welcome/__test__/welcome.test.tsx +++ b/src/renderer/components/+welcome/__test__/welcome.test.tsx @@ -17,15 +17,6 @@ import { LensRendererExtension } from "../../../../extensions/lens-renderer-exte import type { WelcomeBannerRegistration } from "../welcome-banner-items/welcome-banner-registration"; import currentlyInClusterFrameInjectable from "../../../routes/currently-in-cluster-frame.injectable"; -jest.mock("electron", () => ({ - ipcRenderer: { - on: jest.fn(), - }, - app: { - getPath: () => "tmp", - }, -})); - describe("", () => { let render: DiRender; let di: DiContainer; diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx b/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx index 18fa009467..d361180e49 100644 --- a/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx +++ b/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx @@ -4,29 +4,37 @@ */ import React from "react"; import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { CronJob } from "../../../common/k8s-api/endpoints"; -import { cronJobApi } from "../../../common/k8s-api/endpoints"; +import type { CronJob, CronJobApi } from "../../../common/k8s-api/endpoints"; import { MenuItem } from "../menu"; -import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; import { Icon } from "../icon"; -import { Notifications } from "../notifications"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; +import type { OpenCronJobTriggerDialog } from "./trigger-dialog/open.injectable"; +import openCronJobTriggerDialogInjectable from "./trigger-dialog/open.injectable"; +import cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable"; +import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; export interface CronJobMenuProps extends KubeObjectMenuProps {} interface Dependencies { openConfirmDialog: OpenConfirmDialog; + openCronJobTriggerDialog: OpenCronJobTriggerDialog; + cronJobApi: CronJobApi; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedCronJobMenu = ({ object, toolbar, openConfirmDialog, + openCronJobTriggerDialog, + cronJobApi, + showCheckedErrorNotification, }: Dependencies & CronJobMenuProps) => ( <> - CronJobTriggerDialog.open(object)}> + openCronJobTriggerDialog(object)}> (NonIn getProps: (di, props) => ({ ...props, openConfirmDialog: di.inject(openConfirmDialogInjectable), + openCronJobTriggerDialog: di.inject(openCronJobTriggerDialogInjectable), + cronJobApi: di.inject(cronJobApiInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts index 0ab5c66241..024515e1b8 100644 --- a/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts +++ b/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; +import { CronJobTriggerDialog } from "./trigger-dialog/view"; import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; const cronJobTriggerDialogClusterFrameChildComponentInjectable = getInjectable({ @@ -17,8 +17,6 @@ const cronJobTriggerDialogClusterFrameChildComponentInjectable = getInjectable({ }), injectionToken: clusterFrameChildComponentInjectionToken, - - causesSideEffects: true, }); export default cronJobTriggerDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx index 4701869ab6..c8f925edc1 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx @@ -14,15 +14,17 @@ import type { JobStore } from "../+workloads-jobs/store"; import { Link } from "react-router-dom"; import type { CronJobStore } from "./store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { getDetailsUrl } from "../kube-detail-params"; import type { Job } from "../../../common/k8s-api/endpoints"; import { CronJob } from "../../../common/k8s-api/endpoints"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import cronJobStoreInjectable from "./store.injectable"; import jobStoreInjectable from "../+workloads-jobs/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; +import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; export interface CronJobDetailsProps extends KubeObjectDetailsProps { } @@ -31,6 +33,8 @@ interface Dependencies { subscribeStores: SubscribeStores; jobStore: JobStore; cronJobStore: CronJobStore; + logger: Logger; + getDetailsUrl: GetDetailsUrl; } @observer @@ -44,14 +48,14 @@ class NonInjectedCronJobDetails extends React.Component subscribeStores: di.inject(subscribeStoresInjectable), cronJobStore: di.inject(cronJobStoreInjectable), jobStore: di.inject(jobStoreInjectable), + logger: di.inject(loggerInjectable), + getDetailsUrl: di.inject(getDetailsUrlInjectable), }), }); diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx deleted file mode 100644 index ec0fe9e9c1..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./cronjob-trigger-dialog.scss"; - -import React, { Component } from "react"; -import { observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../dialog"; -import { Dialog } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import type { CronJob } from "../../../common/k8s-api/endpoints"; -import { jobApi } from "../../../common/k8s-api/endpoints"; -import { Notifications } from "../notifications"; -import { cssNames } from "../../utils"; -import { Input } from "../input"; -import { systemName, maxLength } from "../input/input_validators"; - -export interface CronJobTriggerDialogProps extends Partial { -} - -const dialogState = observable.box(); - -@observer -export class CronJobTriggerDialog extends Component { - @observable jobName = ""; - - constructor(props: CronJobTriggerDialogProps) { - super(props); - makeObservable(this); - } - - static open(cronjob: CronJob) { - dialogState.set(cronjob); - } - - static close() { - dialogState.set(undefined); - } - - onOpen = () => { - const cronJob = dialogState.get(); - - this.jobName = cronJob ? `${cronJob.getName()}-manual-${Math.random().toString(36).slice(2, 7)}` : ""; - this.jobName = this.jobName.slice(0, 63); - }; - - async trigger(cronJob: CronJob): Promise { - if (!cronJob.spec.jobTemplate) { - return void Notifications.error(`CronJob ${cronJob.getName()} has no jobTemplate`); - } - - try { - await jobApi.create({ - name: this.jobName, - namespace: cronJob.getNs(), - }, { - spec: cronJob.spec.jobTemplate.spec, - metadata: { - annotations: { "cronjob.kubernetes.io/instantiate": "manual" }, - ownerReferences: [{ - apiVersion: cronJob.apiVersion, - blockOwnerDeletion: true, - controller: true, - kind: cronJob.kind, - name: cronJob.metadata.name, - uid: cronJob.metadata.uid, - }], - }, - }); - - CronJobTriggerDialog.close(); - } catch (err) { - Notifications.checkedError(err, "Unknown error occurred while creating job"); - } - } - - renderContents(cronJob: CronJob) { - return ( - - Trigger CronJob - {cronJob.getName()} - - )} - done={CronJobTriggerDialog.close} - > - this.trigger(cronJob)} - nextLabel="Trigger" - > -
- Job name: -
-
- this.jobName = v.toLowerCase()} - className="box grow" - /> -
-
-
- ); - } - - render() { - const { className, ...dialogProps } = this.props; - const cronJob = dialogState.get(); - - return ( - - {cronJob && this.renderContents(cronJob)} - - ); - } -} diff --git a/src/renderer/components/+workloads-cronjobs/store.injectable.ts b/src/renderer/components/+workloads-cronjobs/store.injectable.ts index 6b0fab2c9c..2569e77440 100644 --- a/src/renderer/components/+workloads-cronjobs/store.injectable.ts +++ b/src/renderer/components/+workloads-cronjobs/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getJobsByOwnerInjectable from "../+workloads-jobs/get-jobs-by-owner.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import { CronJobStore } from "./store"; @@ -21,6 +22,7 @@ const cronJobStoreInjectable = getInjectable({ return new CronJobStore({ getJobsByOwner: di.inject(getJobsByOwnerInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts new file mode 100644 index 0000000000..d089db47a9 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { action } from "mobx"; +import cronJobTriggerDialogStateInjectable from "./state.injectable"; + +const closeCronJobTriggerDialogInjectable = getInjectable({ + id: "close-cron-job-trigger-dialog", + instantiate: (di) => { + const state = di.inject(cronJobTriggerDialogStateInjectable); + + return action(() => state.set(undefined)); + }, +}); + +export default closeCronJobTriggerDialogInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts new file mode 100644 index 0000000000..79ebc8a618 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts @@ -0,0 +1,21 @@ +/** + * 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 { action } from "mobx"; +import type { CronJob } from "../../../../common/k8s-api/endpoints"; +import cronJobTriggerDialogStateInjectable from "./state.injectable"; + +export type OpenCronJobTriggerDialog = (cronJob: CronJob) => void; + +const openCronJobTriggerDialogInjectable = getInjectable({ + id: "open-cron-job-trigger-dialog", + instantiate: (di): OpenCronJobTriggerDialog => { + const state = di.inject(cronJobTriggerDialogStateInjectable); + + return action((cronJob) => state.set(cronJob)); + }, +}); + +export default openCronJobTriggerDialogInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts new file mode 100644 index 0000000000..944eae40c4 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts @@ -0,0 +1,14 @@ +/** + * 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 { observable } from "mobx"; +import type { CronJob } from "../../../../common/k8s-api/endpoints"; + +const cronJobTriggerDialogStateInjectable = getInjectable({ + id: "cron-job-trigger-dialog-state", + instantiate: () => observable.box(), +}); + +export default cronJobTriggerDialogStateInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.scss b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.scss similarity index 100% rename from src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.scss rename to src/renderer/components/+workloads-cronjobs/trigger-dialog/view.scss diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx new file mode 100644 index 0000000000..48ff80dc28 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx @@ -0,0 +1,151 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./view.scss"; + +import React, { Component } from "react"; +import type { IObservableValue } from "mobx"; +import { observable, makeObservable } from "mobx"; +import { observer } from "mobx-react"; +import type { DialogProps } from "../../dialog"; +import { Dialog } from "../../dialog"; +import { Wizard, WizardStep } from "../../wizard"; +import type { CronJob, JobApi } from "../../../../common/k8s-api/endpoints"; +import type { ShowNotification } from "../../notifications"; +import { cssNames } from "../../../utils"; +import { Input } from "../../input"; +import { systemName, maxLength } from "../../input/input_validators"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import closeCronJobTriggerDialogInjectable from "./close.injectable"; +import jobApiInjectable from "../../../../common/k8s-api/endpoints/job.api.injectable"; +import cronJobTriggerDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; +import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable"; + +export interface CronJobTriggerDialogProps extends Partial { +} + +interface Dependencies { + state: IObservableValue; + jobApi: JobApi; + closeCronJobTriggerDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; + showErrorNotification: ShowNotification; +} + +@observer +class NonInjectedCronJobTriggerDialog extends Component { + @observable jobName = ""; + + constructor(props: CronJobTriggerDialogProps & Dependencies) { + super(props); + makeObservable(this); + } + + onOpen = () => { + const cronJob = this.props.state.get(); + + this.jobName = cronJob ? `${cronJob.getName()}-manual-${Math.random().toString(36).slice(2, 7)}` : ""; + this.jobName = this.jobName.slice(0, 63); + }; + + async trigger(cronJob: CronJob): Promise { + if (!cronJob.spec.jobTemplate) { + this.props.showErrorNotification(`CronJob ${cronJob.getName()} has no jobTemplate`); + + return; + } + + try { + await this.props.jobApi.create({ + name: this.jobName, + namespace: cronJob.getNs(), + }, { + spec: cronJob.spec.jobTemplate.spec, + metadata: { + annotations: { "cronjob.kubernetes.io/instantiate": "manual" }, + ownerReferences: [{ + apiVersion: cronJob.apiVersion, + blockOwnerDeletion: true, + controller: true, + kind: cronJob.kind, + name: cronJob.metadata.name, + uid: cronJob.metadata.uid, + }], + }, + }); + + this.props.closeCronJobTriggerDialog(); + } catch (err) { + this.props.showCheckedErrorNotification(err, "Unknown error occurred while creating job"); + } + } + + renderContents(cronJob: CronJob) { + return ( + + Trigger CronJob + {cronJob.getName()} + + )} + done={this.props.closeCronJobTriggerDialog} + > + this.trigger(cronJob)} + nextLabel="Trigger" + > +
+ Job name: +
+
+ this.jobName = v.toLowerCase()} + className="box grow" + /> +
+
+
+ ); + } + + render() { + const { className, state, closeCronJobTriggerDialog, jobApi, ...dialogProps } = this.props; + const cronJob = state.get(); + + return ( + + {cronJob && this.renderContents(cronJob)} + + ); + } +} + +export const CronJobTriggerDialog = withInjectables(NonInjectedCronJobTriggerDialog, { + getProps: (di, props) => ({ + ...props, + closeCronJobTriggerDialog: di.inject(closeCronJobTriggerDialogInjectable), + jobApi: di.inject(jobApiInjectable), + state: di.inject(cronJobTriggerDialogStateInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx index 24a9c042b3..4c98835d65 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx @@ -7,17 +7,19 @@ import type { KubeObjectMenuProps } from "../kube-object-menu"; import type { DaemonSet, DaemonSetApi } from "../../../common/k8s-api/endpoints"; import { MenuItem } from "../menu"; import { Icon } from "../icon"; -import { Notifications } from "../notifications"; import { withInjectables } from "@ogre-tools/injectable-react"; import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; +import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; export interface DaemonSetMenuProps extends KubeObjectMenuProps {} interface Dependencies { daemonsetApi: DaemonSetApi; openConfirmDialog: OpenConfirmDialog; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedDaemonSetMenu = ({ @@ -25,6 +27,7 @@ const NonInjectedDaemonSetMenu = ({ object, toolbar, openConfirmDialog, + showCheckedErrorNotification, }: Dependencies & DaemonSetMenuProps) => ( <> (N ...props, daemonsetApi: di.inject(daemonSetApiInjectable), openConfirmDialog: di.inject(openConfirmDialogInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-daemonsets/store.injectable.ts b/src/renderer/components/+workloads-daemonsets/store.injectable.ts index 5711ad625f..59252688bf 100644 --- a/src/renderer/components/+workloads-daemonsets/store.injectable.ts +++ b/src/renderer/components/+workloads-daemonsets/store.injectable.ts @@ -10,6 +10,7 @@ import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-create import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import { DaemonSetStore } from "./store"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const daemonSetStoreInjectable = getInjectable({ id: "daemon-set-store", @@ -21,6 +22,7 @@ const daemonSetStoreInjectable = getInjectable({ return new DaemonSetStore({ getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-deployments/deployment-menu.tsx b/src/renderer/components/+workloads-deployments/deployment-menu.tsx index ce8b031ce9..a32cc0eb11 100644 --- a/src/renderer/components/+workloads-deployments/deployment-menu.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-menu.tsx @@ -7,13 +7,14 @@ import type { KubeObjectMenuProps } from "../kube-object-menu"; import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints"; import { MenuItem } from "../menu"; import { Icon } from "../icon"; -import { Notifications } from "../notifications"; import type { OpenDeploymentScaleDialog } from "./scale/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable"; import openDeploymentScaleDialogInjectable from "./scale/open.injectable"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; +import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; export interface DeploymentMenuProps extends KubeObjectMenuProps {} @@ -21,6 +22,7 @@ interface Dependencies { openDeploymentScaleDialog: OpenDeploymentScaleDialog; deploymentApi: DeploymentApi; openConfirmDialog: OpenConfirmDialog; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedDeploymentMenu = ({ @@ -29,6 +31,7 @@ const NonInjectedDeploymentMenu = ({ openDeploymentScaleDialog, toolbar, openConfirmDialog, + showCheckedErrorNotification, }: Dependencies & DeploymentMenuProps) => ( <> openDeploymentScaleDialog(object)}> @@ -49,7 +52,7 @@ const NonInjectedDeploymentMenu = ({ name: object.getName(), }); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while restarting deployment"); + showCheckedErrorNotification(err, "Unknown error occured while restarting deployment"); } }, labelOk: "Restart", @@ -78,5 +81,6 @@ export const DeploymentMenu = withInjectables deploymentApi: di.inject(deploymentApiInjectable), openDeploymentScaleDialog: di.inject(openDeploymentScaleDialogInjectable), openConfirmDialog: di.inject(openConfirmDialogInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts index 712b5d4321..31a444dba0 100644 --- a/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts +++ b/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts @@ -17,8 +17,6 @@ const deploymentScaleDialogClusterFrameChildComponentInjectable = getInjectable( }), injectionToken: clusterFrameChildComponentInjectionToken, - - causesSideEffects: true, }); export default deploymentScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/scale/dialog.tsx b/src/renderer/components/+workloads-deployments/scale/dialog.tsx index 00c953cf4c..d6c8b0fcaa 100644 --- a/src/renderer/components/+workloads-deployments/scale/dialog.tsx +++ b/src/renderer/components/+workloads-deployments/scale/dialog.tsx @@ -15,11 +15,12 @@ import { Wizard, WizardStep } from "../../wizard"; import type { Deployment, DeploymentApi } from "../../../../common/k8s-api/endpoints"; import { Icon } from "../../icon"; import { Slider } from "../../slider"; -import { Notifications } from "../../notifications"; import { cssNames } from "../../../utils"; import { withInjectables } from "@ogre-tools/injectable-react"; import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable"; import deploymentScaleDialogStateInjectable from "./dialog-state.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface DeploymentScaleDialogProps extends Partial { } @@ -27,6 +28,7 @@ export interface DeploymentScaleDialogProps extends Partial { interface Dependencies { deploymentApi: DeploymentApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedDeploymentScaleDialog extends Component ({ - app: { - getPath: () => "/foo", - }, -})); - const tolerations: Toleration[] =[ { key: "CriticalAddonsOnly", diff --git a/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx b/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx index 127f78cdcc..b0f0662da3 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx @@ -2,13 +2,15 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; import { Link } from "react-router-dom"; import type { PodVolumeVariants, Pod, SecretReference } from "../../../../../common/k8s-api/endpoints"; import type { KubeApiQueryParams, ResourceDescriptor } from "../../../../../common/k8s-api/kube-api"; import type { LocalObjectReference } from "../../../../../common/k8s-api/kube-object"; import { DrawerItem } from "../../../drawer"; -import { getDetailsUrl } from "../../../kube-detail-params"; +import type { GetDetailsUrl } from "../../../kube-detail-params/get-details-url.injectable"; +import getDetailsUrlInjectable from "../../../kube-detail-params/get-details-url.injectable"; export interface PodVolumeVariantSpecificProps { variant: PodVolumeVariants[Kind]; @@ -29,16 +31,35 @@ export interface LocalRefProps { api: LocalRefPropsApi; } -export const LocalRef = ({ pod, title, kubeRef: ref, api }: LocalRefProps) => { - if (!ref) { +interface Dependencies { + getDetailsUrl: GetDetailsUrl; +} + +const NonInjectedLocalRef = (props: LocalRefProps & Dependencies) => { + const { + pod, + title, + kubeRef, + api, + getDetailsUrl, + } = props; + + if (!kubeRef) { return null; } return ( - - {ref.name} + + {kubeRef.name} ); }; + +export const LocalRef = withInjectables(NonInjectedLocalRef, { + getProps: (di, props) => ({ + ...props, + getDetailsUrl: di.inject(getDetailsUrlInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx index 5555332328..0345050a77 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx @@ -3,13 +3,26 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { render } from "@testing-library/react"; import React from "react"; import type { CephfsSource } from "../../../../../../../common/k8s-api/endpoints"; import { Pod } from "../../../../../../../common/k8s-api/endpoints"; +import { getDiForUnitTesting } from "../../../../../../getDiForUnitTesting"; +import storesAndApisCanBeCreatedInjectable from "../../../../../../stores-apis-can-be-created.injectable"; +import type { DiRender } from "../../../../../test-utils/renderFor"; +import { renderFor } from "../../../../../test-utils/renderFor"; import { CephFs } from "../ceph-fs"; describe("", () => { + let render: DiRender; + + beforeEach(() => { + const di = getDiForUnitTesting({ doGeneralOverrides: true }); + + render = renderFor(di); + + di.override(storesAndApisCanBeCreatedInjectable, () => true); + }); + it("should render 'false' for Readonly when not provided", () => { const cephfsName = "my-ceph"; const cephfsVolume: CephfsSource = { diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx index a723f7be92..64b68436a3 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx @@ -3,14 +3,33 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const CephFs: VolumeVariantComponent<"cephfs"> = ( - ({ pod, variant: { monitors, path = "/", user = "admin", secretFile = "/etc/ceph/user.secret", secretRef, readOnly = false }}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedCephFs = (props: PodVolumeVariantSpecificProps<"cephfs"> & Dependencies) => { + const { + pod, + variant: { + monitors, + path = "/", + user = "admin", + secretFile = "/etc/ceph/user.secret", + secretRef, + readOnly = false, + }, + secretApi, + } = props; + + return ( <>
    @@ -43,5 +62,12 @@ export const CephFs: VolumeVariantComponent<"cephfs"> = ( {readOnly.toString()} - ) -); + ); +}; + +export const CephFs = withInjectables>(NonInjectedCephFs, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx index d4eb28c0e5..e59fed5633 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx @@ -3,18 +3,37 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { configMapApi } from "../../../../../../common/k8s-api/endpoints"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { ConfigMapApi } from "../../../../../../common/k8s-api/endpoints"; +import configMapApiInjectable from "../../../../../../common/k8s-api/endpoints/config-map.api.injectable"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const ConfigMap: VolumeVariantComponent<"configMap"> = ( - ({ pod, variant: { name }}) => ( +interface Dependencies { + configMapApi: ConfigMapApi; +} + +const NonInjectedConfigMap = (props: PodVolumeVariantSpecificProps<"configMap"> & Dependencies) => { + const { + pod, + variant: { name }, + configMapApi, + } = props; + + return ( - ) -); + ); +}; + +export const ConfigMap = withInjectables>(NonInjectedConfigMap, { + getProps: (di, props) => ({ + ...props, + configMapApi: di.inject(configMapApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx index 3949fb8b2b..78576a8ed5 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx @@ -3,66 +3,78 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const ContainerStorageInterface: VolumeVariantComponent<"csi"> = ({ - pod, - variant: { - driver, - readOnly = false, - fsType = "ext4", - volumeAttributes = {}, - nodePublishSecretRef, - controllerPublishSecretRef, - nodeStageSecretRef, - controllerExpandSecretRef, - }, -}) => ( - <> - - {driver} - - - {readOnly.toString()} - - - {fsType} - - - - - - { - Object.entries(volumeAttributes) +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedContainerStorageInterface = (props: PodVolumeVariantSpecificProps<"csi"> & Dependencies) => { + const { + pod, + variant: { + driver, + readOnly = false, + fsType = "ext4", + volumeAttributes = {}, + nodePublishSecretRef, + controllerPublishSecretRef, + nodeStageSecretRef, + controllerExpandSecretRef, + }, + secretApi, + } = props; + + return ( + <> + + {driver} + + + {readOnly.toString()} + + + {fsType} + + + + + + {Object.entries(volumeAttributes) .map(([key, value]) => ( {value} - )) - } - -); + ))} + + ); +}; + +export const ContainerStorageInterface = withInjectables>(NonInjectedContainerStorageInterface, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx index afdb08b380..130edfaaa6 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx @@ -3,14 +3,32 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const FlexVolume: VolumeVariantComponent<"flexVolume"> = ( - ({ pod, variant: { driver, fsType, secretRef, readOnly = false, options = {}}}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedFlexVolume = (props: PodVolumeVariantSpecificProps<"flexVolume"> & Dependencies) => { + const { + pod, + variant: { + driver, + fsType, + secretRef, + readOnly = false, + options = {}, + }, + secretApi, + } = props; + + return ( <> {driver} @@ -36,5 +54,12 @@ export const FlexVolume: VolumeVariantComponent<"flexVolume"> = ( )) } - ) -); + ); +}; + +export const FlexVolume = withInjectables>(NonInjectedFlexVolume, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx index 641442d0ac..9893a65931 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx @@ -3,18 +3,37 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { persistentVolumeClaimApi } from "../../../../../../common/k8s-api/endpoints"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PersistentVolumeClaimApi } from "../../../../../../common/k8s-api/endpoints"; +import persistentVolumeClaimApiInjectable from "../../../../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const PersistentVolumeClaim: VolumeVariantComponent<"persistentVolumeClaim"> = ( - ({ pod, variant: { claimName }}) => ( +interface Dependencies { + persistentVolumeClaimApi: PersistentVolumeClaimApi; +} + +const NonInjectedPersistentVolumeClaim = (props: PodVolumeVariantSpecificProps<"persistentVolumeClaim"> & Dependencies) => { + const { + pod, + variant: { claimName }, + persistentVolumeClaimApi, + } = props; + + return ( - ) -); + ); +}; + +export const PersistentVolumeClaim = withInjectables>(NonInjectedPersistentVolumeClaim, { + getProps: (di, props) => ({ + ...props, + persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx index da619a2954..eac32050d7 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx @@ -3,14 +3,35 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const RadosBlockDevice: VolumeVariantComponent<"rbd"> = ( - ({ pod, variant: { monitors, image, fsType = "ext4", pool = "rbd", user = "admin", keyring = "/etc/ceph/keyright", secretRef, readOnly = false }}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedRadosBlockDevice = (props: PodVolumeVariantSpecificProps<"rbd"> & Dependencies) => { + const { + pod, + variant: { + monitors, + image, + fsType = "ext4", + pool = "rbd", + user = "admin", + keyring = "/etc/ceph/keyright", + secretRef, + readOnly = false, + }, + secretApi, + } = props; + + return ( <>
      @@ -49,5 +70,12 @@ export const RadosBlockDevice: VolumeVariantComponent<"rbd"> = ( {readOnly.toString()} - ) -); + ); +}; + +export const RadosBlockDevice = withInjectables>(NonInjectedRadosBlockDevice, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx index 29208c5719..717be48fe5 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx @@ -3,14 +3,37 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const ScaleIo: VolumeVariantComponent<"scaleIO"> = ( - ({ pod, variant: { gateway, system, secretRef, sslEnabled = false, protectionDomain, storagePool, storageMode = "ThinProvisioned", volumeName, fsType = "xfs", readOnly = false }}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedScaleIo = (props: PodVolumeVariantSpecificProps<"scaleIO"> & Dependencies) => { + const { + pod, + variant: { + gateway, + system, + secretRef, + sslEnabled = false, + protectionDomain, + storagePool, + storageMode = "ThinProvisioned", + volumeName, + fsType = "xfs", + readOnly = false, + }, + secretApi, + } = props; + + return ( <> {gateway} @@ -46,5 +69,12 @@ export const ScaleIo: VolumeVariantComponent<"scaleIO"> = ( {readOnly.toString()} - ) -); + ); +}; + +export const ScaleIo = withInjectables>(NonInjectedScaleIo, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx index 08169a03a4..5ad1ee542c 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx @@ -3,14 +3,26 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const Secret: VolumeVariantComponent<"secret"> = ( - ({ pod, variant: { secretName, items = [], defaultMode = 0o644, optional = false }}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedSecret = (props: PodVolumeVariantSpecificProps<"secret"> & Dependencies) => { + const { + pod, + variant: { secretName, items = [], defaultMode = 0o644, optional = false }, + secretApi, + } = props; + + return ( <> = ( {optional.toString()} - ) -); + ); +}; + +export const Secret = withInjectables>(NonInjectedSecret, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx index 9891052dce..a69ec5918b 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx @@ -3,14 +3,26 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import { secretApi } from "../../../../../../common/k8s-api/endpoints"; +import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; +import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; +import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; -export const StorageOs: VolumeVariantComponent<"storageos"> = ( - ({ pod, variant: { volumeName, volumeNamespace, fsType = "ext4", readOnly = false, secretRef }}) => ( +interface Dependencies { + secretApi: SecretApi; +} + +const NonInjectedStorageOs = (props: PodVolumeVariantSpecificProps<"storageos"> & Dependencies) => { + const { + pod, + variant: { volumeName, volumeNamespace, fsType = "ext4", readOnly = false, secretRef }, + secretApi, + } = props; + + return ( <> {volumeName} @@ -35,5 +47,12 @@ export const StorageOs: VolumeVariantComponent<"storageos"> = ( api={secretApi} /> - ) -); + ); +}; + +export const StorageOs = withInjectables>(NonInjectedStorageOs, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/legacy-store.ts b/src/renderer/components/+workloads-pods/legacy-store.ts deleted file mode 100644 index e05d8fcffd..0000000000 --- a/src/renderer/components/+workloads-pods/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import podStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(podStoreInjectable)` instead - */ -export const podStore = asLegacyGlobalForExtensionApi(podStoreInjectable); diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx index 21d73df6e9..eb2464f664 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import type { ContainerPort, Pod } from "../../../common/k8s-api/endpoints"; import { action, makeObservable, observable, reaction } from "mobx"; import { cssNames } from "../../utils"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { Button } from "../button"; import type { ForwardedPort, PortForwardStore } from "../../port-forward"; import { predictProtocol } from "../../port-forward"; @@ -18,11 +18,13 @@ import { Spinner } from "../spinner"; import { withInjectables } from "@ogre-tools/injectable-react"; import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import aboutPortForwardingInjectable from "../../port-forward/about-port-forwarding.injectable"; import notifyErrorPortForwardingInjectable from "../../port-forward/notify-error-port-forwarding.injectable"; import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable"; import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; export interface PodContainerPortProps { pod: Pod; @@ -31,10 +33,12 @@ export interface PodContainerPortProps { interface Dependencies { portForwardStore: PortForwardStore; + logger: Logger; openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void; aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } @observer @@ -123,7 +127,7 @@ class NonInjectedPodContainerPort extends React.Component( - NonInjectedPodContainerPort, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const PodContainerPort = withInjectables(NonInjectedPodContainerPort, { + getProps: (di, props) => ({ + ...props, + portForwardStore: di.inject(portForwardStoreInjectable), + openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, + aboutPortForwarding: di.inject(aboutPortForwardingInjectable), + notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), + openPortForward: di.inject(openPortForwardInjectable), + logger: di.inject(loggerInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/pod-details-list.tsx b/src/renderer/components/+workloads-pods/pod-details-list.tsx index 46e9e2151f..37e8dab23f 100644 --- a/src/renderer/components/+workloads-pods/pod-details-list.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-list.tsx @@ -9,7 +9,6 @@ import React from "react"; import kebabCase from "lodash/kebabCase"; import { reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; -import { podStore } from "./legacy-store"; import type { Pod } from "../../../common/k8s-api/endpoints"; import { autoBind, bytesToUnits, cssNames, interval, prevDefault } from "../../utils"; import { LineProgress } from "../line-progress"; @@ -18,7 +17,11 @@ import { Table, TableCell, TableHead, TableRow } from "../table"; import { Spinner } from "../spinner"; import { DrawerTitle } from "../drawer"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { showDetails } from "../kube-detail-params"; +import type { PodStore } from "./store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import podStoreInjectable from "./store.injectable"; +import type { ShowDetails } from "../kube-detail-params/show-details.injectable"; +import showDetailsInjectable from "../kube-detail-params/show-details.injectable"; enum sortBy { name = "name", @@ -35,15 +38,20 @@ export interface PodDetailsListProps { maxMemory?: number; } +interface Dependencies { + podStore: PodStore; + showDetails: ShowDetails; +} + @observer -export class PodDetailsList extends React.Component { - constructor(props: PodDetailsListProps) { +class NonInjectedPodDetailsList extends React.Component { + constructor(props: PodDetailsListProps & Dependencies) { super(props); autoBind(this); } private metricsWatcher = interval(120, () => { - podStore.loadKubeMetrics(this.props.owner.getNs()); + this.props.podStore.loadKubeMetrics(this.props.owner.getNs()); }); componentDidMount() { @@ -107,7 +115,7 @@ export class PodDetailsList extends React.Component { } getTableRow(uid: string) { - const { pods } = this.props; + const { pods, podStore, showDetails } = this.props; const pod = pods.find(pod => pod.getId() == uid); if (!pod) { @@ -138,7 +146,7 @@ export class PodDetailsList extends React.Component { } render() { - const { pods } = this.props; + const { pods, podStore } = this.props; if (!podStore.isLoaded) { return ( @@ -197,3 +205,11 @@ export class PodDetailsList extends React.Component { ); } } + +export const PodDetailsList = withInjectables(NonInjectedPodDetailsList, { + getProps: (di, props) => ({ + ...props, + podStore: di.inject(podStoreInjectable), + showDetails: di.inject(showDetailsInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/pod-details-secrets.tsx b/src/renderer/components/+workloads-pods/pod-details-secrets.tsx index de32d174eb..8d78987a75 100644 --- a/src/renderer/components/+workloads-pods/pod-details-secrets.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-secrets.tsx @@ -9,15 +9,27 @@ import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { reaction } from "mobx"; import { observer } from "mobx-react"; -import type { Pod, Secret } from "../../../common/k8s-api/endpoints"; -import { secretApi } from "../../../common/k8s-api/endpoints"; -import { getDetailsUrl } from "../kube-detail-params"; +import type { Pod, Secret, SecretApi } from "../../../common/k8s-api/endpoints"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable"; +import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; +import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; export interface PodDetailsSecretsProps { pod: Pod; } -export const PodDetailsSecrets = observer(({ pod }: PodDetailsSecretsProps) => { +interface Dependencies { + secretApi: SecretApi; + getDetailsUrl: GetDetailsUrl; +} + +const NonInjectedPodDetailsSecrets = observer((props: PodDetailsSecretsProps & Dependencies) => { + const { + pod, + secretApi, + getDetailsUrl, + } = props; const [secrets, setSecrets] = useState(new Map()); useEffect(() => ( @@ -68,3 +80,10 @@ export const PodDetailsSecrets = observer(({ pod }: PodDetailsSecretsProps) => { ); }); +export const PodDetailsSecrets = withInjectables(NonInjectedPodDetailsSecrets, { + getProps: (di, props) => ({ + ...props, + secretApi: di.inject(secretApiInjectable), + getDetailsUrl: di.inject(getDetailsUrlInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-pods/store.injectable.ts b/src/renderer/components/+workloads-pods/store.injectable.ts index a1bf527bef..473cd80c5c 100644 --- a/src/renderer/components/+workloads-pods/store.injectable.ts +++ b/src/renderer/components/+workloads-pods/store.injectable.ts @@ -10,6 +10,7 @@ import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manag import { PodStore } from "./store"; import podMetricsApiInjectable from "../../../common/k8s-api/endpoints/pod-metrics.api.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const podStoreInjectable = getInjectable({ id: "pod-store", @@ -21,6 +22,7 @@ const podStoreInjectable = getInjectable({ return new PodStore({ podMetricsApi: di.inject(podMetricsApiInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx b/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx index 3b6969a5bb..9999917bdb 100644 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx +++ b/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx @@ -14,12 +14,13 @@ import { Dialog } from "../../dialog"; import { Wizard, WizardStep } from "../../wizard"; import { Icon } from "../../icon"; import { Slider } from "../../slider"; -import { Notifications } from "../../notifications"; import { cssNames } from "../../../utils"; import type { ReplicaSet, ReplicaSetApi } from "../../../../common/k8s-api/endpoints"; import { withInjectables } from "@ogre-tools/injectable-react"; import replicaSetApiInjectable from "../../../../common/k8s-api/endpoints/replica-set.api.injectable"; import replicaSetScaleDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface ReplicaSetScaleDialogProps extends Partial { } @@ -27,6 +28,7 @@ export interface ReplicaSetScaleDialogProps extends Partial { interface Dependencies { replicaSetApi: ReplicaSetApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedReplicaSetScaleDialog extends Component { } @@ -27,6 +28,7 @@ export interface StatefulSetScaleDialogProps extends Partial { interface Dependencies { statefulSetApi: StatefulSetApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedStatefulSetScaleDialog extends Component { } @@ -39,6 +40,7 @@ export interface ConfirmDialogBooleanParams { interface Dependencies { state: IObservableValue; + showErrorNotification: ShowNotification; } const defaultParams = { @@ -68,7 +70,7 @@ class NonInjectedConfirmDialog extends React.Component this.params.ok())(); } catch (error) { - Notifications.error( + this.props.showErrorNotification( <>

      Confirmation action failed:

      @@ -96,7 +98,7 @@ class NonInjectedConfirmDialog extends React.Component

      Cancelling action failed:

      @@ -167,5 +169,6 @@ export const ConfirmDialog = withInjectables(N getProps: (di, props) => ({ ...props, state: di.inject(confirmDialogStateInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/countdown/countdown.test.tsx b/src/renderer/components/countdown/countdown.test.tsx index 9bb08f486b..6a84e6970e 100644 --- a/src/renderer/components/countdown/countdown.test.tsx +++ b/src/renderer/components/countdown/countdown.test.tsx @@ -10,7 +10,7 @@ import { renderFor } from "../test-utils/renderFor"; import { Countdown } from "./countdown"; import React from "react"; import type { RenderResult } from "@testing-library/react"; -import { advanceFakeTime, useFakeTime } from "../../../common/test-utils/use-fake-time"; +import { advanceFakeTime, testUsingFakeTime } from "../../../common/test-utils/use-fake-time"; import type { IComputedValue } from "mobx"; import { observe } from "mobx"; import { noop } from "../../../common/utils"; @@ -20,7 +20,7 @@ describe("countdown", () => { let render: DiRender; beforeEach(() => { - useFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime("2015-10-21T07:28:00Z"); di = createContainer("irrelevant"); diff --git a/src/renderer/components/delete-cluster-dialog/is-current-context.tsx b/src/renderer/components/delete-cluster-dialog/is-current-context.tsx new file mode 100644 index 0000000000..e0eebcea63 --- /dev/null +++ b/src/renderer/components/delete-cluster-dialog/is-current-context.tsx @@ -0,0 +1,10 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { KubeConfig } from "@kubernetes/client-node"; +import type { Cluster } from "../../../common/cluster/cluster"; + +export function isCurrentContext(config: KubeConfig, cluster: Cluster) { + return config.currentContext == cluster.contextName; +} diff --git a/src/renderer/components/delete-cluster-dialog/open.injectable.ts b/src/renderer/components/delete-cluster-dialog/open.injectable.ts index 992956845f..987e627774 100644 --- a/src/renderer/components/delete-cluster-dialog/open.injectable.ts +++ b/src/renderer/components/delete-cluster-dialog/open.injectable.ts @@ -2,18 +2,25 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { KubeConfig } from "@kubernetes/client-node"; import { getInjectable } from "@ogre-tools/injectable"; -import type { DeleteClusterDialogState } from "./state.injectable"; +import type { Cluster } from "../../../common/cluster/cluster"; +import { isCurrentContext } from "./is-current-context"; import deleteClusterDialogStateInjectable from "./state.injectable"; -export type OpenDeleteClusterDialog = (props: DeleteClusterDialogState) => void; +export type OpenDeleteClusterDialog = (config: KubeConfig, cluster: Cluster) => void; const openDeleteClusterDialogInjectable = getInjectable({ id: "open-delete-cluster-dialog", instantiate: (di): OpenDeleteClusterDialog => { const state = di.inject(deleteClusterDialogStateInjectable); - return (props) => state.set(props); + return (config, cluster) => state.set({ + cluster, + config, + newCurrentContext: "", + showContextSwitch: isCurrentContext(config, cluster), + }); }, }); diff --git a/src/renderer/components/delete-cluster-dialog/state.injectable.ts b/src/renderer/components/delete-cluster-dialog/state.injectable.ts index 8287ba6445..fe75e034ef 100644 --- a/src/renderer/components/delete-cluster-dialog/state.injectable.ts +++ b/src/renderer/components/delete-cluster-dialog/state.injectable.ts @@ -10,6 +10,8 @@ import type { Cluster } from "../../../common/cluster/cluster"; export interface DeleteClusterDialogState { config: KubeConfig; cluster: Cluster; + showContextSwitch: boolean; + newCurrentContext: string; } const deleteClusterDialogStateInjectable = getInjectable({ diff --git a/src/renderer/components/delete-cluster-dialog/view.tsx b/src/renderer/components/delete-cluster-dialog/view.tsx index 69b3bc1612..78b06c7fe2 100644 --- a/src/renderer/components/delete-cluster-dialog/view.tsx +++ b/src/renderer/components/delete-cluster-dialog/view.tsx @@ -5,7 +5,7 @@ import styles from "./view.module.scss"; import type { IObservableValue } from "mobx"; -import { action, observable } from "mobx"; +import { runInAction } from "mobx"; import { observer } from "mobx-react"; import React from "react"; @@ -29,6 +29,7 @@ import requestDeleteClusterInjectable from "../../../features/cluster/delete-dia import type { SaveKubeconfig } from "./save-kubeconfig.injectable"; import saveKubeconfigInjectable from "./save-kubeconfig.injectable"; import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; +import { isCurrentContext } from "./is-current-context"; interface Dependencies { state: IObservableValue; @@ -42,40 +43,22 @@ interface Dependencies { @observer class NonInjectedDeleteClusterDialog extends React.Component { - private readonly showContextSwitch = observable.box(false); - private readonly newCurrentContext = observable.box(); - - @action - onOpen(state: DeleteClusterDialogState) { - this.newCurrentContext.set(""); - this.showContextSwitch.set(this.isCurrentContext(state)); - } - - onClose = () => { - this.showContextSwitch.set(false); - }; - - removeContext(state: DeleteClusterDialogState) { - state.config.contexts = state.config.contexts.filter(item => - item.name !== state.cluster.contextName, - ); - } - - changeCurrentContext(state: DeleteClusterDialogState) { - const newCurrentContext = this.newCurrentContext.get(); - const showContextSwitch = this.showContextSwitch.get(); - - if (newCurrentContext && showContextSwitch) { - state.config.currentContext = newCurrentContext; - } - } - async onDelete(state: DeleteClusterDialogState) { - const { cluster, config } = state; + const { cluster, config, newCurrentContext, showContextSwitch } = state; await this.props.requestSetClusterAsDeleting(cluster.id); - this.removeContext(state); - this.changeCurrentContext(state); + + runInAction(() => { + this.props.state.set({ + ...state, + config: Object.assign(config, { + contexts: config.contexts.filter(item => item.name !== state.cluster.contextName), + currentContext: newCurrentContext && showContextSwitch + ? newCurrentContext + : config.currentContext, + }), + }); + }); try { await this.props.saveKubeconfig(config, cluster.kubeConfigPath); @@ -85,15 +68,13 @@ class NonInjectedDeleteClusterDialog extends React.Component { this.props.showErrorNotification(`Cannot remove cluster, failed to process config file. ${error}`); } finally { await this.props.requestClearClusterAsDeleting(cluster.id); + this.close(); } - - this.onClose(); } - shouldDeleteBeDisabled({ cluster, config }: DeleteClusterDialogState): boolean { + shouldDeleteBeDisabled({ cluster, config, newCurrentContext, showContextSwitch }: DeleteClusterDialogState): boolean { const noContextsAvailable = config.contexts.filter(context => context.name !== cluster.contextName).length == 0; - const newContextNotSelected = this.newCurrentContext.get() === ""; - const showContextSwitch = this.showContextSwitch.get(); + const newContextNotSelected = newCurrentContext === ""; if (noContextsAvailable) { return false; @@ -102,12 +83,10 @@ class NonInjectedDeleteClusterDialog extends React.Component { return showContextSwitch && newContextNotSelected; } - isCurrentContext({ cluster, config }: DeleteClusterDialogState) { - return config.currentContext == cluster.contextName; - } + renderCurrentContextSwitch(state: DeleteClusterDialogState) { + const { cluster, config, showContextSwitch, newCurrentContext } = state; - renderCurrentContextSwitch({ cluster, config }: DeleteClusterDialogState) { - if (!this.showContextSwitch.get()) { + if (!showContextSwitch) { return null; } @@ -124,10 +103,13 @@ class NonInjectedDeleteClusterDialog extends React.Component { ", () => { let di: DiContainer; @@ -32,16 +23,8 @@ describe("