mirror of
https://github.com/ansible/awx.git
synced 2026-02-18 03:30:02 -03:30
refactor, lint, separate data transformation logic from display logic
This commit is contained in:
@@ -44,19 +44,16 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
key = this.replaceDefaultFlags(key);
|
key = this.replaceDefaultFlags(key);
|
||||||
|
|
||||||
if (!Array.isArray(values)) {
|
if (!Array.isArray(values)) {
|
||||||
values = this.replaceEncodedTokens(values);
|
values = [values];
|
||||||
|
|
||||||
return `${key}=${values}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return values
|
return values
|
||||||
.map(value => {
|
.map(value => {
|
||||||
value = this.replaceDefaultFlags(value);
|
value = this.replaceDefaultFlags(value);
|
||||||
value = this.replaceEncodedTokens(value);
|
value = this.replaceEncodedTokens(value);
|
||||||
|
return [key, value]
|
||||||
return `${key}=${value}`;
|
|
||||||
})
|
})
|
||||||
.join('&');
|
|
||||||
},
|
},
|
||||||
// encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL
|
// encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL
|
||||||
encodeQueryset(params) {
|
encodeQueryset(params) {
|
||||||
@@ -69,15 +66,33 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
result += '&';
|
result += '&';
|
||||||
}
|
}
|
||||||
|
|
||||||
return result += this.encodeTerms(value, key);
|
const encodedTermString = this.encodeTerms(value, key)
|
||||||
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
|
return result += encodedTermString;
|
||||||
}, '?');
|
}, '?');
|
||||||
},
|
},
|
||||||
|
// like encodeQueryset, but return an actual unstringified API-consumable http param object
|
||||||
|
encodeQuerysetObject(params) {
|
||||||
|
return _.reduce(params, (obj, value, key) => {
|
||||||
|
const encodedTerms = this.encodeTerms(value, key);
|
||||||
|
|
||||||
|
for (let encodedIndex in encodedTerms) {
|
||||||
|
const [encodedKey, encodedValue] = encodedTerms[encodedIndex];
|
||||||
|
obj[encodedKey] = obj[encodedKey] || [];
|
||||||
|
obj[encodedKey].push(encodedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
// encodes a ui smart-search param to a django-friendly param
|
// encodes a ui smart-search param to a django-friendly param
|
||||||
// operand:key:comparator:value => {operand__key__comparator: value}
|
// operand:key:comparator:value => {operand__key__comparator: value}
|
||||||
encodeParam(params){
|
encodeParam({ term, relatedSearchTerm, searchTerm, singleSearchParam }){
|
||||||
// Assumption here is that we have a key and a value so the length
|
// Assumption here is that we have a key and a value so the length
|
||||||
// of the paramParts array will be 2. [0] is the key and [1] the value
|
// of the paramParts array will be 2. [0] is the key and [1] the value
|
||||||
let paramParts = SmartSearchService.splitTermIntoParts(params.term);
|
let paramParts = SmartSearchService.splitTermIntoParts(term);
|
||||||
let keySplit = paramParts[0].split('.');
|
let keySplit = paramParts[0].split('.');
|
||||||
let exclude = false;
|
let exclude = false;
|
||||||
let lessThanGreaterThan = paramParts[1].match(/^(>|<).*$/) ? true : false;
|
let lessThanGreaterThan = paramParts[1].match(/^(>|<).*$/) ? true : false;
|
||||||
@@ -88,16 +103,16 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
let paramString = exclude ? "not__" : "";
|
let paramString = exclude ? "not__" : "";
|
||||||
let valueString = paramParts[1];
|
let valueString = paramParts[1];
|
||||||
if(keySplit.length === 1) {
|
if(keySplit.length === 1) {
|
||||||
if(params.searchTerm && !lessThanGreaterThan) {
|
if(searchTerm && !lessThanGreaterThan) {
|
||||||
if(params.singleSearchParam) {
|
if(singleSearchParam) {
|
||||||
paramString += keySplit[0] + '__icontains';
|
paramString += keySplit[0] + '__icontains';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
paramString += keySplit[0] + '__icontains_DEFAULT';
|
paramString += keySplit[0] + '__icontains_DEFAULT';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(params.relatedSearchTerm) {
|
else if(relatedSearchTerm) {
|
||||||
if(params.singleSearchParam) {
|
if(singleSearchParam) {
|
||||||
paramString += keySplit[0];
|
paramString += keySplit[0];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -131,8 +146,8 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(params.singleSearchParam) {
|
if(singleSearchParam) {
|
||||||
return {[params.singleSearchParam]: paramString + "=" + valueString};
|
return {[singleSearchParam]: paramString + "=" + valueString};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return {[paramString] : encodeURIComponent(valueString)};
|
return {[paramString] : encodeURIComponent(valueString)};
|
||||||
@@ -189,7 +204,14 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
return decodeParamString(value);
|
return decodeParamString(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
convertToSearchTags(obj) {
|
||||||
|
const tags = [];
|
||||||
|
for (let key in obj) {
|
||||||
|
const value = obj[key];
|
||||||
|
tags.push(this.decodeParam(value, key));
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
},
|
||||||
// encodes a django queryset for ui-router's URLMatcherFactory
|
// encodes a django queryset for ui-router's URLMatcherFactory
|
||||||
// {operand__key__comparator: value, } => 'operand:key:comparator:value;...'
|
// {operand__key__comparator: value, } => 'operand:key:comparator:value;...'
|
||||||
// value.isArray expands to:
|
// value.isArray expands to:
|
||||||
|
|||||||
@@ -1,422 +1,422 @@
|
|||||||
export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', 'SmartSearchService', 'i18n', 'ConfigService', '$transitions',
|
function SmartSearchController (
|
||||||
function($stateParams, $scope, $state, GetBasePath, qs, SmartSearchService, i18n, configService, $transitions) {
|
$scope,
|
||||||
|
$state,
|
||||||
|
$stateParams,
|
||||||
|
$transitions,
|
||||||
|
configService,
|
||||||
|
GetBasePath,
|
||||||
|
i18n,
|
||||||
|
qs,
|
||||||
|
SmartSearchService
|
||||||
|
) {
|
||||||
|
const searchKey = `${$scope.iterator}_search`;
|
||||||
|
const optionsKey = `${$scope.list.iterator}_options`;
|
||||||
|
|
||||||
let path,
|
let path;
|
||||||
defaults,
|
let defaults;
|
||||||
queryset,
|
let queryset;
|
||||||
transitionSuccessListener;
|
let transitionSuccessListener;
|
||||||
|
|
||||||
configService.getConfig()
|
configService.getConfig()
|
||||||
.then(config => init(config));
|
.then(config => init(config));
|
||||||
|
|
||||||
function init(config) {
|
function init (config) {
|
||||||
let version;
|
let version;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
version = config.version.split('-')[0];
|
[version] = config.version.split('-');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
version = 'latest';
|
version = 'latest';
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.documentationLink = `http://docs.ansible.com/ansible-tower/${version}/html/userguide/search_sort.html`;
|
$scope.documentationLink = `http://docs.ansible.com/ansible-tower/${version}/html/userguide/search_sort.html`;
|
||||||
|
$scope.searchPlaceholder = i18n._('Search');
|
||||||
|
|
||||||
if($scope.defaultParams) {
|
if ($scope.defaultParams) {
|
||||||
defaults = $scope.defaultParams;
|
defaults = $scope.defaultParams;
|
||||||
}
|
} else {
|
||||||
else {
|
// steps through the current tree of $state configurations, grabs default search params
|
||||||
// steps through the current tree of $state configurations, grabs default search params
|
const stateConfig = _.find($state.$current.path, step => _.has(step, `params.${searchKey}`));
|
||||||
defaults = _.find($state.$current.path, (step) => {
|
defaults = stateConfig.params[searchKey].config.value;
|
||||||
if(step && step.params && step.params.hasOwnProperty(`${$scope.iterator}_search`)){
|
}
|
||||||
return step.params.hasOwnProperty(`${$scope.iterator}_search`);
|
|
||||||
}
|
|
||||||
}).params[`${$scope.iterator}_search`].config.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($scope.querySet) {
|
if ($scope.querySet) {
|
||||||
queryset = _.cloneDeep($scope.querySet);
|
queryset = _.cloneDeep($scope.querySet);
|
||||||
}
|
} else {
|
||||||
else {
|
queryset = $state.params[searchKey];
|
||||||
queryset = $state.params[`${$scope.iterator}_search`];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
path = GetBasePath($scope.basePath) || $scope.basePath;
|
path = GetBasePath($scope.basePath) || $scope.basePath;
|
||||||
generateSearchTags();
|
generateSearchTags();
|
||||||
qs.initFieldset(path, $scope.djangoModel).then((data) => {
|
|
||||||
|
qs.initFieldset(path, $scope.djangoModel)
|
||||||
|
.then((data) => {
|
||||||
$scope.models = data.models;
|
$scope.models = data.models;
|
||||||
$scope.options = data.options.data;
|
$scope.options = data.options.data;
|
||||||
if ($scope.list) {
|
if ($scope.list) {
|
||||||
$scope.$emit(`${$scope.list.iterator}_options`, data.options);
|
$scope.$emit(optionsKey, data.options);
|
||||||
}
|
|
||||||
});
|
|
||||||
$scope.searchPlaceholder = $scope.disableSearch ? i18n._('Cannot search running job') : i18n._('Search');
|
|
||||||
|
|
||||||
function compareParams(a, b) {
|
|
||||||
for (let key in a) {
|
|
||||||
if (!(key in b) || a[key].toString() !== b[key].toString()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let key in b) {
|
|
||||||
if (!(key in a)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(transitionSuccessListener) {
|
|
||||||
transitionSuccessListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionSuccessListener = $transitions.onSuccess({}, function(trans) {
|
|
||||||
// State has changed - check to see if this is a param change
|
|
||||||
if(trans.from().name === trans.to().name) {
|
|
||||||
if(!compareParams(trans.params('from')[`${$scope.iterator}_search`], trans.params('to')[`${$scope.iterator}_search`])) {
|
|
||||||
// Params are not the same - we need to update the search. This should only happen when the user
|
|
||||||
// hits the forward/back navigation buttons in their browser.
|
|
||||||
queryset = trans.params('to')[`${$scope.iterator}_search`];
|
|
||||||
qs.search(path, queryset).then((res) => {
|
|
||||||
$scope.dataset = res.data;
|
|
||||||
$scope.collection = res.data.results;
|
|
||||||
$scope.$emit('updateDataset', res.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.searchTerm = null;
|
|
||||||
generateSearchTags();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$on('$destroy', transitionSuccessListener);
|
function compareParams(a, b) {
|
||||||
|
for (let key in a) {
|
||||||
|
if (!(key in b) || a[key].toString() !== b[key].toString()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key in b) {
|
||||||
|
if (!(key in a)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.$watch('disableSearch', function(disableSearch){
|
if (transitionSuccessListener) {
|
||||||
if(disableSearch) {
|
transitionSuccessListener();
|
||||||
$scope.searchPlaceholder = i18n._('Cannot search running job');
|
}
|
||||||
}
|
|
||||||
else {
|
transitionSuccessListener = $transitions.onSuccess({}, trans => {
|
||||||
$scope.searchPlaceholder = i18n._('Search');
|
// State has changed - check to see if this is a param change
|
||||||
|
if (trans.from().name === trans.to().name) {
|
||||||
|
if (!compareParams(trans.params('from')[searchKey], trans.params('to')[searchKey])) {
|
||||||
|
// Params are not the same - we need to update the search. This should only
|
||||||
|
// happen when the user hits the forward/back browser navigation buttons.
|
||||||
|
queryset = trans.params('to')[searchKey];
|
||||||
|
qs.search(path, queryset).then((res) => {
|
||||||
|
$scope.dataset = res.data;
|
||||||
|
$scope.collection = res.data.results;
|
||||||
|
$scope.$emit('updateDataset', res.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.searchTerm = null;
|
||||||
|
generateSearchTags();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$on('$destroy', transitionSuccessListener);
|
||||||
|
$scope.$watch('disableSearch', disableSearch => {
|
||||||
|
if (disableSearch) {
|
||||||
|
$scope.searchPlaceholder = i18n._('Cannot search running job');
|
||||||
|
} else {
|
||||||
|
$scope.searchPlaceholder = i18n._('Search');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateSearchTags () {
|
||||||
|
$scope.searchTags = [];
|
||||||
|
|
||||||
|
const querysetCopy = angular.copy(queryset);
|
||||||
|
|
||||||
|
if ($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) {
|
||||||
|
const searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20');
|
||||||
|
delete querysetCopy[$scope.singleSearchParam];
|
||||||
|
|
||||||
|
$.each(searchParam, (index, param) => {
|
||||||
|
const paramParts = decodeURIComponent(param).split(/=(.+)/);
|
||||||
|
const reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]);
|
||||||
|
$scope.searchTags.push(reconstructedSearchString);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSearchTags() {
|
$scope.searchTags = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults));
|
||||||
$scope.searchTags = [];
|
}
|
||||||
|
|
||||||
let querysetCopy = angular.copy(queryset);
|
function revertSearch (queryToBeRestored) {
|
||||||
|
queryset = queryToBeRestored;
|
||||||
if($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) {
|
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
||||||
let searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20');
|
// This transition will not reload controllers/resolves/views
|
||||||
delete querysetCopy[$scope.singleSearchParam];
|
// but will register new $stateParams[$scope.iterator + '_search'] terms
|
||||||
|
if (!$scope.querySet) {
|
||||||
$.each(searchParam, function(index, param) {
|
$state.go('.', { [searchKey]: queryset });
|
||||||
let paramParts = decodeURIComponent(param).split(/=(.+)/);
|
}
|
||||||
let reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]);
|
qs.search(path, queryset).then((res) => {
|
||||||
$scope.searchTags.push(reconstructedSearchString);
|
if ($scope.querySet) {
|
||||||
});
|
$scope.querySet = queryset;
|
||||||
}
|
}
|
||||||
|
$scope.dataset = res.data;
|
||||||
|
$scope.collection = res.data.results;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.searchTags = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults));
|
$scope.searchTerm = null;
|
||||||
|
|
||||||
|
generateSearchTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.toggleKeyPane = () => {
|
||||||
|
$scope.showKeyPane = !$scope.showKeyPane;
|
||||||
|
};
|
||||||
|
|
||||||
|
function searchWithoutKey (term, singleSearchParam = null) {
|
||||||
|
if (singleSearchParam) {
|
||||||
|
return { [singleSearchParam]: `search=${encodeURIComponent(term)}` };
|
||||||
|
}
|
||||||
|
return { search: encodeURIComponent(term) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnsibleFactSearchTerm (termParts) {
|
||||||
|
const rootField = termParts[0].split('.')[0].replace(/^-/, '');
|
||||||
|
return rootField === 'ansible_facts';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRelatedField (termParts) {
|
||||||
|
const rootField = termParts[0].split('.')[0].replace(/^-/, '');
|
||||||
|
const listName = $scope.list.name;
|
||||||
|
const baseRelatedTypePath = `models.${listName}.base.${rootField}.type`;
|
||||||
|
|
||||||
|
const isRelatedSearchTermField = (_.contains($scope.models[listName].related, rootField));
|
||||||
|
const isBaseModelRelatedSearchTermField = (_.get($scope, baseRelatedTypePath) === 'field');
|
||||||
|
|
||||||
|
return (isRelatedSearchTermField || isBaseModelRelatedSearchTermField);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchInputQueryset ({ terms, singleSearchParam }) {
|
||||||
|
let params = {};
|
||||||
|
|
||||||
|
// remove leading/trailing whitespace
|
||||||
|
terms = (terms) ? terms.trim() : '';
|
||||||
|
let splitTerms;
|
||||||
|
|
||||||
|
if (singleSearchParam === 'host_filter') {
|
||||||
|
splitTerms = SmartSearchService.splitFilterIntoTerms(terms);
|
||||||
|
} else {
|
||||||
|
splitTerms = SmartSearchService.splitSearchIntoTerms(terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
function revertSearch(queryToBeRestored) {
|
const combineSameSearches = (a, b) => {
|
||||||
queryset = queryToBeRestored;
|
if (!a) {
|
||||||
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
return undefined;
|
||||||
// This transition will not reload controllers/resolves/views
|
|
||||||
// but will register new $stateParams[$scope.iterator + '_search'] terms
|
|
||||||
if(!$scope.querySet) {
|
|
||||||
$state.go('.', {
|
|
||||||
[$scope.iterator + '_search']: queryset });
|
|
||||||
}
|
}
|
||||||
qs.search(path, queryset).then((res) => {
|
|
||||||
if($scope.querySet) {
|
|
||||||
$scope.querySet = queryset;
|
|
||||||
}
|
|
||||||
$scope.dataset = res.data;
|
|
||||||
$scope.collection = res.data.results;
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.searchTerm = null;
|
if (_.isArray(a)) {
|
||||||
|
return a.concat(b);
|
||||||
generateSearchTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchWithoutKey(term) {
|
|
||||||
if($scope.singleSearchParam) {
|
|
||||||
return {
|
|
||||||
[$scope.singleSearchParam]: "search=" + encodeURIComponent(term)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
search: encodeURIComponent(term)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.toggleKeyPane = function() {
|
if (singleSearchParam) {
|
||||||
$scope.showKeyPane = !$scope.showKeyPane;
|
return `${a}%20and%20${b}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [a, b];
|
||||||
};
|
};
|
||||||
|
|
||||||
// add a search tag, merge new queryset, $state.go()
|
_.each(splitTerms, term => {
|
||||||
$scope.addTerm = function(terms) {
|
const termParts = SmartSearchService.splitTermIntoParts(term);
|
||||||
let params = {},
|
let termParams;
|
||||||
origQueryset = _.clone(queryset);
|
|
||||||
|
|
||||||
// Remove leading/trailing whitespace if there is any
|
|
||||||
terms = (terms) ? terms.trim() : "";
|
|
||||||
|
|
||||||
if(terms && terms !== '') {
|
|
||||||
let splitTerms;
|
|
||||||
|
|
||||||
if ($scope.singleSearchParam === 'host_filter') {
|
|
||||||
splitTerms = SmartSearchService.splitFilterIntoTerms(terms);
|
|
||||||
} else {
|
|
||||||
splitTerms = SmartSearchService.splitSearchIntoTerms(terms);
|
|
||||||
}
|
|
||||||
|
|
||||||
_.forEach(splitTerms, (term) => {
|
|
||||||
let termParts = SmartSearchService.splitTermIntoParts(term);
|
|
||||||
|
|
||||||
function combineSameSearches(a,b){
|
|
||||||
if (_.isArray(a)) {
|
|
||||||
return a.concat(b);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(a) {
|
|
||||||
if($scope.singleSearchParam) {
|
|
||||||
return a + "%20and%20" + b;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [a,b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($scope.singleSearchParam) {
|
|
||||||
if (termParts.length === 1) {
|
|
||||||
params = _.merge(params, searchWithoutKey(term), combineSameSearches);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let root = termParts[0].split(".")[0].replace(/^-/, '');
|
|
||||||
if(_.has($scope.models[$scope.list.name].base, root) || root === "ansible_facts") {
|
|
||||||
if(_.has($scope.models[$scope.list.name].base[root], "type") && $scope.models[$scope.list.name].base[root].type === 'field'){
|
|
||||||
// Intent is to land here for searching on the base model.
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Intent is to land here when performing ansible_facts searches
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, searchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(_.contains($scope.models[$scope.list.name].related, root)) {
|
|
||||||
// Intent is to land here for related searches
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches);
|
|
||||||
}
|
|
||||||
// Its not a search term or a related search term - treat it as a string
|
|
||||||
else {
|
|
||||||
params = _.merge(params, searchWithoutKey(term), combineSameSearches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
// if only a value is provided, search using default keys
|
|
||||||
if (termParts.length === 1) {
|
|
||||||
params = _.merge(params, searchWithoutKey(term), combineSameSearches);
|
|
||||||
} else {
|
|
||||||
// Figure out if this is a search term
|
|
||||||
let root = termParts[0].split(".")[0].replace(/^-/, '');
|
|
||||||
if(_.has($scope.models[$scope.list.name].base, root)) {
|
|
||||||
if($scope.models[$scope.list.name].base[root].type && $scope.models[$scope.list.name].base[root].type === 'field') {
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true}), combineSameSearches);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, searchTerm: true}), combineSameSearches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The related fields need to also be checked for related searches.
|
|
||||||
// The related fields for the search are retrieved from the API
|
|
||||||
// options endpoint, and are stored in the $scope.model. FYI, the
|
|
||||||
// Django search model is what sets the related fields on the model.
|
|
||||||
else if(_.contains($scope.models[$scope.list.name].related, root)) {
|
|
||||||
params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true}), combineSameSearches);
|
|
||||||
}
|
|
||||||
// Its not a search term or a related search term - treat it as a string
|
|
||||||
else {
|
|
||||||
params = _.merge(params, searchWithoutKey(term), combineSameSearches);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
queryset = _.merge({}, queryset, params, (objectValue, sourceValue, key, object) => {
|
|
||||||
if (object[key] && object[key] !== sourceValue){
|
|
||||||
if(_.isArray(object[key])) {
|
|
||||||
// Add the new value to the array and return
|
|
||||||
object[key].push(sourceValue);
|
|
||||||
return object[key];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if($scope.singleSearchParam) {
|
|
||||||
if(!object[key]) {
|
|
||||||
return sourceValue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let singleSearchParamKeys = object[key].split("%20and%20");
|
|
||||||
|
|
||||||
if(_.includes(singleSearchParamKeys, sourceValue)) {
|
|
||||||
return object[key];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return object[key] + "%20and%20" + sourceValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Start the array of keys
|
|
||||||
return [object[key], sourceValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// // https://lodash.com/docs/3.10.1#merge
|
|
||||||
// If customizer fn returns undefined merging is handled by default _.merge algorithm
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Go back to the first page after a new search
|
|
||||||
delete queryset.page;
|
|
||||||
|
|
||||||
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
|
||||||
// This transition will not reload controllers/resolves/views
|
|
||||||
// but will register new $stateParams[$scope.iterator + '_search'] terms
|
|
||||||
if(!$scope.querySet) {
|
|
||||||
$state.go('.', {[$scope.iterator + '_search']:queryset }).then(function(){
|
|
||||||
// ISSUE: same as above in $scope.remove. For some reason deleting the page
|
|
||||||
// from the queryset works for all lists except lists in modals.
|
|
||||||
delete $stateParams[$scope.iterator + '_search'].page;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
qs.search(path, queryset).then((res) => {
|
|
||||||
if($scope.querySet) {
|
|
||||||
$scope.querySet = queryset;
|
|
||||||
}
|
|
||||||
$scope.dataset = res.data;
|
|
||||||
$scope.collection = res.data.results;
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
revertSearch(origQueryset);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.searchTerm = null;
|
|
||||||
|
|
||||||
generateSearchTags();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove tag, merge new queryset, $state.go
|
|
||||||
$scope.removeTerm = function(index) {
|
|
||||||
let tagToRemove = $scope.searchTags.splice(index, 1)[0],
|
|
||||||
termParts = SmartSearchService.splitTermIntoParts(tagToRemove),
|
|
||||||
removed;
|
|
||||||
|
|
||||||
let removeFromQuerySet = function(set) {
|
|
||||||
_.each(removed, (value, key) => {
|
|
||||||
if (Array.isArray(set[key])){
|
|
||||||
_.remove(set[key], (item) => item === value);
|
|
||||||
// If the array is now empty, remove that key
|
|
||||||
if (set[key].length === 0) {
|
|
||||||
delete set[key];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($scope.singleSearchParam && set[$scope.singleSearchParam] && set[$scope.singleSearchParam].includes("%20and%20")) {
|
|
||||||
let searchParamParts = set[$scope.singleSearchParam].split("%20and%20");
|
|
||||||
// The value side of each paramPart might have been encoded in SmartSearch.splitFilterIntoTerms
|
|
||||||
_.each(searchParamParts, (paramPart, paramPartIndex) => {
|
|
||||||
searchParamParts[paramPartIndex] = decodeURIComponent(paramPart);
|
|
||||||
});
|
|
||||||
var index = searchParamParts.indexOf(value);
|
|
||||||
if (index !== -1) {
|
|
||||||
searchParamParts.splice(index, 1);
|
|
||||||
}
|
|
||||||
set[$scope.singleSearchParam] = searchParamParts.join("%20and%20");
|
|
||||||
} else {
|
|
||||||
delete set[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (termParts.length === 1) {
|
if (termParts.length === 1) {
|
||||||
removed = searchWithoutKey(tagToRemove);
|
termParams = searchWithoutKey(term, singleSearchParam);
|
||||||
|
} else if (isAnsibleFactSearchTerm(termParts)) {
|
||||||
|
termParams = qs.encodeParam({ term, singleSearchParam });
|
||||||
|
} else if (isRelatedField(termParts)) {
|
||||||
|
termParams = qs.encodeParam({ term, singleSearchParam, related: true });
|
||||||
} else {
|
} else {
|
||||||
let root = termParts[0].split(".")[0].replace(/^-/, '');
|
termParams = qs.encodeParam({ term, singleSearchParam });
|
||||||
let encodeParams = {
|
|
||||||
term: tagToRemove,
|
|
||||||
singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false
|
|
||||||
};
|
|
||||||
if($scope.models[$scope.list.name]) {
|
|
||||||
if($scope.singleSearchParam) {
|
|
||||||
removed = qs.encodeParam(encodeParams);
|
|
||||||
}
|
|
||||||
else if(_.has($scope.models[$scope.list.name].base, root)) {
|
|
||||||
if($scope.models[$scope.list.name].base[root].type && $scope.models[$scope.list.name].base[root].type === 'field') {
|
|
||||||
encodeParams.relatedSearchTerm = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
encodeParams.searchTerm = true;
|
|
||||||
}
|
|
||||||
removed = qs.encodeParam(encodeParams);
|
|
||||||
}
|
|
||||||
else if(_.contains($scope.models[$scope.list.name].related, root)) {
|
|
||||||
encodeParams.relatedSearchTerm = true;
|
|
||||||
removed = qs.encodeParam(encodeParams);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
removed = searchWithoutKey(termParts[termParts.length-1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
removed = searchWithoutKey(termParts[termParts.length-1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
removeFromQuerySet(queryset);
|
|
||||||
if (!$scope.querySet) {
|
|
||||||
$state.go('.', {
|
|
||||||
[$scope.iterator + '_search']: queryset }).then(function(){
|
|
||||||
// ISSUE: for some reason deleting a tag from a list in a modal does not
|
|
||||||
// remove the param from $stateParams. Here we'll manually check to make sure
|
|
||||||
// that that happened and remove it if it didn't.
|
|
||||||
|
|
||||||
removeFromQuerySet($stateParams[`${$scope.iterator}_search`]);
|
params = _.merge(params, termParams, combineSameSearches);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
qs.search(path, queryset).then((res) => {
|
|
||||||
if($scope.querySet) {
|
|
||||||
$scope.querySet = queryset;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.dataset = res.data;
|
return params;
|
||||||
$scope.collection = res.data.results;
|
|
||||||
|
|
||||||
generateSearchTags();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.clearAllTerms = function(){
|
|
||||||
let cleared = _.cloneDeep(defaults);
|
|
||||||
delete cleared.page;
|
|
||||||
queryset = cleared;
|
|
||||||
if(!$scope.querySet) {
|
|
||||||
$state.go('.', {[$scope.iterator + '_search']: queryset});
|
|
||||||
}
|
|
||||||
qs.search(path, queryset).then((res) => {
|
|
||||||
if($scope.querySet) {
|
|
||||||
$scope.querySet = queryset;
|
|
||||||
}
|
|
||||||
$scope.dataset = res.data;
|
|
||||||
$scope.collection = res.data.results;
|
|
||||||
});
|
|
||||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergeQueryset (qset, additional, singleSearchParam) {
|
||||||
|
const merged = _.merge({}, qset, additional, (objectValue, sourceValue, key, object) => {
|
||||||
|
if (!(object[key] && object[key] !== sourceValue)) {
|
||||||
|
// // https://lodash.com/docs/3.10.1#each
|
||||||
|
// If this returns undefined merging is handled by default _.merge algorithm
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isArray(object[key])) {
|
||||||
|
object[key].push(sourceValue);
|
||||||
|
return object[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (singleSearchParam) {
|
||||||
|
if (!object[key]) {
|
||||||
|
return sourceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleSearchParamKeys = object[key].split('%20and%20');
|
||||||
|
|
||||||
|
if (_.includes(singleSearchParamKeys, sourceValue)) {
|
||||||
|
return object[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${object[key]}%20and%20${sourceValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the array of keys
|
||||||
|
return [object[key], sourceValue];
|
||||||
|
});
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.addTerms = terms => {
|
||||||
|
const { singleSearchParam } = $scope;
|
||||||
|
const origQueryset = _.clone(queryset);
|
||||||
|
|
||||||
|
// Remove leading/trailing whitespace if there is any
|
||||||
|
terms = (terms) ? terms.trim() : '';
|
||||||
|
|
||||||
|
if (!(terms && terms !== '')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchInputQueryset = getSearchInputQueryset({ terms, singleSearchParam });
|
||||||
|
queryset = mergeQueryset(queryset, searchInputQueryset, singleSearchParam);
|
||||||
|
|
||||||
|
// Go back to the first page after a new search
|
||||||
|
delete queryset.page;
|
||||||
|
|
||||||
|
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
||||||
|
// This transition will not reload controllers/resolves/views but will register new
|
||||||
|
// $stateParams[searchKey] terms.
|
||||||
|
if (!$scope.querySet) {
|
||||||
|
$state.go('.', { [searchKey]: queryset })
|
||||||
|
.then(() => {
|
||||||
|
// same as above in $scope.remove. For some reason deleting the page
|
||||||
|
// from the queryset works for all lists except lists in modals.
|
||||||
|
delete $stateParams[searchKey].page;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
qs.search(path, queryset)
|
||||||
|
.then(({ data }) => {
|
||||||
|
if ($scope.querySet) {
|
||||||
|
$scope.querySet = queryset;
|
||||||
|
}
|
||||||
|
$scope.dataset = data;
|
||||||
|
$scope.collection = data.results;
|
||||||
|
})
|
||||||
|
.catch(() => revertSearch(origQueryset));
|
||||||
|
|
||||||
|
$scope.searchTerm = null;
|
||||||
|
|
||||||
|
generateSearchTags();
|
||||||
|
};
|
||||||
|
|
||||||
|
function removeTermsFromQueryset(qset, term, singleSearchParam = null) {
|
||||||
|
const returnedQueryset = _.cloneDeep(qset);
|
||||||
|
|
||||||
|
const removeSingleTermFromQueryset = (value, key) => {
|
||||||
|
const space = '%20and%20';
|
||||||
|
|
||||||
|
if (Array.isArray(returnedQueryset[key])) {
|
||||||
|
returnedQueryset[key] = returnedQueryset[key].filter(item => item !== value);
|
||||||
|
if (returnedQueryset[key].length < 1) {
|
||||||
|
delete returnedQueryset[key];
|
||||||
|
}
|
||||||
|
} else if (singleSearchParam && _.get(returnedQueryset, singleSearchParam, []).includes(space)) {
|
||||||
|
const searchParamParts = returnedQueryset[singleSearchParam].split(space);
|
||||||
|
// The value side of each paramPart might have been encoded in
|
||||||
|
// SmartSearch.splitFilterIntoTerms
|
||||||
|
_.each(searchParamParts, (paramPart, paramPartIndex) => {
|
||||||
|
searchParamParts[paramPartIndex] = decodeURIComponent(paramPart);
|
||||||
|
});
|
||||||
|
|
||||||
|
const paramPartIndex = searchParamParts.indexOf(value);
|
||||||
|
|
||||||
|
if (paramPartIndex !== -1) {
|
||||||
|
searchParamParts.splice(paramPartIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnedQueryset[singleSearchParam] = searchParamParts.join(space);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
delete returnedQueryset[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const termParts = SmartSearchService.splitTermIntoParts(term);
|
||||||
|
|
||||||
|
let removed;
|
||||||
|
|
||||||
|
if (termParts.length === 1) {
|
||||||
|
removed = searchWithoutKey(term, singleSearchParam);
|
||||||
|
} else if (isRelatedField(termParts)) {
|
||||||
|
removed = qs.encodeParam({ term, singleSearchParam, related: true });
|
||||||
|
} else {
|
||||||
|
removed = qs.encodeParam({ term, singleSearchParam });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!removed) {
|
||||||
|
removed = searchWithoutKey(termParts[termParts.length - 1], singleSearchParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(removed, removeSingleTermFromQueryset);
|
||||||
|
|
||||||
|
return returnedQueryset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove tag, merge new queryset, $state.go
|
||||||
|
$scope.removeTerm = index => {
|
||||||
|
const { singleSearchParam } = $scope;
|
||||||
|
const [term] = $scope.searchTags.splice(index, 1);
|
||||||
|
|
||||||
|
const modifiedQueryset = removeTermsFromQueryset(queryset, term, singleSearchParam);
|
||||||
|
|
||||||
|
if (!$scope.querySet) {
|
||||||
|
$state.go('.', { [searchKey]: modifiedQueryset })
|
||||||
|
.then(() => {
|
||||||
|
// for some reason deleting a tag from a list in a modal does not
|
||||||
|
// remove the param from $stateParams. Here we'll manually check to make sure
|
||||||
|
// that that happened and remove it if it didn't.
|
||||||
|
const clearedParams = removeTermsFromQueryset(
|
||||||
|
$stateParams[searchKey], term, singleSearchParam);
|
||||||
|
$stateParams[searchKey] = clearedParams;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
qs.search(path, queryset)
|
||||||
|
.then(({ data }) => {
|
||||||
|
if ($scope.querySet) {
|
||||||
|
$scope.querySet = queryset;
|
||||||
|
}
|
||||||
|
$scope.dataset = data;
|
||||||
|
$scope.collection = data.results;
|
||||||
|
});
|
||||||
|
|
||||||
|
generateSearchTags();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.clearAllTerms = () => {
|
||||||
|
const cleared = _.cloneDeep(defaults);
|
||||||
|
|
||||||
|
delete cleared.page;
|
||||||
|
|
||||||
|
queryset = cleared;
|
||||||
|
|
||||||
|
if (!$scope.querySet) {
|
||||||
|
$state.go('.', { [searchKey]: queryset });
|
||||||
|
}
|
||||||
|
|
||||||
|
qs.search(path, queryset)
|
||||||
|
.then(({ data }) => {
|
||||||
|
if ($scope.querySet) {
|
||||||
|
$scope.querySet = queryset;
|
||||||
|
}
|
||||||
|
$scope.dataset = data;
|
||||||
|
$scope.collection = data.results;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartSearchController.$inject = [
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
'$stateParams',
|
||||||
|
'$transitions',
|
||||||
|
'ConfigService',
|
||||||
|
'GetBasePath',
|
||||||
|
'i18n',
|
||||||
|
'QuerySet',
|
||||||
|
'SmartSearchService',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export default SmartSearchController;
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<div class="SmartSearch-bar" ng-class="{'SmartSearch-bar--fullWidth': searchBarFullWidth}">
|
<div class="SmartSearch-bar" ng-class="{'SmartSearch-bar--fullWidth': searchBarFullWidth}">
|
||||||
<div class="SmartSearch-searchTermContainer">
|
<div class="SmartSearch-searchTermContainer">
|
||||||
<!-- string search input -->
|
<!-- string search input -->
|
||||||
<form name="smartSearch" class="SmartSearch-form" aw-enter-key="addTerm(searchTerm)" novalidate>
|
<form name="smartSearch" class="SmartSearch-form" aw-enter-key="addTerms(searchTerm)" novalidate>
|
||||||
<input class="SmartSearch-input" ng-model="searchTerm" placeholder="{{searchPlaceholder}}"
|
<input class="SmartSearch-input" ng-model="searchTerm" placeholder="{{searchPlaceholder}}"
|
||||||
ng-disabled="disableSearch">
|
ng-disabled="disableSearch">
|
||||||
</form>
|
</form>
|
||||||
<div type="submit" class="SmartSearch-searchButton" ng-disabled="!searchTerm" ng-click="addTerm(searchTerm)">
|
<div type="submit" class="SmartSearch-searchButton" ng-disabled="!searchTerm" ng-click="addTerms(searchTerm)">
|
||||||
<i class="fa fa-search"></i>
|
<i class="fa fa-search"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user