Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BZ#145848583 - Introduce service tag filtering #840

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"no-bitwise": "error",
"no-caller": "error",
"no-catch-shadow": "error",
"no-confusing-arrow": "error",
"no-confusing-arrow": ["error", {"allowParens": true}],
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
Expand Down
86 changes: 39 additions & 47 deletions client/app/services/service-explorer/service-explorer.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@ export const ServiceExplorerComponent = {
/** @ngInject */
function ComponentController($state, ServicesState, Language, ListView, Chargeback, TaggingService, TagEditorModal,
EventNotifications, ModalService, PowerOperations, lodash, Polling, POLLING_INTERVAL) {
var vm = this;
const vm = this;

vm.$onDestroy = function() {
Polling.stop('serviceListPolling');
};

vm.$onInit = () => {
vm.permissions = ServicesState.getPermissions();
if ($state.params.filter) {
ServicesState.services.setFilters($state.params.filter);
ServicesState.services.filterApplied = true;
} else {
ServicesState.services.setFilters([]);
ServicesState.services.filterApplied = false;
}
$state.params.filter ? ServicesState.services.setFilters($state.params.filter) : ServicesState.services.setFilters([]);
ServicesState.services.setSort({id: "created_at", title: "Created", sortType: "numeric"}, false);

TaggingService.queryAvailableTags().then((response) => {
vm.filterTags = response;
});

angular.extend(vm, {
loading: false,
title: __('Services'),
Expand All @@ -53,17 +52,22 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
cardConfig: getCardConfig(),
listConfig: getListConfig(),
listActions: getListActions(),
headerConfig: getHeaderConfig(),
toolbarConfig: {
sortConfig: serviceSortConfig(),
filterConfig: serviceFilterConfig(),
actionsConfig: {
actionsInclude: true,
},
},
menuActions: getMenuActions(),
serviceChildrenListConfig: createServiceChildrenListConfig(),
pollingInterval: POLLING_INTERVAL,
});
vm.offset = 0;

Language.fixState(ServicesState.services, vm.headerConfig);
Language.fixState(ServicesState.services, vm.toolbarConfig);

resolveServices(vm.limit, 0);
Polling.start('serviceListPolling', pollUpdateServicesList, vm.pollingInterval);
};

function getCardConfig() {
Expand All @@ -86,7 +90,7 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba

function handleSelectionChange() {
vm.selectedItemsList = vm.servicesList.filter((service) => service.selected);
vm.headerConfig.filterConfig.selectedCount = vm.selectedItemsList.length;
vm.toolbarConfig.filterConfig.selectedCount = vm.selectedItemsList.length;
}

function isAnsibleService(service) {
Expand Down Expand Up @@ -173,30 +177,24 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
}


function getHeaderConfig() {
var serviceFilterConfig = {
fields: getServiceFilterFields(),
function serviceFilterConfig() {
return {
fields: [],
resultsCount: 0,
totalCount: 0,
selectedCount: 0,
appliedFilters: ServicesState.services.filterApplied ? ServicesState.services.getFilters() : [],
appliedFilters: ServicesState.services.getFilters() || [],
onFilterChange: filterChange,
};
}

var serviceSortConfig = {
function serviceSortConfig() {
return {
fields: getServiceSortFields(),
onSortChange: sortChange,
isAscending: ServicesState.services.getSort().isAscending,
currentField: ServicesState.services.getSort().currentField,
};

return {
sortConfig: serviceSortConfig,
filterConfig: serviceFilterConfig,
actionsConfig: {
actionsInclude: true,
},
};
}

function createServiceChildrenListConfig() {
Expand Down Expand Up @@ -318,14 +316,15 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
}

function getServiceFilterFields() {
const filterTags = lodash(vm.filterTags.map((tag) => (tag.name.match(/\//g).length === 3 ? tag.name : false)))
.compact()
.uniqBy()
.value();

return [
ListView.createFilterField('name', __('Name'), __('Filter by Name'), 'text'),
ListView.createFilterField('description', __('Description'), __('Filter by Description'), 'text'),

// TODO: find a way to filter on virtual attributes
// ListView.createFilterField('chargeback_relative_cost', __('Relative Cost'), __('Filter by Relative Cost'), 'select', dollars),
// TODO: find a good way to filter on date other than string
// ListView.createFilterField('owner', __('Created'), __('Filter by Created On'), 'text'),
ListView.createFilterField('tags.name', __('Tag Category/Value'), __('Filter by Tag Category/Value'), 'select', filterTags),
];
}

Expand All @@ -334,9 +333,6 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
ListView.createSortField('created_at', __('Created'), 'numeric'),
ListView.createSortField('name', __('Name'), 'alpha'),
ListView.createSortField('retires_on', __('Retirement Date'), 'numeric'),

// TODO: Find a way to sort by charback cost
// ListView.createSortField('chargeback_report.used_cost_sum', __('Relative Cost'), 'alpha'),
];
}

Expand All @@ -347,7 +343,7 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba

function querySuccess(result) {
vm.filterCount = result.subcount;
vm.headerConfig.filterConfig.resultsCount = vm.filterCount;
vm.toolbarConfig.filterConfig.resultsCount = vm.filterCount;
resolve();
}

Expand All @@ -360,31 +356,27 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
}

function resolveServices(limit, offset, refresh) {
if (!refresh) {
vm.loading = true;
} else {
vm.loading = false;
}
Polling.stop('serviceListPolling');
vm.loading = !refresh;
vm.offset = offset;
getFilterCount().then(() => {
ServicesState.getServices(
limit,
offset,
ServicesState.services.getFilters(),
ServicesState.services.getSort().currentField,
ServicesState.services.getSort().isAscending,
refresh).then(querySuccess, queryFailure);
});

function querySuccess(result) {
Polling.start('serviceListPolling', pollUpdateServicesList, vm.pollingInterval);
vm.loading = false;
vm.services = [];
var existingServices = (angular.isDefined(vm.servicesList) && refresh ? angular.copy(vm.servicesList) : []);
vm.selectedItemsList = [];
vm.headerConfig.filterConfig.totalCount = result.subcount;
vm.headerConfig.filterConfig.selectedCount = 0;
vm.toolbarConfig.filterConfig.fields = getServiceFilterFields();
vm.toolbarConfig.filterConfig.totalCount = result.subcount;
vm.toolbarConfig.filterConfig.selectedCount = 0;

angular.forEach(result.resources, function(item) {
result.resources.forEach((item) => {
if (angular.isUndefined(item.service_id)) {
item.disableRowExpansion = item.all_service_children.length < 1;
item.power_state = PowerOperations.getPowerState(item);
Expand Down Expand Up @@ -426,9 +418,9 @@ function ComponentController($state, ServicesState, Language, ListView, Chargeba
return (powerState !== 'on' && powerState !== 'off' ? powerStates.unknown : powerStates[powerState]);
}

function queryFailure(_error) {
function queryFailure(response) {
vm.loading = false;
EventNotifications.error(__('There was an error loading the services.'));
EventNotifications.error(__('There was an error loading the services. ') + response.data.error.message);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ describe('Component: serviceExplorer', () => {
});

it('should set toolbar', () => {
expect(ctrl.headerConfig.sortConfig.fields).to.have.lengthOf(3);
expect(ctrl.headerConfig.sortConfig.currentField).to.eql({id: 'created_at', title: 'Created', sortType: 'numeric'});
expect(ctrl.toolbarConfig.sortConfig.fields).to.have.lengthOf(3);
expect(ctrl.toolbarConfig.sortConfig.currentField).to.eql({id: 'created_at', title: 'Created', sortType: 'numeric'});
});

it('should allow for sorting to be able to be updated', () => {
const catalogSpy = sinon.spy(ServicesState.services, 'setSort');
ctrl.headerConfig.sortConfig.onSortChange('name', true);
ctrl.toolbarConfig.sortConfig.onSortChange('name', true);

expect(catalogSpy).to.have.been.called.once;
});
Expand Down
43 changes: 25 additions & 18 deletions client/app/services/service-explorer/service-explorer.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
</div>
</div>
</div>
<pf-toolbar class="section-toolbar section-toolbar-right-actions" config="vm.headerConfig">
<pf-toolbar class="section-toolbar section-toolbar-right-actions" config="vm.toolbarConfig">
<actions>
<custom-dropdown class="custom-dropdown pull-left"
config="item"
items="vm.selectedItemsList"
items-count="vm.headerConfig.filterConfig.selectedCount"
items-count="vm.toolbarConfig.filterConfig.selectedCount"
ng-repeat="item in vm.listActions"
on-update="vm.listActionDisable($config, $changes)"
menu-right="true">
Expand All @@ -29,10 +29,10 @@
<div ng-if="vm.viewType == 'listView'" class="list-view-container section-container explorer-list">
<loading status="vm.loading"></loading>
<pf-list-view ng-if="!vm.loading" id="serviceList" config="vm.listConfig"
items="vm.servicesList"
menu-actions="vm.menuActions"
update-menu-action-for-item-fn="vm.updateMenuActionForItemFn"
custom-scope="vm">
items="vm.servicesList"
menu-actions="vm.menuActions"
update-menu-action-for-item-fn="vm.updateMenuActionForItemFn"
custom-scope="vm">
<div class="row service-explorer-row">
<div class="col-lg-4 col-md-4 col-sm-5 col-xs-8 name-column">
<span class="no-wrap">
Expand Down Expand Up @@ -70,7 +70,8 @@
</span>
</div>
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-4" ng-if="$ctrl.customScope.isAnsibleService(item)">
<span class="no-wrap" uib-tooltip="{{ 'Playbook: [[playbook]]'|translate|substitute:{playbook: item.playbook} }}"
<span class="no-wrap"
uib-tooltip="{{ 'Playbook: [[playbook]]'|translate|substitute:{playbook: item.playbook} }}"
tooltip-append-to-body="true"
tooltip-popup-delay="1000"
tooltip-placement="bottom">
Expand Down Expand Up @@ -121,11 +122,11 @@
</div>
<list-expanded-content>
<pf-list-view
class="explorer-children-list"
config="vm.serviceChildrenListConfig"
items="$parent.item.all_service_children"
custom-scope="vm"
update-menu-action-for-item-fn="vm.updateMenuActionForItemFn">
class="explorer-children-list"
config="vm.serviceChildrenListConfig"
items="$parent.item.all_service_children"
custom-scope="vm"
update-menu-action-for-item-fn="vm.updateMenuActionForItemFn">
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-5 col-xs-8 name-column">
<span class="no-wrap">
Expand Down Expand Up @@ -179,25 +180,31 @@
<div class="col-lg-1 col-md-1 hidden-sm hidden-xs">
</div>
</div>
<div uib-dropdown="" dropdown-append-to-body class="dropdown dropdown-kebab-pf" ng-if="!$ctrl.customScope.isAnsibleService(item)">
<button uib-dropdown-toggle="" class="btn btn-link dropdown-toggle" type="button"ng-class="{'disabled': checkDisabled(item)}">
<div uib-dropdown="" dropdown-append-to-body class="dropdown dropdown-kebab-pf"
ng-if="!$ctrl.customScope.isAnsibleService(item)">
<button uib-dropdown-toggle="" class="btn btn-link dropdown-toggle" type="button"
ng-class="{'disabled': checkDisabled(item)}">
<span class="fa fa-ellipsis-v"></span>
</button>
<ul uib-dropdown-menu="" class="dropdown-menu dropdown-menu-right dropdown-menu-appended-to-body">
<li role="menuitem" ng-class="{'disabled': !$ctrl.customScope.actionEnabled('start', item)}">
<a title="{{'Start this service' | translate}}" ng-click="$ctrl.customScope.startService(null, item)" translate>Start</a>
<a title="{{'Start this service' | translate}}" ng-click="$ctrl.customScope.startService(null, item)"
translate>Start</a>
</li>
<li role="menuitem" ng-class="{'disabled': !$ctrl.customScope.actionEnabled('stop', item)}">
<a title="{{'Stop this service' | translate}}" ng-click="$ctrl.customScope.stopService(null, item)" translate>Stop</a>
<a title="{{'Stop this service' | translate}}" ng-click="$ctrl.customScope.stopService(null, item)"
translate>Stop</a>
</li>
<li role="menuitem" ng-class="{'disabled': !$ctrl.customScope.actionEnabled('suspend', item)}">
<a title="{{'suspend this service' | translate}}" ng-click="$ctrl.customScope.suspendService(null, item)" translate>Suspend</a>
<a title="{{'suspend this service' | translate}}" ng-click="$ctrl.customScope.suspendService(null, item)"
translate>Suspend</a>
</li>
</ul>
</div>
</pf-list-view>
</list-expanded-content>
</pf-list-view>
</div>
<explorer-pagination limit="vm.limit" count="vm.filterCount" limit-options="vm.limitOptions" on-update="vm.paginationUpdate($limit, $offset)">
<explorer-pagination limit="vm.limit" count="vm.filterCount" limit-options="vm.limitOptions"
on-update="vm.paginationUpdate($limit, $offset)">
</explorer-pagination>
12 changes: 5 additions & 7 deletions client/app/services/services-state.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function ServicesStateFactory(ListConfiguration, CollectionsApi, RBAC) {
return CollectionsApi.query('services', options);
}

function getServices(limit, offset, filters, sortField, sortAscending, refresh) {
function getServices(limit, offset, refresh) {
const options = {
expand: 'resources',
limit: limit,
Expand All @@ -87,15 +87,13 @@ export function ServicesStateFactory(ListConfiguration, CollectionsApi, RBAC) {
'service_resources',
'tags',
],
filter: getQueryFilters(filters),
filter: getQueryFilters(services.getFilters()),
auto_refresh: refresh,
};

if (angular.isDefined(sortField)) {
options.sort_by = services.getSort().currentField.id;
options.sort_options = services.getSort().currentField.sortType === 'alpha' ? 'ignore_case' : '';
options.sort_order = sortAscending ? 'asc' : 'desc';
}
options.sort_by = services.getSort().currentField.id || '';
options.sort_options = services.getSort().currentField.sortType === 'alpha' ? 'ignore_case' : '';
options.sort_order = services.getSort().isAscending ? 'asc' : 'desc';

return CollectionsApi.query('services', options);
}
Expand Down
Loading