mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-28 09:35:38 -05:00
add save/load filter feature to profiler (#6170)
* save/load profiler filter * add role for custom buttons
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
.profiler-filter-dialog {
|
||||
height: 300px;
|
||||
padding: 10px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.profiler-filter-clause-table {
|
||||
@@ -14,13 +13,21 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.clause-table-container{
|
||||
max-height: 270px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.profiler-filter-remove-condition {
|
||||
width:20px;
|
||||
height:20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profiler-filter-add-clause-prompt {
|
||||
.profiler-filter-clause-table-action {
|
||||
cursor: pointer;
|
||||
margin: 0px 2px 0px 2px
|
||||
margin-right: 10px;
|
||||
padding: 2px;
|
||||
text-decoration: underline;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import * as nls from 'vs/nls';
|
||||
|
||||
import { ProfilerInput } from 'sql/workbench/parts/profiler/browser/profilerInput';
|
||||
import { ProfilerEditor } from 'sql/workbench/parts/profiler/browser/profilerEditor';
|
||||
import { PROFILER_VIEW_TEMPLATE_SETTINGS, PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerViewTemplate, IProfilerSessionTemplate, EngineType } from 'sql/workbench/services/profiler/common/interfaces';
|
||||
import { PROFILER_VIEW_TEMPLATE_SETTINGS, PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerViewTemplate, IProfilerSessionTemplate, EngineType, PROFILER_FILTER_SETTINGS } from 'sql/workbench/services/profiler/common/interfaces';
|
||||
|
||||
const profilerDescriptor = new EditorDescriptor(
|
||||
ProfilerEditor,
|
||||
@@ -346,14 +346,20 @@ const profilerSessionTemplateSchema: IJSONSchema = {
|
||||
]
|
||||
};
|
||||
|
||||
const profilerFiltersSchema: IJSONSchema = {
|
||||
description: nls.localize('profiler.settings.Filters', "Profiler Filters"),
|
||||
type: 'array'
|
||||
};
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
const dashboardConfig: IConfigurationNode = {
|
||||
const profilerConfig: IConfigurationNode = {
|
||||
id: 'Profiler',
|
||||
type: 'object',
|
||||
properties: {
|
||||
[PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema,
|
||||
[PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema
|
||||
[PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema,
|
||||
[PROFILER_FILTER_SETTINGS]: profilerFiltersSchema
|
||||
}
|
||||
};
|
||||
|
||||
configurationRegistry.registerConfiguration(dashboardConfig);
|
||||
configurationRegistry.registerConfiguration(profilerConfig);
|
||||
|
||||
@@ -22,19 +22,20 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ProfilerFilter, ProfilerFilterClause, ProfilerFilterClauseOperator } from 'sql/workbench/services/profiler/common/interfaces';
|
||||
import { ProfilerFilter, ProfilerFilterClause, ProfilerFilterClauseOperator, IProfilerService } from 'sql/workbench/services/profiler/common/interfaces';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
|
||||
const ClearText: string = localize('profilerFilterDialog.clear', "Clear All");
|
||||
const ClearText: string = localize('profilerFilterDialog.clear', "Clear all");
|
||||
const ApplyText: string = localize('profilerFilterDialog.apply', "Apply");
|
||||
const OkText: string = localize('profilerFilterDialog.ok', "OK");
|
||||
const CancelText: string = localize('profilerFilterDialog.cancel', "Cancel");
|
||||
const DialogTitle: string = localize('profilerFilterDialog.title', "Filters");
|
||||
const RemoveText: string = localize('profilerFilterDialog.remove', "Remove");
|
||||
const AddText: string = localize('profilerFilterDialog.add', "Add");
|
||||
const AddClausePromptText: string = localize('profilerFilterDialog.addClauseText', "Click here to add a clause");
|
||||
const RemoveText: string = localize('profilerFilterDialog.remove', "Remove this clause");
|
||||
const SaveFilterText: string = localize('profilerFilterDialog.saveFilter', "Save Filter");
|
||||
const LoadFilterText: string = localize('profilerFilterDialog.loadFilter', "Load Filter");
|
||||
const AddClauseText: string = localize('profilerFilterDialog.addClauseText', "Add a clause");
|
||||
const TitleIconClass: string = 'icon filterLabel';
|
||||
|
||||
const FieldText: string = localize('profilerFilterDialog.fieldColumn', "Field");
|
||||
@@ -61,9 +62,9 @@ export class ProfilerFilterDialog extends Modal {
|
||||
private _clauseBuilder: HTMLElement;
|
||||
private _okButton: Button;
|
||||
private _cancelButton: Button;
|
||||
private _clearButton: Button;
|
||||
private _applyButton: Button;
|
||||
private _addClauseButton: Button;
|
||||
private _loadFilterButton: Button;
|
||||
private _saveFilterButton: Button;
|
||||
private _input: ProfilerInput;
|
||||
private _clauseRows: ClauseRowUI[] = [];
|
||||
|
||||
@@ -75,7 +76,8 @@ export class ProfilerFilterDialog extends Modal {
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ILogService logService: ILogService,
|
||||
@IContextViewService private contextViewService: IContextViewService
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IProfilerService private profilerService: IProfilerService
|
||||
) {
|
||||
super('', TelemetryKeys.ProfilerFilter, telemetryService, layoutService, clipboardService, themeService, logService, contextKeyService, { isFlyout: false, hasTitleIcon: true });
|
||||
}
|
||||
@@ -96,21 +98,22 @@ export class ProfilerFilterDialog extends Modal {
|
||||
this.title = DialogTitle;
|
||||
this.titleIconClassName = TitleIconClass;
|
||||
this._register(attachModalDialogStyler(this, this._themeService));
|
||||
this._addClauseButton = this.addFooterButton(AddText, () => this.addClauseRow(false), 'left');
|
||||
this._clearButton = this.addFooterButton(ClearText, () => this.handleClearButtonClick(), 'left');
|
||||
this._saveFilterButton = this.addFooterButton(SaveFilterText, () => this.saveFilter(), 'left');
|
||||
this._loadFilterButton = this.addFooterButton(LoadFilterText, () => this.loadSavedFilter(), 'left');
|
||||
this._applyButton = this.addFooterButton(ApplyText, () => this.filterSession());
|
||||
this._okButton = this.addFooterButton(OkText, () => this.handleOkButtonClick());
|
||||
this._cancelButton = this.addFooterButton(CancelText, () => this.hide());
|
||||
this._register(attachButtonStyler(this._okButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._clearButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._applyButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._addClauseButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._saveFilterButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._loadFilterButton, this._themeService));
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
const body = DOM.append(container, DOM.$('.profiler-filter-dialog'));
|
||||
this._clauseBuilder = DOM.append(body, DOM.$('table.profiler-filter-clause-table'));
|
||||
const clauseTableContainer = DOM.append(body, DOM.$('.clause-table-container'));
|
||||
this._clauseBuilder = DOM.append(clauseTableContainer, DOM.$('table.profiler-filter-clause-table'));
|
||||
const headerRow = DOM.append(this._clauseBuilder, DOM.$('tr'));
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = FieldText;
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = OperatorText;
|
||||
@@ -121,15 +124,8 @@ export class ProfilerFilterDialog extends Modal {
|
||||
this.addClauseRow(true, clause.field, this.convertToOperatorString(clause.operator), clause.value);
|
||||
});
|
||||
|
||||
const prompt = DOM.append(body, DOM.$('.profiler-filter-add-clause-prompt', { tabIndex: '0' }));
|
||||
prompt.innerText = AddClausePromptText;
|
||||
DOM.addDisposableListener(prompt, DOM.EventType.CLICK, () => this.addClauseRow(false));
|
||||
DOM.addStandardDisposableListener(prompt, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
|
||||
this.addClauseRow(false);
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.createClauseTableActionLink(AddClauseText, body, () => { this.addClauseRow(false); });
|
||||
this.createClauseTableActionLink(ClearText, body, () => { this.handleClearButtonClick(); });
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
@@ -158,6 +154,21 @@ export class ProfilerFilterDialog extends Modal {
|
||||
this._clauseRows = [];
|
||||
}
|
||||
|
||||
private createClauseTableActionLink(text: string, parent: HTMLElement, handler: () => void): void {
|
||||
const actionLink = DOM.append(parent, DOM.$('.profiler-filter-clause-table-action', {
|
||||
'tabIndex': '0',
|
||||
'role': 'button'
|
||||
}));
|
||||
actionLink.innerText = text;
|
||||
DOM.addDisposableListener(actionLink, DOM.EventType.CLICK, handler);
|
||||
DOM.addStandardDisposableListener(actionLink, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
|
||||
handler();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private createSelectBox(container: HTMLElement, options: string[], selectedOption: string, ariaLabel: string): SelectBox {
|
||||
const dropdown = new SelectBox(options, selectedOption, this.contextViewService, undefined, { ariaLabel: ariaLabel });
|
||||
dropdown.render(container);
|
||||
@@ -165,10 +176,29 @@ export class ProfilerFilterDialog extends Modal {
|
||||
return dropdown;
|
||||
}
|
||||
|
||||
private filterSession() {
|
||||
private filterSession(): void {
|
||||
this._input.filterSession(this.getFilter());
|
||||
}
|
||||
|
||||
private saveFilter(): void {
|
||||
this.profilerService.saveFilter(this.getFilter());
|
||||
}
|
||||
|
||||
private loadSavedFilter(): void {
|
||||
// for now we only have one saved filter, this is enough for what user asked for so far.
|
||||
const savedFilters = this.profilerService.getFilters();
|
||||
if (savedFilters && savedFilters.length > 0) {
|
||||
const savedFilter = savedFilters[0];
|
||||
this._clauseRows.forEach(clause => {
|
||||
clause.row.remove();
|
||||
});
|
||||
this._clauseRows = [];
|
||||
savedFilter.clauses.forEach(clause => {
|
||||
this.addClauseRow(true, clause.field, this.convertToOperatorString(clause.operator), clause.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getFilter(): ProfilerFilter {
|
||||
const clauses: ProfilerFilterClause[] = [];
|
||||
|
||||
@@ -181,15 +211,20 @@ export class ProfilerFilterDialog extends Modal {
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'default',
|
||||
clauses: clauses
|
||||
};
|
||||
}
|
||||
|
||||
private addClauseRow(setInitialValue: boolean, field?: string, operator?: string, value?: string): any {
|
||||
private addClauseRow(setInitialValue: boolean, field?: string, operator?: string, value?: string): void {
|
||||
const columns = this._input.columns.map(column => column.name);
|
||||
if (field && !columns.includes(field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const row = DOM.append(this._clauseBuilder, DOM.$('tr'));
|
||||
const clauseId = generateUuid();
|
||||
|
||||
const columns = this._input.columns.map(column => column.name);
|
||||
const fieldDropDown = this.createSelectBox(DOM.append(row, DOM.$('td')), columns, columns[0], FieldText);
|
||||
|
||||
const operatorDropDown = this.createSelectBox(DOM.append(row, DOM.$('td')), Operators, Operators[0], OperatorText);
|
||||
@@ -201,7 +236,8 @@ export class ProfilerFilterDialog extends Modal {
|
||||
const removeClauseButton = DOM.append(removeCell, DOM.$('.profiler-filter-remove-condition.icon.remove', {
|
||||
'tabIndex': '0',
|
||||
'aria-label': RemoveText,
|
||||
'title': RemoveText
|
||||
'title': RemoveText,
|
||||
'role': 'button'
|
||||
}));
|
||||
|
||||
DOM.addStandardDisposableListener(removeClauseButton, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||
|
||||
@@ -17,6 +17,7 @@ export type ProfilerSessionID = string;
|
||||
|
||||
export const PROFILER_VIEW_TEMPLATE_SETTINGS = 'profiler.viewTemplates';
|
||||
export const PROFILER_SESSION_TEMPLATE_SETTINGS = 'profiler.sessionTemplates';
|
||||
export const PROFILER_FILTER_SETTINGS = 'profiler.filters';
|
||||
export const PROFILER_SETTINGS = 'profiler';
|
||||
|
||||
/**
|
||||
@@ -130,11 +131,21 @@ export interface IProfilerService {
|
||||
* @param input input object
|
||||
*/
|
||||
launchFilterSessionDialog(input: ProfilerInput): void;
|
||||
/**
|
||||
* Gets the filters
|
||||
*/
|
||||
getFilters(): Array<ProfilerFilter>;
|
||||
/**
|
||||
* Saves the filter
|
||||
* @param filter filter object
|
||||
*/
|
||||
saveFilter(filter: ProfilerFilter): void;
|
||||
}
|
||||
|
||||
export interface IProfilerSettings {
|
||||
viewTemplates: Array<IProfilerViewTemplate>;
|
||||
sessionTemplates: Array<IProfilerSessionTemplate>;
|
||||
filters: Array<ProfilerFilter>;
|
||||
}
|
||||
|
||||
export interface IColumnViewTemplate {
|
||||
@@ -160,6 +171,7 @@ export interface IProfilerSessionTemplate {
|
||||
}
|
||||
|
||||
export interface ProfilerFilter {
|
||||
name?: string;
|
||||
clauses: ProfilerFilterClause[];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
|
||||
import {
|
||||
ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate,
|
||||
PROFILER_SETTINGS, IProfilerSettings, EngineType
|
||||
} from './interfaces';
|
||||
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS } from './interfaces';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ProfilerInput } from 'sql/workbench/parts/profiler/browser/profilerInput';
|
||||
import { ProfilerColumnEditorDialog } from 'sql/workbench/parts/profiler/browser/profilerColumnEditorDialog';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -242,4 +239,14 @@ export class ProfilerService implements IProfilerService {
|
||||
let dialog = this._instantiationService.createInstance(ProfilerFilterDialog);
|
||||
dialog.open(input);
|
||||
}
|
||||
|
||||
public getFilters(): ProfilerFilter[] {
|
||||
const config = <ProfilerFilter[]>this._configurationService.getValue(PROFILER_FILTER_SETTINGS);
|
||||
return config;
|
||||
}
|
||||
|
||||
public saveFilter(filter: ProfilerFilter): void {
|
||||
const config = [filter];
|
||||
this._configurationService.updateValue(PROFILER_FILTER_SETTINGS, config, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user