github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/settings/components/project-details/resource-lists-panel.tsx (about)

     1  import {AutocompleteField, FormField, Tooltip} from 'argo-ui';
     2  import * as React from 'react';
     3  import {FormApi} from 'react-form';
     4  
     5  import {EditablePanel} from '../../../shared/components';
     6  import {ApplicationDestination, ApplicationDestinationServiceAccount, GroupKind, Groups, Project, ProjectSpec, ResourceKinds} from '../../../shared/models';
     7  
     8  function removeEl(items: any[], index: number) {
     9      return items.slice(0, index).concat(items.slice(index + 1));
    10  }
    11  
    12  function helpTip(text: string) {
    13      return (
    14          <Tooltip content={text}>
    15              <span style={{fontSize: 'smaller'}}>
    16                  {' '}
    17                  <i className='fas fa-info-circle' />
    18              </span>
    19          </Tooltip>
    20      );
    21  }
    22  
    23  type field = keyof ProjectSpec;
    24  
    25  const infoByField: {[type: string]: {title: string; helpText: string}} = {
    26      clusterResourceWhitelist: {
    27          title: 'cluster resource allow list',
    28          helpText: 'Cluster-scoped K8s API Groups and Kinds which are permitted to be deployed'
    29      },
    30      clusterResourceBlacklist: {
    31          title: 'cluster resource deny list',
    32          helpText: 'Cluster-scoped K8s API Groups and Kinds which are not permitted to be deployed'
    33      },
    34      namespaceResourceWhitelist: {
    35          title: 'namespace resource allow list',
    36          helpText: 'Namespace-scoped K8s API Groups and Kinds which are permitted to deploy'
    37      },
    38      namespaceResourceBlacklist: {
    39          title: 'namespace resource deny list',
    40          helpText: 'Namespace-scoped K8s API Groups and Kinds which are prohibited from being deployed'
    41      }
    42  };
    43  
    44  function viewList(type: field, proj: Project) {
    45      const info = infoByField[type];
    46      const list = proj.spec[type] as Array<GroupKind>;
    47      return (
    48          <React.Fragment>
    49              <p className='project-details__list-title'>
    50                  {info.title} {helpTip(info.helpText)}
    51              </p>
    52              {(list || []).length > 0 ? (
    53                  <React.Fragment>
    54                      <div className='row white-box__details-row'>
    55                          <div className='columns small-4'>Kind</div>
    56                          <div className='columns small-8'>Group</div>
    57                      </div>
    58                      {list.map((resource, i) => (
    59                          <div className='row white-box__details-row' key={i}>
    60                              <div className='columns small-4'>{resource.kind}</div>
    61                              <div className='columns small-8'>{resource.group}</div>
    62                          </div>
    63                      ))}
    64                  </React.Fragment>
    65              ) : (
    66                  <p>The {info.title} is empty</p>
    67              )}
    68          </React.Fragment>
    69      );
    70  }
    71  
    72  const sourceReposInfoByField: {[type: string]: {title: string; helpText: string}} = {
    73      sourceRepos: {
    74          title: 'source repositories',
    75          helpText: 'Git repositories where application manifests are permitted to be retrieved from'
    76      }
    77  };
    78  
    79  function viewSourceReposInfoList(type: field, proj: Project) {
    80      const info = sourceReposInfoByField[type];
    81      const list = proj.spec[type] as Array<string>;
    82      return (
    83          <React.Fragment>
    84              <p className='project-details__list-title'>
    85                  {info.title} {helpTip(info.helpText)}
    86              </p>
    87              {(list || []).length > 0 ? (
    88                  <React.Fragment>
    89                      {list.map((repo, i) => (
    90                          <div className='row white-box__details-row' key={i}>
    91                              <div className='columns small-12'>{repo}</div>
    92                          </div>
    93                      ))}
    94                  </React.Fragment>
    95              ) : (
    96                  <p>The {info.title} is empty</p>
    97              )}
    98          </React.Fragment>
    99      );
   100  }
   101  
   102  const sourceNamespacesInfoByField: {[type: string]: {title: string; helpText: string}} = {
   103      sourceNamespaces: {
   104          title: 'source namespaces',
   105          helpText: 'Kubernetes namespaces where application resources are allowed to be created in'
   106      }
   107  };
   108  
   109  function viewSourceNamespacesInfoList(type: field, proj: Project) {
   110      const info = sourceNamespacesInfoByField[type];
   111      const list = proj.spec[type] as Array<string>;
   112      return (
   113          <React.Fragment>
   114              <p className='project-details__list-title'>
   115                  {info.title} {helpTip(info.helpText)}
   116              </p>
   117              {(list || []).length > 0 ? (
   118                  <React.Fragment>
   119                      {list.map((namespace, i) => (
   120                          <div className='row white-box__details-row' key={i}>
   121                              <div className='columns small-12'>{namespace}</div>
   122                          </div>
   123                      ))}
   124                  </React.Fragment>
   125              ) : (
   126                  <p>The {info.title} is empty</p>
   127              )}
   128          </React.Fragment>
   129      );
   130  }
   131  
   132  const destinationsInfoByField: {[type: string]: {title: string; helpText: string}} = {
   133      destinations: {
   134          title: 'destinations',
   135          helpText: 'Cluster and namespaces where applications are permitted to be deployed to'
   136      }
   137  };
   138  
   139  function viewDestinationsInfoList(type: field, proj: Project) {
   140      const info = destinationsInfoByField[type];
   141      const list = proj.spec[type] as Array<ApplicationDestination>;
   142      return (
   143          <React.Fragment>
   144              <p className='project-details__list-title'>
   145                  {info.title} {helpTip(info.helpText)}
   146              </p>
   147              {(list || []).length > 0 ? (
   148                  <React.Fragment>
   149                      <div className='row white-box__details-row'>
   150                          <div className='columns small-4'>Server</div>
   151                          <div className='columns small-8'>Namespace</div>
   152                      </div>
   153                      {list.map((destination, i) => (
   154                          <div className='row white-box__details-row' key={i}>
   155                              <div className='columns small-4'>{destination.server}</div>
   156                              <div className='columns small-8'>{destination.namespace}</div>
   157                          </div>
   158                      ))}
   159                  </React.Fragment>
   160              ) : (
   161                  <p>The {info.title} is empty</p>
   162              )}
   163          </React.Fragment>
   164      );
   165  }
   166  
   167  const destinationServiceAccountsInfoByField: {[type: string]: {title: string; helpText: string}} = {
   168      destinationServiceAccounts: {
   169          title: 'destination service accounts',
   170          helpText: 'DestinationServiceAccounts holds information about the service accounts to be impersonated for the application sync operation for each destination.'
   171      }
   172  };
   173  
   174  function viewDestinationServiceAccountsInfoList(type: field, proj: Project) {
   175      const info = destinationServiceAccountsInfoByField[type];
   176      const list = proj.spec[type] as Array<ApplicationDestinationServiceAccount>;
   177      return (
   178          <React.Fragment>
   179              <p className='project-details__list-title'>
   180                  {info.title} {helpTip(info.helpText)}
   181              </p>
   182              {(list || []).length > 0 ? (
   183                  <React.Fragment>
   184                      <div className='row white-box__details-row'>
   185                          <div className='columns small-4'>Server</div>
   186                          <div className='columns small-8'>Namespace</div>
   187                          <div className='columns small-12'>DefaultServiceAccount</div>
   188                      </div>
   189                      {list.map((destinationServiceAccounts, i) => (
   190                          <div className='row white-box__details-row' key={i}>
   191                              <div className='columns small-4'>{destinationServiceAccounts.server}</div>
   192                              <div className='columns small-8'>{destinationServiceAccounts.namespace}</div>
   193                              <div className='columns small-12'>{destinationServiceAccounts.defaultServiceAccount}</div>
   194                          </div>
   195                      ))}
   196                  </React.Fragment>
   197              ) : (
   198                  <p>The {info.title} is empty</p>
   199              )}
   200          </React.Fragment>
   201      );
   202  }
   203  
   204  function editList(type: field, formApi: FormApi) {
   205      const info = infoByField[type];
   206  
   207      return (
   208          <React.Fragment>
   209              <p className='project-details__list-title'>
   210                  {info.title} {helpTip(info.helpText)}
   211              </p>
   212              <div className='row white-box__details-row'>
   213                  <div className='columns small-4'>Kind</div>
   214                  <div className='columns small-8'>Group</div>
   215              </div>
   216              {(formApi.values.spec[type] || []).map((_: Project, i: number) => (
   217                  <div className='row white-box__details-row' key={i}>
   218                      <div className='columns small-4'>
   219                          <FormField
   220                              formApi={formApi}
   221                              field={`spec.${type}[${i}].kind`}
   222                              component={AutocompleteField}
   223                              componentProps={{items: ResourceKinds, filterSuggestions: true}}
   224                          />
   225                      </div>
   226                      <div className='columns small-8'>
   227                          <FormField formApi={formApi} field={`spec.${type}[${i}].group`} component={AutocompleteField} componentProps={{items: Groups, filterSuggestions: true}} />
   228                      </div>
   229                      <i className='fa fa-times' onClick={() => formApi.setValue(`spec.${type}`, removeEl(formApi.values.spec[type], i))} />
   230                  </div>
   231              ))}
   232              <button className='argo-button argo-button--short' onClick={() => formApi.setValue(`spec.${type}`, (formApi.values.spec[type] || []).concat({group: '*', kind: '*'}))}>
   233                  ADD RESOURCE
   234              </button>
   235          </React.Fragment>
   236      );
   237  }
   238  
   239  export const ResourceListsPanel = ({proj, saveProject, title}: {proj: Project; title?: React.ReactNode; saveProject?: (proj: Project) => any}) => (
   240      <EditablePanel
   241          save={saveProject}
   242          values={proj}
   243          view={
   244              <React.Fragment>
   245                  {title}
   246                  {Object.keys(infoByField).map(key => (
   247                      <React.Fragment key={key}>{viewList(key as field, proj)}</React.Fragment>
   248                  ))}
   249                  {!proj.metadata && Object.keys(sourceReposInfoByField).map(key => <React.Fragment key={key}>{viewSourceReposInfoList(key as field, proj)}</React.Fragment>)}
   250                  {!proj.metadata &&
   251                      Object.keys(sourceNamespacesInfoByField).map(key => <React.Fragment key={key}>{viewSourceNamespacesInfoList(key as field, proj)}</React.Fragment>)}
   252                  {!proj.metadata && Object.keys(destinationsInfoByField).map(key => <React.Fragment key={key}>{viewDestinationsInfoList(key as field, proj)}</React.Fragment>)}
   253                  {!proj.metadata &&
   254                      Object.keys(destinationServiceAccountsInfoByField).map(key => (
   255                          <React.Fragment key={key}>{viewDestinationServiceAccountsInfoList(key as field, proj)}</React.Fragment>
   256                      ))}
   257              </React.Fragment>
   258          }
   259          edit={
   260              saveProject &&
   261              (formApi => (
   262                  <React.Fragment>
   263                      {title}
   264                      {Object.keys(infoByField).map(key => (
   265                          <React.Fragment key={key}>{editList(key as field, formApi)}</React.Fragment>
   266                      ))}
   267                  </React.Fragment>
   268              ))
   269          }
   270          items={[]}
   271      />
   272  );