Add ability to hide KubeObjectMenu Edit and Remove buttons in extensions (#5107)

This commit is contained in:
Sebastian Malton authored and GitHub committed 2022-05-04 12:44:31 -04:00
1 parent e532b90b72
commit dbdde19222
51 files changed
+1245 -765

No files matched your search

@@ -0,0 +1,138 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExtendableDisposer } from "../../../common/utils";
import { downloadFile, downloadJson } from "../../../common/utils";
import { Notifications } from "../notifications";
import React from "react";
import path from "path";
import { SemVer } from "semver";
import URLParse from "url-parse";
import type { InstallRequest } from "./attempt-install/install-request";
import { reduce } from "lodash";
import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store";
import type { Confirm } from "../confirm-dialog/confirm.injectable";
import { getInjectable } from "@ogre-tools/injectable";
import attemptInstallInjectable from "./attempt-install/attempt-install.injectable";
import getBaseRegistryUrlInjectable from "./get-base-registry-url/get-base-registry-url.injectable";
import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import confirmInjectable from "../confirm-dialog/confirm.injectable";
export interface ExtensionInfo {
name: string;
version?: string;
requireConfirmation?: boolean;
}
export type AttemptInstallByInfo = (info: ExtensionInfo) => Promise<void>;
interface Dependencies {
attemptInstall: (request: InstallRequest, d: ExtendableDisposer) => Promise<void>;
getBaseRegistryUrl: () => Promise<string>;
extensionInstallationStateStore: ExtensionInstallationStateStore;
confirm: Confirm;
}
const attemptInstallByInfo = ({
attemptInstall,
getBaseRegistryUrl,
extensionInstallationStateStore,
confirm,
}: Dependencies): AttemptInstallByInfo => (
async (info) => {
const { name, version, requireConfirmation = false } = info;
const disposer = extensionInstallationStateStore.startPreInstall();
const baseUrl = await getBaseRegistryUrl();
const registryUrl = new URLParse(baseUrl).set("pathname", name).toString();
let json: any;
let finalVersion = version;
try {
json = await downloadJson({ url: registryUrl }).promise;
if (!json || json.error || typeof json.versions !== "object" || !json.versions) {
const message = json?.error ? `: ${json.error}` : "";
Notifications.error(`Failed to get registry information for that extension${message}`);
return disposer();
}
} catch (error) {
if (error instanceof SyntaxError) {
// assume invalid JSON
console.warn("Set registry has invalid json", { url: baseUrl }, error);
Notifications.error("Failed to get valid registry information for that extension. Registry did not return valid JSON");
} else {
console.error("Failed to download registry information", error);
Notifications.error(`Failed to get valid registry information for that extension. ${error}`);
}
return disposer();
}
if (version) {
if (!json.versions[version]) {
if (json["dist-tags"][version]) {
finalVersion = json["dist-tags"][version];
} else {
Notifications.error((
<p>
The <em>{name}</em> extension does not have a version or tag <code>{version}</code>.
</p>
));
return disposer();
}
}
} else {
const versions = Object.keys(json.versions)
.map(version => new SemVer(version, { loose: true, includePrerelease: true }))
// ignore pre-releases for auto picking the version
.filter(version => version.prerelease.length === 0);
finalVersion = reduce(
versions,
(prev, curr) => prev.compareMain(curr) === -1 ? curr : prev,
).format();
}
if (requireConfirmation) {
const proceed = await confirm({
message: (
<p>
Are you sure you want to install{" "}
<b>
{name}@{finalVersion}
</b>
?
</p>
),
labelCancel: "Cancel",
labelOk: "Install",
});
if (!proceed) {
return disposer();
}
}
const url = json.versions[finalVersion].dist.tarball;
const fileName = path.basename(url);
const { promise: dataP } = downloadFile({ url, timeout: 10 * 60 * 1000 });
return attemptInstall({ fileName, dataP }, disposer);
}
);
const attemptInstallByInfoInjectable = getInjectable({
id: "attempt-install-by-info",
instantiate: (di) => attemptInstallByInfo({
attemptInstall: di.inject(attemptInstallInjectable),
getBaseRegistryUrl: di.inject(getBaseRegistryUrlInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
confirm: di.inject(confirmInjectable),
}),
});
export default attemptInstallByInfoInjectable;
@@ -1,23 +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 { attemptInstallByInfo } from "./attempt-install-by-info";
import attemptInstallInjectable from "../attempt-install/attempt-install.injectable";
import getBaseRegistryUrlInjectable from "../get-base-registry-url/get-base-registry-url.injectable";
import extensionInstallationStateStoreInjectable
from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
const attemptInstallByInfoInjectable = getInjectable({
id: "attempt-install-by-info",
instantiate: (di) =>
attemptInstallByInfo({
attemptInstall: di.inject(attemptInstallInjectable),
getBaseRegistryUrl: di.inject(getBaseRegistryUrlInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
}),
});
export default attemptInstallByInfoInjectable;
@@ -1,116 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExtendableDisposer } from "../../../../common/utils";
import { downloadFile, downloadJson } from "../../../../common/utils";
import { Notifications } from "../../notifications";
import { ConfirmDialog } from "../../confirm-dialog";
import React from "react";
import path from "path";
import { SemVer } from "semver";
import URLParse from "url-parse";
import type { InstallRequest } from "../attempt-install/install-request";
import lodash from "lodash";
import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
export interface ExtensionInfo {
name: string;
version?: string;
requireConfirmation?: boolean;
}
interface Dependencies {
attemptInstall: (request: InstallRequest, d: ExtendableDisposer) => Promise<void>;
getBaseRegistryUrl: () => Promise<string>;
extensionInstallationStateStore: ExtensionInstallationStateStore;
}
export const attemptInstallByInfo = ({ attemptInstall, getBaseRegistryUrl, extensionInstallationStateStore }: Dependencies) => async ({
name,
version,
requireConfirmation = false,
}: ExtensionInfo) => {
const disposer = extensionInstallationStateStore.startPreInstall();
const baseUrl = await getBaseRegistryUrl();
const registryUrl = new URLParse(baseUrl).set("pathname", name).toString();
let json: any;
try {
json = await downloadJson({ url: registryUrl }).promise;
if (!json || json.error || typeof json.versions !== "object" || !json.versions) {
const message = json?.error ? `: ${json.error}` : "";
Notifications.error(`Failed to get registry information for that extension${message}`);
return disposer();
}
} catch (error) {
if (error instanceof SyntaxError) {
// assume invalid JSON
console.warn("Set registry has invalid json", { url: baseUrl }, error);
Notifications.error("Failed to get valid registry information for that extension. Registry did not return valid JSON");
} else {
console.error("Failed to download registry information", error);
Notifications.error(`Failed to get valid registry information for that extension. ${error}`);
}
return disposer();
}
if (version) {
if (!json.versions[version]) {
if (json["dist-tags"][version]) {
version = json["dist-tags"][version];
} else {
Notifications.error(
<p>
The <em>{name}</em> extension does not have a version or tag{" "}
<code>{version}</code>.
</p>,
);
return disposer();
}
}
} else {
const versions = Object.keys(json.versions)
.map(
version =>
new SemVer(version, { loose: true, includePrerelease: true }),
)
// ignore pre-releases for auto picking the version
.filter(version => version.prerelease.length === 0);
version = lodash.reduce(versions, (prev, curr) =>
prev.compareMain(curr) === -1 ? curr : prev,
).format();
}
if (requireConfirmation) {
const proceed = await ConfirmDialog.confirm({
message: (
<p>
Are you sure you want to install{" "}
<b>
{name}@{version}
</b>
?
</p>
),
labelCancel: "Cancel",
labelOk: "Install",
});
if (!proceed) {
return disposer();
}
}
const url = json.versions[version].dist.tarball;
const fileName = path.basename(url);
const { promise: dataP } = downloadFile({ url, timeout: 10 * 60 * 1000 });
return attemptInstall({ fileName, dataP }, disposer);
};
@@ -0,0 +1,54 @@
/**
* 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 React from "react";
import type { InstalledExtension } from "../../../extensions/extension-discovery/extension-discovery";
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";
interface Dependencies {
uninstallExtension: (id: LensExtensionId) => Promise<boolean>;
confirm: Confirm;
}
export type ConfirmUninstallExtension = (ext: InstalledExtension) => Promise<void>;
const confirmUninstallExtension = ({
uninstallExtension,
confirm,
}: Dependencies): ConfirmUninstallExtension => (
async (extension) => {
const displayName = extensionDisplayName(
extension.manifest.name,
extension.manifest.version,
);
const confirmed = await confirm({
message: (
<p>
Are you sure you want to uninstall extension <b>{displayName}</b>?
</p>
),
labelOk: "Yes",
labelCancel: "No",
});
if (confirmed) {
await uninstallExtension(extension.id);
}
}
);
const confirmUninstallExtensionInjectable = getInjectable({
id: "confirm-uninstall-extension",
instantiate: (di) => confirmUninstallExtension({
uninstallExtension: di.inject(uninstallExtensionInjectable),
confirm: di.inject(confirmInjectable),
}),
});
export default confirmUninstallExtensionInjectable;
@@ -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 { confirmUninstallExtension } from "./confirm-uninstall-extension";
import uninstallExtensionInjectable from "../uninstall-extension/uninstall-extension.injectable";
const confirmUninstallExtensionInjectable = getInjectable({
id: "confirm-uninstall-extension",
instantiate: (di) =>
confirmUninstallExtension({
uninstallExtension: di.inject(uninstallExtensionInjectable),
}),
});
export default confirmUninstallExtensionInjectable;
@@ -1,35 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import type { InstalledExtension } from "../../../../extensions/extension-discovery/extension-discovery";
import type { LensExtensionId } from "../../../../extensions/lens-extension";
import { extensionDisplayName } from "../../../../extensions/lens-extension";
import { ConfirmDialog } from "../../confirm-dialog";
interface Dependencies {
uninstallExtension: (id: LensExtensionId) => Promise<boolean>;
}
export const confirmUninstallExtension =
({ uninstallExtension }: Dependencies) =>
async (extension: InstalledExtension): Promise<void> => {
const displayName = extensionDisplayName(
extension.manifest.name,
extension.manifest.version,
);
const confirmed = await ConfirmDialog.confirm({
message: (
<p>
Are you sure you want to uninstall extension <b>{displayName}</b>?
</p>
),
labelOk: "Yes",
labelCancel: "No",
});
if (confirmed) {
await uninstallExtension(extension.id);
}
};
@@ -26,7 +26,8 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import userExtensionsInjectable from "./user-extensions/user-extensions.injectable";
import enableExtensionInjectable from "./enable-extension/enable-extension.injectable";
import disableExtensionInjectable from "./disable-extension/disable-extension.injectable";
import confirmUninstallExtensionInjectable from "./confirm-uninstall-extension/confirm-uninstall-extension.injectable";
import type { ConfirmUninstallExtension } from "./confirm-uninstall-extension.injectable";
import confirmUninstallExtensionInjectable from "./confirm-uninstall-extension.injectable";
import installFromInputInjectable from "./install-from-input/install-from-input.injectable";
import installFromSelectFileDialogInjectable from "./install-from-select-file-dialog.injectable";
import type { LensExtensionId } from "../../../extensions/lens-extension";
@@ -39,7 +40,7 @@ interface Dependencies {
userExtensions: IComputedValue<InstalledExtension[]>;
enableExtension: (id: LensExtensionId) => void;
disableExtension: (id: LensExtensionId) => void;
confirmUninstallExtension: (extension: InstalledExtension) => Promise<void>;
confirmUninstallExtension: ConfirmUninstallExtension;
installFromInput: (input: string) => Promise<void>;
installFromSelectFileDialog: () => Promise<void>;
installOnDrop: (files: File[]) => Promise<void>;
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import attemptInstallInjectable from "../attempt-install/attempt-install.injectable";
import { installFromInput } from "./install-from-input";
import attemptInstallByInfoInjectable from "../attempt-install-by-info/attempt-install-by-info.injectable";
import attemptInstallByInfoInjectable from "../attempt-install-by-info.injectable";
import extensionInstallationStateStoreInjectable
from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
@@ -12,7 +12,7 @@ import path from "path";
import React from "react";
import { readFileNotify } from "../read-file-notify/read-file-notify";
import type { InstallRequest } from "../attempt-install/install-request";
import type { ExtensionInfo } from "../attempt-install-by-info/attempt-install-by-info";
import type { ExtensionInfo } from "../attempt-install-by-info.injectable";
import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
interface Dependencies {