github.com/manicqin/nomad@v0.9.5/ui/app/controllers/jobs/index.js (about)

     1  import { inject as service } from '@ember/service';
     2  import { alias } from '@ember/object/computed';
     3  import Controller, { inject as controller } from '@ember/controller';
     4  import { computed } from '@ember/object';
     5  import { scheduleOnce } from '@ember/runloop';
     6  import intersection from 'lodash.intersection';
     7  import Sortable from 'nomad-ui/mixins/sortable';
     8  import Searchable from 'nomad-ui/mixins/searchable';
     9  import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
    10  
    11  export default Controller.extend(Sortable, Searchable, {
    12    system: service(),
    13    jobsController: controller('jobs'),
    14  
    15    isForbidden: alias('jobsController.isForbidden'),
    16  
    17    queryParams: {
    18      currentPage: 'page',
    19      searchTerm: 'search',
    20      sortProperty: 'sort',
    21      sortDescending: 'desc',
    22      qpType: 'type',
    23      qpStatus: 'status',
    24      qpDatacenter: 'dc',
    25      qpPrefix: 'prefix',
    26    },
    27  
    28    currentPage: 1,
    29    pageSize: 10,
    30  
    31    sortProperty: 'modifyIndex',
    32    sortDescending: true,
    33  
    34    searchProps: computed(() => ['id', 'name']),
    35    fuzzySearchProps: computed(() => ['name']),
    36    fuzzySearchEnabled: true,
    37  
    38    qpType: '',
    39    qpStatus: '',
    40    qpDatacenter: '',
    41    qpPrefix: '',
    42  
    43    selectionType: selection('qpType'),
    44    selectionStatus: selection('qpStatus'),
    45    selectionDatacenter: selection('qpDatacenter'),
    46    selectionPrefix: selection('qpPrefix'),
    47  
    48    optionsType: computed(() => [
    49      { key: 'batch', label: 'Batch' },
    50      { key: 'parameterized', label: 'Parameterized' },
    51      { key: 'periodic', label: 'Periodic' },
    52      { key: 'service', label: 'Service' },
    53      { key: 'system', label: 'System' },
    54    ]),
    55  
    56    optionsStatus: computed(() => [
    57      { key: 'pending', label: 'Pending' },
    58      { key: 'running', label: 'Running' },
    59      { key: 'dead', label: 'Dead' },
    60    ]),
    61  
    62    optionsDatacenter: computed('visibleJobs.[]', function() {
    63      const flatten = (acc, val) => acc.concat(val);
    64      const allDatacenters = new Set(this.visibleJobs.mapBy('datacenters').reduce(flatten, []));
    65  
    66      // Remove any invalid datacenters from the query param/selection
    67      const availableDatacenters = Array.from(allDatacenters).compact();
    68      scheduleOnce('actions', () => {
    69        this.set(
    70          'qpDatacenter',
    71          serialize(intersection(availableDatacenters, this.selectionDatacenter))
    72        );
    73      });
    74  
    75      return availableDatacenters.sort().map(dc => ({ key: dc, label: dc }));
    76    }),
    77  
    78    optionsPrefix: computed('visibleJobs.[]', function() {
    79      // A prefix is defined as the start of a job name up to the first - or .
    80      // ex: mktg-analytics -> mktg, ds.supermodel.classifier -> ds
    81      const hasPrefix = /.[-._]/;
    82  
    83      // Collect and count all the prefixes
    84      const allNames = this.visibleJobs.mapBy('name');
    85      const nameHistogram = allNames.reduce((hist, name) => {
    86        if (hasPrefix.test(name)) {
    87          const prefix = name.match(/(.+?)[-._]/)[1];
    88          hist[prefix] = hist[prefix] ? hist[prefix] + 1 : 1;
    89        }
    90        return hist;
    91      }, {});
    92  
    93      // Convert to an array
    94      const nameTable = Object.keys(nameHistogram).map(key => ({
    95        prefix: key,
    96        count: nameHistogram[key],
    97      }));
    98  
    99      // Only consider prefixes that match more than one name
   100      const prefixes = nameTable.filter(name => name.count > 1);
   101  
   102      // Remove any invalid prefixes from the query param/selection
   103      const availablePrefixes = prefixes.mapBy('prefix');
   104      scheduleOnce('actions', () => {
   105        this.set('qpPrefix', serialize(intersection(availablePrefixes, this.selectionPrefix)));
   106      });
   107  
   108      // Sort, format, and include the count in the label
   109      return prefixes.sortBy('prefix').map(name => ({
   110        key: name.prefix,
   111        label: `${name.prefix} (${name.count})`,
   112      }));
   113    }),
   114  
   115    /**
   116      Visible jobs are those that match the selected namespace and aren't children
   117      of periodic or parameterized jobs.
   118    */
   119    visibleJobs: computed('model.[]', 'model.@each.parent', function() {
   120      // Namespace related properties are ommitted from the dependent keys
   121      // due to a prop invalidation bug caused by region switching.
   122      const hasNamespaces = this.get('system.namespaces.length');
   123      const activeNamespace = this.get('system.activeNamespace.id') || 'default';
   124  
   125      return this.model
   126        .compact()
   127        .filter(job => !hasNamespaces || job.get('namespace.id') === activeNamespace)
   128        .filter(job => !job.get('parent.content'));
   129    }),
   130  
   131    filteredJobs: computed(
   132      'visibleJobs.[]',
   133      'selectionType',
   134      'selectionStatus',
   135      'selectionDatacenter',
   136      'selectionPrefix',
   137      function() {
   138        const {
   139          selectionType: types,
   140          selectionStatus: statuses,
   141          selectionDatacenter: datacenters,
   142          selectionPrefix: prefixes,
   143        } = this;
   144  
   145        // A job must match ALL filter facets, but it can match ANY selection within a facet
   146        // Always return early to prevent unnecessary facet predicates.
   147        return this.visibleJobs.filter(job => {
   148          if (types.length && !types.includes(job.get('displayType'))) {
   149            return false;
   150          }
   151  
   152          if (statuses.length && !statuses.includes(job.get('status'))) {
   153            return false;
   154          }
   155  
   156          if (datacenters.length && !job.get('datacenters').find(dc => datacenters.includes(dc))) {
   157            return false;
   158          }
   159  
   160          const name = job.get('name');
   161          if (prefixes.length && !prefixes.find(prefix => name.startsWith(prefix))) {
   162            return false;
   163          }
   164  
   165          return true;
   166        });
   167      }
   168    ),
   169  
   170    listToSort: alias('filteredJobs'),
   171    listToSearch: alias('listSorted'),
   172    sortedJobs: alias('listSearched'),
   173  
   174    isShowingDeploymentDetails: false,
   175  
   176    setFacetQueryParam(queryParam, selection) {
   177      this.set(queryParam, serialize(selection));
   178    },
   179  
   180    actions: {
   181      gotoJob(job) {
   182        this.transitionToRoute('jobs.job', job.get('plainId'));
   183      },
   184    },
   185  });