forked from spinnaker/deck
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New gobal fast property search view. (spinnaker#3005)
Updating tslint to inclued console.log and console.warn.
- Loading branch information
Showing
20 changed files
with
1,154 additions
and
112 deletions.
There are no files selected for viewing
239 changes: 208 additions & 31 deletions
239
app/scripts/modules/netflix/fastProperties/dataNav/fastProperties.controller.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,242 @@ | ||
'use strict'; | ||
|
||
let angular = require('angular'); | ||
import _ from 'lodash'; | ||
|
||
module.exports = angular | ||
.module('spinnaker.netflix.fastProperties.controller', [ | ||
require('angular-ui-router'), | ||
require('core/application/service/applications.read.service.js'), | ||
require('core/cache/deckCacheFactory.js'), | ||
require('../fastProperty.read.service.js'), | ||
require('core/cache/deckCacheFactory.js') | ||
]) | ||
.controller('FastPropertiesController', function ($filter, applicationReader, settings) { | ||
var vm = this; | ||
.controller('FastPropertiesController', function ($scope, $filter, $state, $stateParams, $location, applicationReader, settings, fastPropertyReader) { | ||
let vm = this; | ||
let filterNames = ['app','env', 'region', 'stack', 'cluster']; | ||
|
||
vm.isOn = settings.feature.fastProperty; | ||
vm.fetchingProperties = false; | ||
|
||
vm.applicationsLoaded = false; | ||
$scope.filters = {list: []}; | ||
|
||
vm.sortModel = { key: 'name' }; | ||
vm.clearFilters = () => { | ||
$scope.filters.list = []; | ||
vm.updateFilter(); | ||
}; | ||
|
||
$scope.createFilterTag = function(tag) { | ||
if(tag) { | ||
return { | ||
label: tag.label, | ||
value: tag.value, | ||
clear: () => { | ||
$scope.filters.list.splice(_.findIndex($scope.filters.list, {label: tag.label, value: tag.value}), 1); | ||
vm.updateFilter(); | ||
} | ||
}; | ||
} | ||
}; | ||
|
||
|
||
/* | ||
* Convert the url filter params to tag objects | ||
*/ | ||
let paramToTagList = (label) => _.flatten([$stateParams[label]]).reduce((acc, val) => { | ||
if (!_.isEmpty(val)) { | ||
acc.push($scope.createFilterTag({label:label, value: val})); | ||
} | ||
return acc; | ||
}, []); | ||
|
||
let createTagsFromUrlParams = () => { | ||
return _.flatten(filterNames.map(paramToTagList)); | ||
}; | ||
|
||
$scope.filters.list = _.uniqWith(_.flatten([createTagsFromUrlParams()]), (a, b) => a.label === b.label && a.value === b.value); | ||
|
||
$scope.$watchCollection('filters', () => { | ||
if(vm.propertiesList) { | ||
vm.updateFilter(); | ||
} | ||
}); | ||
|
||
vm.searchTerm = $stateParams.q || ''; | ||
|
||
vm.filterNames = { | ||
SHOW_ALL: 'showall', | ||
GLOBAL: 'global' | ||
}; | ||
|
||
vm.groupNames = { | ||
NONE: 'none', | ||
APP: 'app', | ||
PROPERTY: 'property' | ||
}; | ||
|
||
|
||
vm.isPropertiesListEmpty = () => _.isEmpty(vm.propertiesList); | ||
|
||
// FILTER SETTINGS | ||
vm.filterName = $stateParams.filter || vm.filterNames.SHOW_ALL; | ||
vm.groupName = $stateParams.group || vm.groupNames.NONE; | ||
|
||
vm.selectedFilterIs = (filterName) => { | ||
return vm.filterName === filterName; | ||
}; | ||
|
||
vm.setFilterTo = (filterName) => { | ||
vm.filterName = filterName; | ||
vm.filterAndGroup(vm.searchResults); | ||
}; | ||
|
||
vm.setGroupTo = (groupByName) => { | ||
vm.groupName = groupByName; | ||
vm.filterAndGroup(vm.searchResults); | ||
}; | ||
|
||
vm.updateFilter = () => { | ||
vm.filterAndGroup(vm.searchResults); | ||
}; | ||
|
||
|
||
vm.selectedGroupIs = (groupByName) => { | ||
return vm.groupName === groupByName; | ||
}; | ||
|
||
vm.applicationFilter = ''; | ||
let allPass = (listOfPredicate) => { | ||
return (property) => listOfPredicate.every(predicate => predicate(property)); | ||
}; | ||
|
||
let globalFilterPredicate = (property) => property.appId.includes('All (Global)'); | ||
|
||
let normalizeForNone = (keys) => { | ||
return keys.map((key) => { | ||
return key === 'none' ? '' : key; | ||
}); | ||
}; | ||
|
||
vm.filterObject = { | ||
name: vm.applicationFilter, | ||
let scopeFilterPredicateFactory = (scopeLabel) => { | ||
return (property) => { | ||
let scopeAttrList = $scope.filters.list | ||
.filter((filter) => filter.label === scopeLabel) | ||
.map((filter) => filter.value); | ||
return scopeAttrList.length ? normalizeForNone(scopeAttrList).includes(property.scope[scopeLabel]) : true; | ||
}; | ||
}; | ||
|
||
vm.pagination = { | ||
currentPage: 1, | ||
itemsPerPage: 12, | ||
maxSize: 12 | ||
|
||
let predicateList = filterNames.map(name => scopeFilterPredicateFactory(name)); | ||
|
||
let filters = { | ||
showall: (propertiesList) => angular.copy(propertiesList).filter(allPass([...predicateList])), | ||
global: (propertiesList) => angular.copy(propertiesList).filter(allPass([globalFilterPredicate, ...predicateList])), | ||
}; | ||
|
||
let groupByFn = { | ||
none: (propertiesList) => _.sortBy(angular.copy(propertiesList), (prop) => prop.key.toLowerCase()), | ||
app: (propertiesList) => { | ||
let groups = _.groupBy(angular.copy(propertiesList), 'appId' ); | ||
for (let key in groups) { | ||
groups[key] = _.sortBy(groups[key], (prop) => prop.key.toLowerCase()); | ||
} | ||
return groups; | ||
}, | ||
property: (propertiesList) => _.groupBy(angular.copy(propertiesList), 'key') | ||
}; | ||
|
||
vm.filterAndGroup = (propertyList) => { | ||
vm.propertiesList = groupByFn[vm.groupName]( filters[vm.filterName](propertyList) ); | ||
setStateParams(); | ||
}; | ||
|
||
let setStateParams = () => { | ||
$location.search('q', vm.searchTerm); | ||
$location.search('group', vm.groupName); | ||
$location.search('filter', vm.filterName); | ||
|
||
filterNames.forEach((name) => { | ||
$location.search(name, $scope.filters.list.filter(tag => tag.label === name).map(tag => tag.value)); | ||
}); | ||
}; | ||
|
||
vm.filteredResultPage = function() { | ||
return vm.resultPage(vm.filter(vm.applications)); | ||
}; | ||
|
||
vm.resultPage = function(applications) { | ||
var start = (vm.pagination.currentPage - 1) * vm.pagination.itemsPerPage; | ||
var end = vm.pagination.currentPage * vm.pagination.itemsPerPage; | ||
return applications.slice(start, end); | ||
vm.search = _.debounce(function () { | ||
|
||
$location.search('q', vm.searchTerm); | ||
|
||
if(vm.searchTerm.length) { | ||
vm.fetchingProperties = true; | ||
vm.propertiesList = undefined; | ||
vm.searchError = undefined; | ||
|
||
fastPropertyReader.search(vm.searchTerm).then((data) => { | ||
vm.searchResults = data.propertiesList.map((fp) => { | ||
fp.scope = extractFastPropertyScopeFromId(fp.propertyId); | ||
fp.appId = fp.appId || 'All (Global)'; | ||
return fp; | ||
}); | ||
return vm.searchResults; | ||
}).then((searchResults) => { | ||
vm.filterAndGroup(searchResults); | ||
}).catch(() => { | ||
vm.propertiesList = undefined; | ||
vm.searchError = `No results found for: ${vm.searchTerm}`; | ||
}).finally(() => { | ||
vm.fetchingProperties = false; | ||
}); | ||
} else { | ||
vm.propertiesList = undefined; | ||
vm.fetchingProperties = false; | ||
} | ||
|
||
}, 500); | ||
|
||
|
||
let extractFastPropertyScopeFromId = (propertyId) => { | ||
// Property Id is a pipe delimited key of that has most of the scope info in it. | ||
// $NAME|$APPLICATION|$ENVIRONMENT|$REGION||$STACK|$COUNTRY(|cluster=$CLUSTER) | ||
if (propertyId) { | ||
let items = propertyId.split('|'); | ||
return { | ||
key: items[0], | ||
app: items[1], | ||
env: items[2], | ||
region: items[3], | ||
stack: items[5], | ||
cluster: items[7] ? items[7].split('=')[1] : '', | ||
}; | ||
} | ||
return {}; | ||
}; | ||
|
||
vm.filter = function(applications) { | ||
return sortApplications(filterApplications(applications), vm.sortModel.key); | ||
}; | ||
|
||
vm.filteredCount = function() { | ||
return filterApplications(vm.applications).length; | ||
vm.toggleExtraFilters = () => { | ||
vm.extraFiltersOpened = !vm.extraFiltersOpened; | ||
setStateParams(); | ||
}; | ||
|
||
var filterApplications = function(applications) { | ||
var filtered = $filter('anyFieldFilter')(applications, {name: vm.applicationFilter}); | ||
return filtered; | ||
vm.getRegions = () => { | ||
if(vm.searchResults) { | ||
return _.compact(_.uniq(vm.searchResults.map((fp) => { | ||
return fp.scope.region; | ||
}))); | ||
} | ||
}; | ||
|
||
var sortApplications = function (applicationList, orderKey) { | ||
var sorted = $filter('orderBy')(applicationList, orderKey); | ||
return sorted; | ||
vm.getStacks = () => { | ||
if(vm.searchResults) { | ||
return _.compact(_.uniq(vm.searchResults.map((fp) => { | ||
return fp.scope.stack; | ||
}))); | ||
} | ||
}; | ||
|
||
applicationReader.listApplications().then(function(applications) { | ||
vm.applicationsLoaded = true; | ||
vm.applications = applications; | ||
}); | ||
|
||
vm.search(); | ||
|
||
|
||
return vm; | ||
}); |
11 changes: 0 additions & 11 deletions
11
app/scripts/modules/netflix/fastProperties/dataNav/fastProperties.data.controller.js
This file was deleted.
Oops, something went wrong.
94 changes: 94 additions & 0 deletions
94
app/scripts/modules/netflix/fastProperties/dataNav/fastPropertyFilter.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import * as _ from 'lodash'; | ||
|
||
class FastPropertyFilterDirective implements ng.IDirective { | ||
|
||
scope: any = { | ||
'properties': '=', | ||
'filters': '=', | ||
'createFilterTag': '=' | ||
}; | ||
template: string = `<input type="search" class="form-control" placeholder="Filters: type '?'">`; | ||
restrict: string = 'E'; | ||
fields: string[] = ['app', 'env', 'region', 'stack', 'cluster']; | ||
|
||
link(scope: any, el: any) { | ||
let input = el.children('input'); | ||
|
||
let getScopeAttributeList = (scopeName: string) => { | ||
let results = ['none'].concat(<string[]> _.uniq(scope.properties.map((prop: any) => prop.scope[scopeName]))); | ||
return results; | ||
}; | ||
|
||
let textcompleteComponents = this.fields.map((field) => { | ||
return { | ||
id: field, | ||
match: new RegExp(`${field}:(\\w*|\\s*)$`), | ||
index: 1, | ||
search: (term: string, callback: any) => { | ||
callback(getScopeAttributeList(field).filter((attr: string) => { | ||
if (attr.includes(term) ) { | ||
return attr; | ||
} | ||
})); | ||
}, | ||
replace: (attr: string): string => { | ||
let copy = scope.filters.list.splice(0); | ||
let tagBody = {label: field, value: attr}; | ||
copy.push(scope.createFilterTag(tagBody)); | ||
scope.filters.list = _.uniqWith(copy, (a: any, b: any) => a.label === b.label && a.value === b.value); | ||
scope.$apply(); | ||
return ''; | ||
} | ||
}; | ||
}); | ||
|
||
input.textcomplete(_.flatten([ | ||
textcompleteComponents, | ||
{ | ||
match: /(\s*|\w*)\?(\s*|\w*|')$/, | ||
index: 2, | ||
search: (term: string, callback: any) => { | ||
callback($.map(this.fields, (word: string) => { | ||
return word.indexOf(term) > -1 ? word : null; | ||
})); | ||
}, | ||
replace: (word: string) => { | ||
return `${word}:`; | ||
} | ||
}, | ||
{ | ||
match: /(^|\b)(\w{1,})$/, | ||
search: (term: string, callback: any): any => { | ||
callback($.map(this.fields, (word: string) => { | ||
return word.indexOf(term) > -1 ? word : null; | ||
})); | ||
}, | ||
replace: (word: string): string => { | ||
return `${word}:`; | ||
} | ||
}, | ||
|
||
])); | ||
} | ||
} | ||
|
||
class DirectiveFactory { | ||
|
||
public static getFactoryFor<T extends ng.IDirective>(classType: Function): ng.IDirectiveFactory { | ||
let factory = (...args: any[]): T => { | ||
let directive = <any>classType; | ||
return new directive(args); | ||
}; | ||
|
||
factory.$inject = classType.$inject; | ||
return factory; | ||
} | ||
} | ||
|
||
const moduleName = 'spinnaker.netflix.fastPropertyFilter.directive'; | ||
|
||
angular | ||
.module(moduleName, []) | ||
.directive('fastPropertyFilter', DirectiveFactory.getFactoryFor(FastPropertyFilterDirective)); | ||
|
||
export default moduleName; |
Oops, something went wrong.