| /******************************************************************************** |
| * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| |
| import {Component, ViewChild, OnInit, OnDestroy} from '@angular/core'; |
| |
| import {SearchService, SearchDefinition, SearchAttribute, SearchLayout} from './search.service'; |
| import {of as observableOf, combineLatest as observableCombineLatest} from 'rxjs'; |
| |
| import {defaultIfEmpty, mergeMap, tap} from 'rxjs/operators'; |
| |
| import {FilterService, SearchFilter, Condition} from './filter.service'; |
| import {NodeService} from '../navigator/node.service'; |
| import {BasketService} from '../basket/basket.service'; |
| import {QueryService, SearchResult } from '../tableview/query.service'; |
| |
| import {LocalizationService} from '../localization/localization.service'; |
| import {MDMNotificationService} from '../core/mdm-notification.service'; |
| |
| import {Node} from '../navigator/node'; |
| |
| import {TableviewComponent} from '../tableview/tableview.component'; |
| import {ViewComponent} from '../tableview/view.component'; |
| |
| import {ModalDirective} from 'ngx-bootstrap'; |
| import {AccordionPanelComponent} from 'ngx-bootstrap/accordion'; |
| |
| import {MenuItem} from 'primeng/primeng'; |
| import {EditSearchFieldsComponent} from './edit-searchFields.component'; |
| import {OverwriteDialogComponent} from '../core/overwrite-dialog.component'; |
| |
| import {classToClass, serialize, deserialize} from 'class-transformer'; |
| import {SelectItem} from 'primeng/primeng'; |
| |
| import { TranslateService } from '@ngx-translate/core'; |
| import { streamTranslate, TRANSLATE } from '../core/mdm-core.module'; |
| |
| @Component({ |
| selector: 'mdm-search', |
| templateUrl: 'mdm-search.component.html', |
| }) |
| export class MDMSearchComponent implements OnInit, OnDestroy { |
| |
| maxResults = 1000; |
| |
| filters: SearchFilter[] = []; |
| currentFilter: SearchFilter; |
| filterName = ''; |
| |
| environments: Node[]; |
| selectedEnvironments: Node[] = []; |
| |
| definitions: SearchDefinition[] = []; |
| |
| results: SearchResult = new SearchResult(); |
| allSearchAttributes: { [type: string]: { [env: string]: SearchAttribute[] }} = {}; |
| allSearchAttributesForCurrentResultType: { [env: string]: SearchAttribute[] } = {}; |
| |
| isAdvancedSearchOpen = false; |
| isAdvancedSearchActive = true; |
| isSearchResultsOpen = false; |
| |
| layout: SearchLayout = new SearchLayout; |
| |
| public dropdownModel: SelectItem[] = []; |
| public selectedEnvs: string[] = []; |
| |
| searchFields: { group: string, attribute: string }[] = []; |
| |
| subscription: any; |
| searchExecuted = false; |
| |
| selectedRow: SearchFilter; |
| lazySelectedRow: SearchFilter; |
| loading = false; |
| |
| contextMenuItems: MenuItem[] = [ |
| { label: 'Add to shopping basket', icon: 'fa fa-shopping-cart', command: (event) => this.addSelectionToBasket() } |
| ]; |
| |
| @ViewChild(ViewComponent) |
| viewComponent: ViewComponent; |
| |
| @ViewChild(TableviewComponent) |
| tableViewComponent: TableviewComponent; |
| |
| @ViewChild('lgSaveModal') |
| childSaveModal: ModalDirective; |
| |
| @ViewChild(EditSearchFieldsComponent) |
| editSearchFieldsComponent: EditSearchFieldsComponent; |
| |
| @ViewChild(OverwriteDialogComponent) |
| overwriteDialogComponent: OverwriteDialogComponent; |
| |
| @ViewChild('advancedSearch') |
| advancedSearchPanel: AccordionPanelComponent; |
| |
| @ViewChild('searchResults') |
| searchResultsPanel: AccordionPanelComponent; |
| |
| constructor(private searchService: SearchService, |
| private queryService: QueryService, |
| private filterService: FilterService, |
| private nodeService: NodeService, |
| private localService: LocalizationService, |
| private notificationService: MDMNotificationService, |
| private basketService: BasketService, |
| private translateService: TranslateService) { } |
| |
| ngOnInit() { |
| this.currentFilter = this.filterService.EMPTY_FILTER; |
| |
| streamTranslate(this.translateService, TRANSLATE('search.mdm-search.add-to-shopping-basket')).subscribe( |
| (msg: string) => this.contextMenuItems[0].label = msg); |
| |
| this.nodeService.getNodes().pipe( |
| mergeMap(envs => observableCombineLatest([ |
| observableOf(envs), |
| this.searchService.loadSearchAttributesStructured(envs.map(env => env.sourceName)), |
| this.filterService.getFilters().pipe(defaultIfEmpty([this.currentFilter])), |
| this.searchService.getDefinitionsSimple() |
| ]))).subscribe( |
| ([envs, attrs, filters, definitions]) => this.init(envs, attrs, filters, definitions), |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-load-data-sources'), error) |
| ); |
| |
| // event handlers |
| this.viewComponent.viewChanged$.subscribe( |
| () => this.onViewChanged(), |
| error => this.notificationService.notifyError(this.translateService.instant('search.mdm-search.err-cannot-update-view'), error) |
| ); |
| } |
| |
| ngOnDestroy() { |
| this.saveState(); |
| } |
| |
| init(envs: Node[], attrs: { [type: string]: { [env: string]: SearchAttribute[] }}, |
| filters: SearchFilter[], definitions: SearchDefinition[]) { |
| this.environments = envs |
| this.allSearchAttributes = attrs; |
| this.filters = filters; |
| this.definitions = definitions; |
| |
| this.dropdownModel = envs.map(env => <SelectItem>{ value: env.sourceName, label: env.name }); |
| this.selectedEnvs = envs.map(env => env.sourceName); |
| |
| this.updateSearchAttributesForCurrentResultType(); |
| this.selectedEnvironmentsChanged(); |
| |
| this.loadState(); |
| } |
| |
| loadState() { |
| |
| this.results = deserialize(SearchResult, sessionStorage.getItem('mdm-search.searchResult')) || new SearchResult(); |
| this.selectFilter(deserialize(SearchFilter, sessionStorage.getItem('mdm-search.currentFilter')) || this.filterService.EMPTY_FILTER); |
| this.isAdvancedSearchActive = !('false' === sessionStorage.getItem('mdm-search.isAdvancedSearchActive')); |
| this.advancedSearchPanel.isOpen = !('false' === sessionStorage.getItem('mdm-search.isAdvancedSearchOpen')); |
| this.searchResultsPanel.isOpen = !('false' === sessionStorage.getItem('mdm-search.isSearchResultsOpen')); |
| } |
| |
| saveState() { |
| sessionStorage.setItem('mdm-search.isSearchResultsOpen', serialize(this.searchResultsPanel.isOpen)); |
| sessionStorage.setItem('mdm-search.isAdvancedSearchOpen', serialize(this.advancedSearchPanel.isOpen)); |
| sessionStorage.setItem('mdm-search.currentFilter', serialize(this.currentFilter)); |
| sessionStorage.setItem('mdm-search.searchResult', serialize(this.results)); |
| sessionStorage.setItem('mdm-search.isAdvancedSearchActive', this.isAdvancedSearchActive.toString()); |
| } |
| |
| onViewClick(e: Event) { |
| e.stopPropagation(); |
| } |
| |
| onCheckBoxClick(event: any) { |
| event.stopPropagation(); |
| this.isAdvancedSearchActive = event.target.checked; |
| } |
| |
| onViewChanged() { |
| if (this.searchExecuted) { |
| this.onSearch(); |
| } |
| } |
| |
| selectedEnvironmentsChanged() { |
| this.currentFilter.environments = this.selectedEnvs; |
| if (this.environments) { |
| let envs = this.environments.filter(env => |
| this.currentFilter.environments.find(envName => envName === env.sourceName)); |
| |
| if (envs.length === 0) { |
| this.selectedEnvironments = this.environments; |
| } else { |
| this.selectedEnvironments = envs; |
| } |
| } |
| this.calcCurrentSearch(); |
| } |
| |
| loadFilters() { |
| this.filters = []; |
| this.filterService.getFilters().pipe( |
| defaultIfEmpty([this.currentFilter])) |
| .subscribe( |
| filters => this.filters = this.filters.concat(filters), |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-load-search-filter'), error) |
| ); |
| } |
| |
| selectFilterByName(defaultFilterName: string) { |
| this.selectFilter(this.filters.find(f => f.name === defaultFilterName)); |
| } |
| |
| removeSearchField(searchField: { group: string, attribute: string }) { |
| let index = this.searchFields.findIndex(sf => sf.group === searchField.group && sf.attribute === searchField.attribute); |
| this.searchFields.splice(index, 1); |
| } |
| |
| onResultTypeChane(e: any) { |
| this.selectResultType(e.value); |
| } |
| |
| selectResultType(type: any) { |
| this.currentFilter.resultType = type.type; |
| this.updateSearchAttributesForCurrentResultType(); |
| } |
| |
| updateSearchAttributesForCurrentResultType() { |
| if (this.allSearchAttributes.hasOwnProperty(this.getSelectedDefinition())) { |
| this.allSearchAttributesForCurrentResultType = this.allSearchAttributes[this.getSelectedDefinition()]; |
| } |
| } |
| |
| getSearchDefinition(type: string) { |
| return this.definitions.find(def => def.type === type); |
| } |
| |
| getSelectedDefinition() { |
| let def = this.getSearchDefinition(this.currentFilter.resultType); |
| if (def) { |
| return def.value; |
| } |
| } |
| |
| onSearch() { |
| let query; |
| this.loading = true; |
| this.isSearchResultsOpen = true; |
| if (this.isAdvancedSearchActive) { |
| query = this.searchService.convertToQuery(this.currentFilter, this.allSearchAttributes, this.viewComponent.selectedView); |
| } else { |
| let filter = classToClass(this.currentFilter); |
| filter.conditions = []; |
| query = this.searchService.convertToQuery(filter, this.allSearchAttributes, this.viewComponent.selectedView); |
| } |
| this.queryService.query(query).pipe( |
| tap(result => this.generateWarningsIfMaxResultsReached(result))) |
| .subscribe( |
| result => { |
| this.results = result; |
| this.isSearchResultsOpen = true; |
| this.searchExecuted = true; |
| this.loading = false; |
| }, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-process-search-query'), error) |
| ); |
| } |
| |
| generateWarningsIfMaxResultsReached(result: SearchResult) { |
| let resultsPerSource = result.rows |
| .map(r => r.source) |
| .reduce((prev, item) => { (prev[item]) ? prev[item] += 1 : prev[item] = 1; return prev; }, {}); |
| |
| Object.keys(resultsPerSource) |
| .filter(source => resultsPerSource[source] > this.maxResults) |
| .forEach(source => this.notificationService.notifyWarn( |
| this.translateService.instant('search.mdm-search.errheading-too-many-search-results'), |
| this.translateService.instant('search.mdm-search.err-too-many-search-results', {'numresults': this.maxResults, 'source': source}))); |
| } |
| |
| calcCurrentSearch() { |
| let environments = this.currentFilter.environments; |
| let conditions = this.currentFilter.conditions; |
| let type = this.getSelectedDefinition(); |
| this.layout = SearchLayout.createSearchLayout(environments, this.allSearchAttributesForCurrentResultType, conditions); |
| } |
| |
| onFilterChange(e: any) { |
| this.selectFilter(e.value); |
| } |
| |
| selectFilter(filter: SearchFilter) { |
| this.currentFilter = classToClass(filter); |
| this.selectedEnvs = this.currentFilter.environments; |
| this.updateSearchAttributesForCurrentResultType(); |
| this.selectedEnvironmentsChanged(); |
| this.calcCurrentSearch(); |
| } |
| |
| resetConditions(e: Event) { |
| e.stopPropagation(); |
| this.currentFilter.conditions.forEach(cond => cond.value = []); |
| this.selectFilter(this.currentFilter); |
| } |
| |
| clearResultlist(e: Event) { |
| e.stopPropagation(); |
| this.results = new SearchResult(); |
| } |
| |
| deleteFilter(e: Event) { |
| e.stopPropagation(); |
| if (this.currentFilter.name === this.filterService.NO_FILTER_NAME |
| || this.currentFilter.name === this.filterService.NEW_FILTER_NAME) { |
| this.notificationService |
| .notifyInfo(this.translateService.instant('search.mdm-search.errheading-cannot-delete-search-filter-none-selected'), |
| this.translateService.instant('search.mdm-search.err-cannot-delete-search-filter-none-selected')); |
| } else { |
| this.layout = new SearchLayout; |
| this.filterService.deleteFilter(this.currentFilter.name).subscribe( |
| () => { |
| this.loadFilters(); |
| this.selectFilter(new SearchFilter(this.filterService.NO_FILTER_NAME, [], 'Test', '', [])); |
| }, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-delete-search-filter'), error) |
| ); |
| } |
| } |
| |
| saveFilter(e: Event) { |
| e.stopPropagation(); |
| if (this.filters.find(f => f.name === this.filterName) != undefined) { |
| this.childSaveModal.hide(); |
| this.overwriteDialogComponent.showOverwriteModal(this.translateService.instant('search.mdm-search.a-filter')).subscribe( |
| needSave => this.saveFilter2(needSave), |
| error => { |
| this.saveFilter2(false); |
| this.notificationService.notifyError(this.translateService.instant('search.mdm-search.err-cannot-save-search-filter'), error); |
| } |
| ); |
| } else { |
| this.saveFilter2(true); |
| } |
| } |
| |
| saveFilter2(save: boolean) { |
| if (save) { |
| let filter = this.currentFilter; |
| filter.name = this.filterName; |
| this.filterService.saveFilter(filter).subscribe( |
| () => { |
| this.loadFilters(); |
| this.selectFilter(filter); |
| }, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-save-search-filter'), error) |
| ); |
| this.childSaveModal.hide(); |
| } else { |
| this.childSaveModal.show(); |
| } |
| } |
| |
| removeCondition(condition: Condition) { |
| this.currentFilter.conditions = this.currentFilter.conditions |
| .filter(c => !(c.type === condition.type && c.attribute === condition.attribute)); |
| |
| this.calcCurrentSearch(); |
| } |
| |
| selected2Basket(e: Event) { |
| e.stopPropagation(); |
| this.tableViewComponent.selectedViewRows.forEach(row => this.basketService.add(row.getItem())); |
| } |
| |
| showSaveModal(e: Event) { |
| e.stopPropagation(); |
| if (this.currentFilter.name === this.filterService.NO_FILTER_NAME |
| || this.currentFilter.name === this.filterService.NEW_FILTER_NAME) { |
| this.filterName = ''; |
| } else { |
| this.filterName = this.currentFilter.name; |
| } |
| this.childSaveModal.show(); |
| } |
| |
| showSearchFieldsEditor(e: Event, conditions?: Condition[]) { |
| e.stopPropagation(); |
| this.editSearchFieldsComponent.show(conditions).subscribe( |
| conds => { |
| if (!conditions) { |
| let filter = new SearchFilter(this.filterService.NEW_FILTER_NAME, this.currentFilter.environments, 'Test', '', conds); |
| this.selectFilter(filter); |
| } |
| this.currentFilter.conditions = conds; |
| this.calcCurrentSearch(); |
| }, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-display-search-field-editor'), error) |
| ); |
| } |
| |
| addSelectionToBasket() { |
| this.basketService.add(this.tableViewComponent.menuSelectedRow.getItem()); |
| } |
| |
| mapSourceNameToName(sourceName: string) { |
| return NodeService.mapSourceNameToName(this.environments, sourceName); |
| } |
| |
| getSaveFilterBtnTitle () { |
| return this.filterName |
| ? TRANSLATE('search.mdm-search.tooltip-save-search-filter') |
| : TRANSLATE('search.mdm-search.tooltip-no-name-set') ; |
| } |
| |
| getAdvancedSearchCbxTitle() { |
| return this.isAdvancedSearchActive |
| ? TRANSLATE('search.mdm-search.tooltip-disable-advanced-search') |
| : TRANSLATE('search.mdm-search.tooltip-enable-advanced-search'); |
| } |
| |
| /* |
| private loadSearchAttributes(environments: string[]) { |
| this.searchService.loadSearchAttributesStructured(environments) |
| .subscribe( |
| attrs => { this.allSearchAttributes = attrs; this.updateSearchAttributesForCurrentResultType(); }, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('search.mdm-search.err-cannot-load-attributes'), error)); |
| } |
| */ |
| onRowSelect(e: any) { |
| if (this.lazySelectedRow !== e.data) { |
| this.selectedRow = e.data; |
| this.filterName = e.data.name; |
| } else { |
| this.selectedRow = undefined; |
| this.filterName = ''; |
| } |
| this.lazySelectedRow = this.selectedRow; |
| } |
| } |