mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 11:20:39 -03:30
refactor, lint, separate data transformation logic from display logic
This commit is contained in:
parent
c12173233b
commit
0adf671de4
@ -44,19 +44,16 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
||||
key = this.replaceDefaultFlags(key);
|
||||
|
||||
if (!Array.isArray(values)) {
|
||||
values = this.replaceEncodedTokens(values);
|
||||
|
||||
return `${key}=${values}`;
|
||||
values = [values];
|
||||
}
|
||||
|
||||
return values
|
||||
.map(value => {
|
||||
value = this.replaceDefaultFlags(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
|
||||
encodeQueryset(params) {
|
||||
@ -69,15 +66,33 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
||||
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
|
||||
// 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
|
||||
// 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 exclude = 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 valueString = paramParts[1];
|
||||
if(keySplit.length === 1) {
|
||||
if(params.searchTerm && !lessThanGreaterThan) {
|
||||
if(params.singleSearchParam) {
|
||||
if(searchTerm && !lessThanGreaterThan) {
|
||||
if(singleSearchParam) {
|
||||
paramString += keySplit[0] + '__icontains';
|
||||
}
|
||||
else {
|
||||
paramString += keySplit[0] + '__icontains_DEFAULT';
|
||||
}
|
||||
}
|
||||
else if(params.relatedSearchTerm) {
|
||||
if(params.singleSearchParam) {
|
||||
else if(relatedSearchTerm) {
|
||||
if(singleSearchParam) {
|
||||
paramString += keySplit[0];
|
||||
}
|
||||
else {
|
||||
@ -131,8 +146,8 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
||||
}
|
||||
}
|
||||
|
||||
if(params.singleSearchParam) {
|
||||
return {[params.singleSearchParam]: paramString + "=" + valueString};
|
||||
if(singleSearchParam) {
|
||||
return {[singleSearchParam]: paramString + "=" + valueString};
|
||||
}
|
||||
else {
|
||||
return {[paramString] : encodeURIComponent(valueString)};
|
||||
@ -189,7 +204,14 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
||||
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
|
||||
// {operand__key__comparator: value, } => 'operand:key:comparator:value;...'
|
||||
// value.isArray expands to:
|
||||
|
||||
@ -1,422 +1,422 @@
|
||||
export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', 'SmartSearchService', 'i18n', 'ConfigService', '$transitions',
|
||||
function($stateParams, $scope, $state, GetBasePath, qs, SmartSearchService, i18n, configService, $transitions) {
|
||||
function SmartSearchController (
|
||||
$scope,
|
||||
$state,
|
||||
$stateParams,
|
||||
$transitions,
|
||||
configService,
|
||||
GetBasePath,
|
||||
i18n,
|
||||
qs,
|
||||
SmartSearchService
|
||||
) {
|
||||
const searchKey = `${$scope.iterator}_search`;
|
||||
const optionsKey = `${$scope.list.iterator}_options`;
|
||||
|
||||
let path,
|
||||
defaults,
|
||||
queryset,
|
||||
transitionSuccessListener;
|
||||
let path;
|
||||
let defaults;
|
||||
let queryset;
|
||||
let transitionSuccessListener;
|
||||
|
||||
configService.getConfig()
|
||||
.then(config => init(config));
|
||||
configService.getConfig()
|
||||
.then(config => init(config));
|
||||
|
||||
function init(config) {
|
||||
let version;
|
||||
function init (config) {
|
||||
let version;
|
||||
|
||||
try {
|
||||
version = config.version.split('-')[0];
|
||||
} catch (err) {
|
||||
version = 'latest';
|
||||
}
|
||||
try {
|
||||
[version] = config.version.split('-');
|
||||
} catch (err) {
|
||||
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) {
|
||||
defaults = $scope.defaultParams;
|
||||
}
|
||||
else {
|
||||
// steps through the current tree of $state configurations, grabs default search params
|
||||
defaults = _.find($state.$current.path, (step) => {
|
||||
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.defaultParams) {
|
||||
defaults = $scope.defaultParams;
|
||||
} else {
|
||||
// steps through the current tree of $state configurations, grabs default search params
|
||||
const stateConfig = _.find($state.$current.path, step => _.has(step, `params.${searchKey}`));
|
||||
defaults = stateConfig.params[searchKey].config.value;
|
||||
}
|
||||
|
||||
if($scope.querySet) {
|
||||
queryset = _.cloneDeep($scope.querySet);
|
||||
}
|
||||
else {
|
||||
queryset = $state.params[`${$scope.iterator}_search`];
|
||||
}
|
||||
if ($scope.querySet) {
|
||||
queryset = _.cloneDeep($scope.querySet);
|
||||
} else {
|
||||
queryset = $state.params[searchKey];
|
||||
}
|
||||
|
||||
path = GetBasePath($scope.basePath) || $scope.basePath;
|
||||
generateSearchTags();
|
||||
qs.initFieldset(path, $scope.djangoModel).then((data) => {
|
||||
path = GetBasePath($scope.basePath) || $scope.basePath;
|
||||
generateSearchTags();
|
||||
|
||||
qs.initFieldset(path, $scope.djangoModel)
|
||||
.then((data) => {
|
||||
$scope.models = data.models;
|
||||
$scope.options = data.options.data;
|
||||
if ($scope.list) {
|
||||
$scope.$emit(`${$scope.list.iterator}_options`, 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.$emit(optionsKey, data.options);
|
||||
}
|
||||
});
|
||||
|
||||
$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(disableSearch) {
|
||||
$scope.searchPlaceholder = i18n._('Cannot search running job');
|
||||
}
|
||||
else {
|
||||
$scope.searchPlaceholder = i18n._('Search');
|
||||
if (transitionSuccessListener) {
|
||||
transitionSuccessListener();
|
||||
}
|
||||
|
||||
transitionSuccessListener = $transitions.onSuccess({}, 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')[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 = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults));
|
||||
}
|
||||
|
||||
let querysetCopy = angular.copy(queryset);
|
||||
|
||||
if($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) {
|
||||
let searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20');
|
||||
delete querysetCopy[$scope.singleSearchParam];
|
||||
|
||||
$.each(searchParam, function(index, param) {
|
||||
let paramParts = decodeURIComponent(param).split(/=(.+)/);
|
||||
let reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]);
|
||||
$scope.searchTags.push(reconstructedSearchString);
|
||||
});
|
||||
function revertSearch (queryToBeRestored) {
|
||||
queryset = queryToBeRestored;
|
||||
// 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('.', { [searchKey]: queryset });
|
||||
}
|
||||
qs.search(path, queryset).then((res) => {
|
||||
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) {
|
||||
queryset = queryToBeRestored;
|
||||
// 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 });
|
||||
const combineSameSearches = (a, b) => {
|
||||
if (!a) {
|
||||
return undefined;
|
||||
}
|
||||
qs.search(path, queryset).then((res) => {
|
||||
if($scope.querySet) {
|
||||
$scope.querySet = queryset;
|
||||
}
|
||||
$scope.dataset = res.data;
|
||||
$scope.collection = res.data.results;
|
||||
});
|
||||
|
||||
$scope.searchTerm = null;
|
||||
|
||||
generateSearchTags();
|
||||
}
|
||||
|
||||
function searchWithoutKey(term) {
|
||||
if($scope.singleSearchParam) {
|
||||
return {
|
||||
[$scope.singleSearchParam]: "search=" + encodeURIComponent(term)
|
||||
};
|
||||
if (_.isArray(a)) {
|
||||
return a.concat(b);
|
||||
}
|
||||
return {
|
||||
search: encodeURIComponent(term)
|
||||
};
|
||||
}
|
||||
|
||||
$scope.toggleKeyPane = function() {
|
||||
$scope.showKeyPane = !$scope.showKeyPane;
|
||||
if (singleSearchParam) {
|
||||
return `${a}%20and%20${b}`;
|
||||
}
|
||||
|
||||
return [a, b];
|
||||
};
|
||||
|
||||
// add a search tag, merge new queryset, $state.go()
|
||||
$scope.addTerm = function(terms) {
|
||||
let params = {},
|
||||
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];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
_.each(splitTerms, term => {
|
||||
const termParts = SmartSearchService.splitTermIntoParts(term);
|
||||
let termParams;
|
||||
|
||||
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 {
|
||||
let root = termParts[0].split(".")[0].replace(/^-/, '');
|
||||
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]);
|
||||
}
|
||||
termParams = qs.encodeParam({ term, singleSearchParam });
|
||||
}
|
||||
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`]);
|
||||
});
|
||||
}
|
||||
qs.search(path, queryset).then((res) => {
|
||||
if($scope.querySet) {
|
||||
$scope.querySet = queryset;
|
||||
}
|
||||
params = _.merge(params, termParams, combineSameSearches);
|
||||
});
|
||||
|
||||
$scope.dataset = res.data;
|
||||
$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);
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
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-searchTermContainer">
|
||||
<!-- 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}}"
|
||||
ng-disabled="disableSearch">
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user