mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Extension support page (#1112)
Signed-off-by: Roman <ixrock@gmail.com> Co-authored-by: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com>
This commit is contained in:
1 parent
ce995f3deb
commit
f3a0059355
67 files changed
+4511
-553
No files matched your search
@@ -1,27 +0,0 @@
|
||||
import { observable } from "mobx"
|
||||
import React from "react"
|
||||
|
||||
export interface AppPreferenceComponents {
|
||||
Hint: React.ComponentType<any>;
|
||||
Input: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface AppPreferenceRegistration {
|
||||
title: string;
|
||||
components: AppPreferenceComponents;
|
||||
}
|
||||
|
||||
export class AppPreferenceRegistry {
|
||||
preferences = observable.array<AppPreferenceRegistration>([], { deep: false });
|
||||
|
||||
add(preference: AppPreferenceRegistration) {
|
||||
this.preferences.push(preference)
|
||||
return () => {
|
||||
this.preferences.replace(
|
||||
this.preferences.filter(c => c !== preference)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const appPreferenceRegistry = new AppPreferenceRegistry()
|
||||
@@ -0,0 +1,22 @@
|
||||
// Lens-extensions api developer's kit
|
||||
export * from "../lens-main-extension"
|
||||
export * from "../lens-renderer-extension"
|
||||
|
||||
import type { WindowManager } from "../../main/window-manager";
|
||||
|
||||
// APIs
|
||||
import * as EventBus from "./event-bus"
|
||||
import * as Store from "./stores"
|
||||
import * as Util from "./utils"
|
||||
import * as Registry from "../registries"
|
||||
import * as CommonVars from "../../common/vars";
|
||||
|
||||
export let windowManager: WindowManager;
|
||||
|
||||
export {
|
||||
EventBus,
|
||||
Store,
|
||||
Util,
|
||||
Registry,
|
||||
CommonVars,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export type { DynamicPageType, PageRegistry } from "../page-registry"
|
||||
export type { AppPreferenceRegistry } from "../app-preference-registry"
|
||||
export type { KubeObjectMenuRegistry } from "../../renderer/api/kube-object-menu-registry"
|
||||
export type { KubeObjectDetailRegistry } from "../../renderer/api/kube-object-detail-registry"
|
||||
@@ -1,17 +0,0 @@
|
||||
// Lens-extensions api developer's kit
|
||||
export type { LensExtensionRuntimeEnv } from "./lens-runtime";
|
||||
export * from "./lens-main-extension"
|
||||
export * from "./lens-renderer-extension"
|
||||
|
||||
// APIs
|
||||
import * as EventBus from "./core-api/event-bus"
|
||||
import * as Store from "./core-api/stores"
|
||||
import * as Util from "./core-api/utils"
|
||||
import * as Registry from "./core-api/registries"
|
||||
|
||||
export {
|
||||
EventBus,
|
||||
Registry,
|
||||
Store,
|
||||
Util
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { cssNames } from "../renderer/utils";
|
||||
import { TabLayout } from "../renderer/components/layout/tab-layout";
|
||||
import { PageRegistration } from "./page-registry"
|
||||
import { PageRegistration } from "./registries/page-registry"
|
||||
|
||||
export class DynamicPage extends React.Component<{ page: PageRegistration }> {
|
||||
render() {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from "./core-extension-api"
|
||||
export * from "./renderer-extension-api"
|
||||
// Extension-api types generation bundle (used by rollup.js)
|
||||
|
||||
export * from "./core-api"
|
||||
export * from "./renderer-api"
|
||||
@@ -1,14 +1,12 @@
|
||||
import type { ExtensionId, LensExtension, ExtensionManifest, ExtensionModel } from "./lens-extension"
|
||||
import type { ExtensionId, ExtensionManifest, ExtensionModel, LensExtension } from "./lens-extension"
|
||||
import type { LensMainExtension } from "./lens-main-extension"
|
||||
import type { LensRendererExtension } from "./lens-renderer-extension"
|
||||
import { broadcastIpc } from "../common/ipc"
|
||||
import type { LensExtensionRuntimeEnv } from "./lens-runtime"
|
||||
import path from "path"
|
||||
import { broadcastIpc } from "../common/ipc"
|
||||
import { observable, reaction, toJS, } from "mobx"
|
||||
import logger from "../main/logger"
|
||||
import { app, remote, ipcRenderer } from "electron"
|
||||
import { pageRegistry } from "./page-registry";
|
||||
import { appPreferenceRegistry } from "./app-preference-registry"
|
||||
import { kubeObjectMenuRegistry } from "../renderer/api/kube-object-menu-registry"
|
||||
import { app, ipcRenderer, remote } from "electron"
|
||||
import { appPreferenceRegistry, kubeObjectMenuRegistry, menuRegistry, pageRegistry, statusBarRegistry } from "./registries";
|
||||
|
||||
export interface InstalledExtension extends ExtensionModel {
|
||||
manifestPath: string;
|
||||
@@ -36,33 +34,34 @@ export class ExtensionLoader {
|
||||
}
|
||||
}
|
||||
|
||||
loadOnClusterRenderer(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer')
|
||||
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
||||
loadOnMain() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main')
|
||||
this.autoloadExtensions((instance: LensMainExtension) => {
|
||||
instance.registerAppMenus(menuRegistry);
|
||||
})
|
||||
}
|
||||
|
||||
loadOnClusterManagerRenderer() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
|
||||
this.autoloadExtensions((instance: LensRendererExtension) => {
|
||||
instance.registerPages(pageRegistry)
|
||||
instance.registerAppPreferences(appPreferenceRegistry)
|
||||
instance.registerStatusBarIcon(statusBarRegistry)
|
||||
})
|
||||
}
|
||||
|
||||
loadOnClusterRenderer() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)')
|
||||
this.autoloadExtensions((instance: LensRendererExtension) => {
|
||||
instance.registerPages(pageRegistry)
|
||||
instance.registerKubeObjectMenus(kubeObjectMenuRegistry)
|
||||
})
|
||||
}
|
||||
|
||||
loadOnMainRenderer(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main renderer')
|
||||
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
||||
instance.registerPages(pageRegistry)
|
||||
instance.registerAppPreferences(appPreferenceRegistry)
|
||||
})
|
||||
}
|
||||
|
||||
loadOnMain(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main')
|
||||
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensExtension) => {
|
||||
// todo
|
||||
})
|
||||
}
|
||||
|
||||
protected autoloadExtensions(getLensRuntimeEnv: () => LensExtensionRuntimeEnv, callback: (instance: LensExtension) => void) {
|
||||
protected autoloadExtensions(callback: (instance: LensExtension) => void) {
|
||||
return reaction(() => this.extensions.toJS(), (installedExtensions) => {
|
||||
for(const [id, ext] of installedExtensions) {
|
||||
let instance = this.instances.get(ext.manifestPath)
|
||||
for (const [id, ext] of installedExtensions) {
|
||||
let instance = this.instances.get(ext.name)
|
||||
if (!instance) {
|
||||
const extensionModule = this.requireExtension(ext)
|
||||
if (!extensionModule) {
|
||||
@@ -70,9 +69,9 @@ export class ExtensionLoader {
|
||||
}
|
||||
const LensExtensionClass = extensionModule.default;
|
||||
instance = new LensExtensionClass({ ...ext.manifest, manifestPath: ext.manifestPath, id: ext.manifestPath }, ext.manifest);
|
||||
instance.enable(getLensRuntimeEnv())
|
||||
instance.enable();
|
||||
callback(instance)
|
||||
this.instances.set(ext.id, instance)
|
||||
this.instances.set(ext.name, instance)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@@ -106,7 +105,9 @@ export class ExtensionLoader {
|
||||
const extension = this.getById(id);
|
||||
if (extension) {
|
||||
const instance = this.instances.get(extension.id)
|
||||
if (instance) { await instance.disable() }
|
||||
if (instance) {
|
||||
await instance.disable()
|
||||
}
|
||||
this.extensions.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { LensExtensionRuntimeEnv } from "./lens-runtime";
|
||||
import { readJsonSync } from "fs-extra";
|
||||
import { action, observable, toJS } from "mobx";
|
||||
import logger from "../main/logger";
|
||||
@@ -34,7 +33,6 @@ export class LensExtension implements ExtensionModel {
|
||||
@observable manifest: ExtensionManifest;
|
||||
@observable manifestPath: string;
|
||||
@observable isEnabled = false;
|
||||
@observable.ref runtime: LensExtensionRuntimeEnv;
|
||||
|
||||
constructor(model: ExtensionModel, manifest: ExtensionManifest) {
|
||||
this.importModel(model, manifest);
|
||||
@@ -56,9 +54,8 @@ export class LensExtension implements ExtensionModel {
|
||||
// mock
|
||||
}
|
||||
|
||||
async enable(runtime: LensExtensionRuntimeEnv) {
|
||||
async enable() {
|
||||
this.isEnabled = true;
|
||||
this.runtime = runtime;
|
||||
logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`);
|
||||
this.onActivate();
|
||||
}
|
||||
@@ -66,7 +63,6 @@ export class LensExtension implements ExtensionModel {
|
||||
async disable() {
|
||||
this.onDeactivate();
|
||||
this.isEnabled = false;
|
||||
this.runtime = null;
|
||||
this.disposers.forEach(cleanUp => cleanUp());
|
||||
this.disposers.length = 0;
|
||||
logger.info(`[EXTENSION]: disabled ${this.name}@${this.version}`);
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import type { MenuRegistry } from "./registries/menu-registry";
|
||||
import type { StatusBarRegistry } from "./registries/status-bar-registry";
|
||||
|
||||
export class LensMainExtension extends LensExtension {
|
||||
async registerAppMenus() {
|
||||
registerAppMenus(registry: MenuRegistry) {
|
||||
//
|
||||
}
|
||||
|
||||
async registerPrometheusProviders(registry: any) {
|
||||
registerStatusBarIcon(registry: StatusBarRegistry) {
|
||||
//
|
||||
}
|
||||
|
||||
registerPrometheusProviders(registry: any) {
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import type { PageRegistry } from "./page-registry"
|
||||
import type { AppPreferenceRegistry } from "./app-preference-registry";
|
||||
import type { KubeObjectMenuRegistry } from "../renderer/api/kube-object-menu-registry";
|
||||
import type { PageRegistry, AppPreferenceRegistry, StatusBarRegistry, KubeObjectMenuRegistry } from "./registries"
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
|
||||
registerPages(registry: PageRegistry) {
|
||||
return
|
||||
}
|
||||
@@ -13,6 +10,10 @@ export class LensRendererExtension extends LensExtension {
|
||||
return
|
||||
}
|
||||
|
||||
registerStatusBarIcon(registry: StatusBarRegistry) {
|
||||
return
|
||||
}
|
||||
|
||||
registerKubeObjectMenus(registry: KubeObjectMenuRegistry) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Lens extension runtime params available to extensions after activation
|
||||
|
||||
import logger from "../main/logger";
|
||||
|
||||
export interface LensExtensionRuntimeEnv {
|
||||
logger: typeof logger;
|
||||
}
|
||||
|
||||
export function getLensRuntime(): LensExtensionRuntimeEnv {
|
||||
return {
|
||||
logger
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Extensions-api -> Dynamic pages
|
||||
|
||||
import { computed, observable } from "mobx";
|
||||
import React from "react";
|
||||
import { RouteProps } from "react-router";
|
||||
import { IconProps } from "../renderer/components/icon";
|
||||
import { IClassName } from "../renderer/utils";
|
||||
import { TabRoute } from "../renderer/components/layout/tab-layout";
|
||||
|
||||
export enum DynamicPageType {
|
||||
GLOBAL = "lens-scope",
|
||||
CLUSTER = "cluster-view-scope",
|
||||
}
|
||||
|
||||
export interface PageRegistration extends RouteProps {
|
||||
className?: IClassName;
|
||||
url?: string; // initial url to be used for building menus and tabs, otherwise "path" applied by default
|
||||
path: string; // route-path
|
||||
title: React.ReactNode; // used in sidebar's & tabs-layout
|
||||
type: DynamicPageType;
|
||||
components: PageComponents;
|
||||
subPages?: (PageRegistration & TabRoute)[];
|
||||
}
|
||||
|
||||
export interface PageComponents {
|
||||
Page: React.ComponentType<any>;
|
||||
MenuIcon: React.ComponentType<IconProps>;
|
||||
}
|
||||
|
||||
export class PageRegistry {
|
||||
protected pages = observable.array<PageRegistration>([], { deep: false });
|
||||
|
||||
@computed get globalPages() {
|
||||
return this.pages.filter(page => page.type === DynamicPageType.GLOBAL);
|
||||
}
|
||||
|
||||
@computed get clusterPages() {
|
||||
return this.pages.filter(page => page.type === DynamicPageType.CLUSTER);
|
||||
}
|
||||
|
||||
// todo: verify paths to avoid collision with existing pages
|
||||
add(params: PageRegistration) {
|
||||
this.pages.push(params);
|
||||
return () => {
|
||||
this.pages.replace(
|
||||
this.pages.filter(page => page.components !== params.components)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const pageRegistry = new PageRegistry();
|
||||
@@ -0,0 +1,17 @@
|
||||
import type React from "react"
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface AppPreferenceComponents {
|
||||
Hint: React.ComponentType<any>;
|
||||
Input: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface AppPreferenceRegistration {
|
||||
title: string;
|
||||
components: AppPreferenceComponents;
|
||||
}
|
||||
|
||||
export class AppPreferenceRegistry extends BaseRegistry<AppPreferenceRegistration> {
|
||||
}
|
||||
|
||||
export const appPreferenceRegistry = new AppPreferenceRegistry()
|
||||
@@ -0,0 +1,17 @@
|
||||
// Base class for extensions-api registries
|
||||
import { observable } from "mobx";
|
||||
|
||||
export class BaseRegistry<T = any> {
|
||||
protected items = observable<T>([], { deep: false });
|
||||
|
||||
getItems(): T[] {
|
||||
return this.items.toJS();
|
||||
}
|
||||
|
||||
add(item: T) {
|
||||
this.items.push(item);
|
||||
return () => {
|
||||
this.items.remove(item); // works because of {deep: false};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// All registries managed by extensions api
|
||||
|
||||
export * from "./page-registry"
|
||||
export * from "./menu-registry"
|
||||
export * from "./app-preference-registry"
|
||||
export * from "./status-bar-registry"
|
||||
export * from "./kube-object-menu-registry";
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from "react"
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectMenuComponents {
|
||||
MenuItem: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface KubeObjectMenuRegistration {
|
||||
kind: string;
|
||||
apiVersions: string[];
|
||||
components: KubeObjectMenuComponents;
|
||||
}
|
||||
|
||||
export class KubeObjectMenuRegistry extends BaseRegistry<KubeObjectMenuRegistration> {
|
||||
getItemsForKind(kind: string, apiVersion: string) {
|
||||
return this.items.filter((item) => {
|
||||
return item.kind === kind && item.apiVersions.includes(apiVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const kubeObjectMenuRegistry = new KubeObjectMenuRegistry()
|
||||
@@ -0,0 +1,14 @@
|
||||
// Extensions API -> Global menu customizations
|
||||
|
||||
import type { MenuTopId } from "../../main/menu";
|
||||
import type { MenuItemConstructorOptions } from "electron";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface MenuRegistration extends MenuItemConstructorOptions {
|
||||
parentId?: MenuTopId;
|
||||
}
|
||||
|
||||
export class MenuRegistry extends BaseRegistry<MenuRegistration> {
|
||||
}
|
||||
|
||||
export const menuRegistry = new MenuRegistry();
|
||||
@@ -0,0 +1,40 @@
|
||||
// Extensions-api -> Dynamic pages
|
||||
|
||||
import type React from "react";
|
||||
import type { RouteProps } from "react-router";
|
||||
import type { IconProps } from "../../renderer/components/icon";
|
||||
import type { IClassName } from "../../renderer/utils";
|
||||
import type { TabRoute } from "../../renderer/components/layout/tab-layout";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { computed } from "mobx";
|
||||
|
||||
export enum PageRegistryType {
|
||||
GLOBAL = "lens-scope",
|
||||
CLUSTER = "cluster-view-scope",
|
||||
}
|
||||
|
||||
export interface PageRegistration extends RouteProps {
|
||||
type: PageRegistryType;
|
||||
components: PageComponents;
|
||||
className?: IClassName;
|
||||
url?: string; // initial url to be used for building menus and tabs, otherwise "path" applied by default
|
||||
title?: React.ReactNode; // used in sidebar's & tabs-layout if provided
|
||||
subPages?: (PageRegistration & TabRoute)[];
|
||||
}
|
||||
|
||||
export interface PageComponents {
|
||||
Page: React.ComponentType<any>;
|
||||
MenuIcon?: React.ComponentType<IconProps>;
|
||||
}
|
||||
|
||||
export class PageRegistry extends BaseRegistry<PageRegistration> {
|
||||
@computed get globalPages() {
|
||||
return this.items.filter(page => page.type === PageRegistryType.GLOBAL);
|
||||
}
|
||||
|
||||
@computed get clusterPages() {
|
||||
return this.items.filter(page => page.type === PageRegistryType.CLUSTER);
|
||||
}
|
||||
}
|
||||
|
||||
export const pageRegistry = new PageRegistry();
|
||||
@@ -0,0 +1,13 @@
|
||||
// Extensions API -> Status bar customizations
|
||||
|
||||
import React from "react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface StatusBarRegistration {
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export class StatusBarRegistry extends BaseRegistry<StatusBarRegistration> {
|
||||
}
|
||||
|
||||
export const statusBarRegistry = new StatusBarRegistry();
|
||||
@@ -1,10 +1,12 @@
|
||||
// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps)
|
||||
|
||||
export * from "../../renderer/components/icon"
|
||||
export * from "../../renderer/components/checkbox"
|
||||
export * from "../../renderer/components/tooltip"
|
||||
export * from "../../renderer/components/button"
|
||||
export * from "../../renderer/components/tabs"
|
||||
export * from "../../renderer/components/badge"
|
||||
export * from "../../renderer/components/layout/page-layout"
|
||||
export * from "../../renderer/components/drawer"
|
||||
|
||||
// kube helpers
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Lens-extensions apis, required in renderer process runtime
|
||||
|
||||
// APIs
|
||||
import * as Component from "./components"
|
||||
import * as K8sApi from "./k8s-api"
|
||||
import * as Navigation from "./navigation"
|
||||
|
||||
export {
|
||||
Component,
|
||||
K8sApi,
|
||||
Navigation,
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// APIs
|
||||
import * as Component from "./renderer-api/components"
|
||||
import * as K8sApi from "./renderer-api/k8s-api"
|
||||
import * as Navigation from "./renderer-api/navigation"
|
||||
|
||||
export {
|
||||
Component,
|
||||
K8sApi,
|
||||
Navigation,
|
||||
}
|
||||
Reference in new issue
Block a user