github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/shared/services/extensions-service.ts (about)

     1  import * as React from 'react';
     2  import * as minimatch from 'minimatch';
     3  
     4  import {Application, ApplicationTree, State} from '../models';
     5  
     6  type ExtensionsEventType = 'resource' | 'systemLevel' | 'appView' | 'statusPanel' | 'topBar';
     7  type ExtensionsType = ResourceTabExtension | SystemLevelExtension | AppViewExtension | StatusPanelExtension | TopBarActionMenuExt;
     8  
     9  class ExtensionsEventTarget {
    10      private listeners: Map<ExtensionsEventType, Array<(extension: ExtensionsType) => void>> = new Map();
    11  
    12      addEventListener(eventName: ExtensionsEventType, listener: (extension: ExtensionsType) => void) {
    13          if (!this.listeners.has(eventName)) {
    14              this.listeners.set(eventName, []);
    15          }
    16          this.listeners.get(eventName)?.push(listener);
    17      }
    18  
    19      removeEventListener(eventName: ExtensionsEventType, listenerToRemove: (extension: ExtensionsType) => void) {
    20          const listeners = this.listeners.get(eventName);
    21          if (!listeners) return;
    22  
    23          const filteredListeners = listeners.filter(listener => listener !== listenerToRemove);
    24          this.listeners.set(eventName, filteredListeners);
    25      }
    26  
    27      emit(eventName: ExtensionsEventType, extension: ExtensionsType) {
    28          this.listeners.get(eventName)?.forEach(listener => listener(extension));
    29      }
    30  }
    31  
    32  const extensions = {
    33      eventTarget: new ExtensionsEventTarget(),
    34      resourceExtentions: new Array<ResourceTabExtension>(),
    35      systemLevelExtensions: new Array<SystemLevelExtension>(),
    36      appViewExtensions: new Array<AppViewExtension>(),
    37      statusPanelExtensions: new Array<StatusPanelExtension>(),
    38      topBarActionMenuExts: new Array<TopBarActionMenuExt>()
    39  };
    40  
    41  function registerResourceExtension(component: ExtensionComponent, group: string, kind: string, tabTitle: string, opts?: {icon: string}) {
    42      const ext = {component, group, kind, title: tabTitle, icon: opts?.icon};
    43      extensions.resourceExtentions.push(ext);
    44      extensions.eventTarget.emit('resource', ext);
    45  }
    46  
    47  function registerSystemLevelExtension(component: ExtensionComponent, title: string, path: string, icon: string) {
    48      const ext = {component, title, icon, path};
    49      extensions.systemLevelExtensions.push(ext);
    50      extensions.eventTarget.emit('systemLevel', ext);
    51  }
    52  
    53  function registerAppViewExtension(component: ExtensionComponent, title: string, icon: string) {
    54      const ext = {component, title, icon};
    55      extensions.appViewExtensions.push(ext);
    56      extensions.eventTarget.emit('appView', ext);
    57  }
    58  
    59  function registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: string, id: string, flyout?: ExtensionComponent) {
    60      const ext = {component, flyout, title, id};
    61      extensions.statusPanelExtensions.push(ext);
    62      extensions.eventTarget.emit('statusPanel', ext);
    63  }
    64  
    65  function registerTopBarActionMenuExt(
    66      component: TopBarActionMenuExtComponent,
    67      title: string,
    68      id: string,
    69      flyout: ExtensionComponent,
    70      shouldDisplay: (app?: Application) => boolean = () => true,
    71      iconClassName?: string,
    72      isMiddle = false
    73  ) {
    74      const ext = {component, flyout, shouldDisplay, title, id, iconClassName, isMiddle};
    75      extensions.topBarActionMenuExts.push(ext);
    76      extensions.eventTarget.emit('topBar', ext);
    77  }
    78  
    79  let legacyInitialized = false;
    80  
    81  function initLegacyExtensions() {
    82      if (legacyInitialized) {
    83          return;
    84      }
    85      legacyInitialized = true;
    86      const resources = (window as any).extensions.resources;
    87      Object.keys(resources).forEach(key => {
    88          const [group, kind] = key.split('/');
    89          registerResourceExtension(resources[key].component, group, kind, 'More');
    90      });
    91  }
    92  
    93  export interface ResourceTabExtension {
    94      title: string;
    95      group: string;
    96      kind: string;
    97      component: ExtensionComponent;
    98      icon?: string;
    99  }
   100  
   101  export interface SystemLevelExtension {
   102      title: string;
   103      component: SystemExtensionComponent;
   104      icon?: string;
   105      path?: string;
   106  }
   107  
   108  export interface AppViewExtension {
   109      component: AppViewExtensionComponent;
   110      title: string;
   111      icon?: string;
   112  }
   113  
   114  export interface StatusPanelExtension {
   115      component: StatusPanelExtensionComponent;
   116      flyout?: StatusPanelExtensionFlyoutComponent;
   117      title: string;
   118      id: string;
   119  }
   120  
   121  export interface TopBarActionMenuExt {
   122      component: TopBarActionMenuExtComponent;
   123      flyout: TopBarActionMenuExtFlyoutComponent;
   124      shouldDisplay: (app: Application) => boolean;
   125      title: string;
   126      id: string;
   127      iconClassName?: string;
   128      isMiddle?: boolean;
   129      isNarrow?: boolean;
   130  }
   131  
   132  export type ExtensionComponent = React.ComponentType<ExtensionComponentProps>;
   133  export type SystemExtensionComponent = React.ComponentType;
   134  export type AppViewExtensionComponent = React.ComponentType<AppViewComponentProps>;
   135  export type StatusPanelExtensionComponent = React.ComponentType<StatusPanelComponentProps>;
   136  export type StatusPanelExtensionFlyoutComponent = React.ComponentType<StatusPanelFlyoutProps>;
   137  export type TopBarActionMenuExtComponent = React.ComponentType<TopBarActionMenuExtComponentProps>;
   138  export type TopBarActionMenuExtFlyoutComponent = React.ComponentType<TopBarActionMenuExtFlyoutProps>;
   139  
   140  export interface Extension {
   141      component: ExtensionComponent;
   142  }
   143  
   144  export interface ExtensionComponentProps {
   145      resource: State;
   146      tree: ApplicationTree;
   147      application: Application;
   148  }
   149  
   150  export interface AppViewComponentProps {
   151      application: Application;
   152      tree: ApplicationTree;
   153  }
   154  
   155  export interface StatusPanelComponentProps {
   156      application: Application;
   157      openFlyout: () => any;
   158  }
   159  
   160  export interface TopBarActionMenuExtComponentProps {
   161      application: Application;
   162      tree: ApplicationTree;
   163      openFlyout: () => any;
   164  }
   165  
   166  export interface StatusPanelFlyoutProps {
   167      application: Application;
   168      tree: ApplicationTree;
   169  }
   170  
   171  export interface TopBarActionMenuExtFlyoutProps {
   172      application: Application;
   173      tree: ApplicationTree;
   174  }
   175  
   176  export class ExtensionsService {
   177      public addEventListener(evtType: ExtensionsEventType, cb: (ext: ExtensionsType) => void) {
   178          extensions.eventTarget.addEventListener(evtType, cb);
   179      }
   180  
   181      public removeEventListener(evtType: ExtensionsEventType, cb: (ext: ExtensionsType) => void) {
   182          extensions.eventTarget.removeEventListener(evtType, cb);
   183      }
   184  
   185      public getResourceTabs(group: string, kind: string): ResourceTabExtension[] {
   186          initLegacyExtensions();
   187          const items = extensions.resourceExtentions.filter(extension => minimatch(group, extension.group) && minimatch(kind, extension.kind)).slice();
   188          return items.sort((a, b) => a.title.localeCompare(b.title));
   189      }
   190  
   191      public getSystemExtensions(): SystemLevelExtension[] {
   192          return extensions.systemLevelExtensions.slice();
   193      }
   194  
   195      public getAppViewExtensions(): AppViewExtension[] {
   196          return extensions.appViewExtensions.slice();
   197      }
   198  
   199      public getStatusPanelExtensions(): StatusPanelExtension[] {
   200          return extensions.statusPanelExtensions.slice();
   201      }
   202      public getActionMenuExtensions(): TopBarActionMenuExt[] {
   203          return extensions.topBarActionMenuExts.slice();
   204      }
   205  }
   206  
   207  ((window: any) => {
   208      // deprecated: kept for backwards compatibility
   209      window.extensions = {resources: {}};
   210      window.extensionsAPI = {
   211          registerResourceExtension,
   212          registerSystemLevelExtension,
   213          registerAppViewExtension,
   214          registerStatusPanelExtension,
   215          registerTopBarActionMenuExt
   216      };
   217  })(window);