Skip to content

Commit deb49ba

Browse files
committed
feat: extend visualization creation flow to support SQL mode with validation and improved UI elements
1 parent 698e529 commit deb49ba

File tree

15 files changed

+105
-72
lines changed

15 files changed

+105
-72
lines changed

frontend/src/app/graphic-builder/chart-builder/chart-builder.component.html

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="container-fluid pr-3 pl-3 pt-2">
22
<div class="d-flex justify-content-between align-items-center">
33
<h5 class="mb-0 label-header px-3 py-2">
4-
{{mode==='edit' ? visualization.name : 'Visualization builder'}}
4+
{{mode==='edit' ? visualization && visualization.name : 'Visualization builder'}}
55
</h5>
66
</div>
77

@@ -14,6 +14,7 @@ <h5 class="mb-0 label-header px-3 py-2">
1414
(sqlModeToggled)="toggleSqlMode($event)"
1515
(indexPatternInitialized)="indexPatternLoaded($event)"
1616
[pattern]="visualization.pattern"
17+
[showModeToggle] = "mode !== 'edit'"
1718
*ngIf="!loading"
1819
>
1920
</app-visualization-header>
@@ -106,11 +107,13 @@ <h5 class="mb-0 label-header px-3 py-2">
106107
</div>
107108
<div *ngIf="visualization" [hidden]="property !== headers[1].name"
108109
class="pt-2 option-container w-100 h-100">
109-
<app-metric-properties-option (metricOptions)="onMetricOptionChange($event)"
110-
*ngSwitchCase="chartTypeEnum.METRIC_CHART"
111-
[mode]="mode"
112-
[visualization]="visualization">
113-
</app-metric-properties-option>
110+
<ng-container *ngSwitchCase="chartTypeEnum.METRIC_CHART">
111+
<app-metric-properties-option (metricOptions)="onMetricOptionChange($event)"
112+
*ngIf="!isSqlMode"
113+
[mode]="mode"
114+
[visualization]="visualization">
115+
</app-metric-properties-option>
116+
</ng-container>
114117
<ng-container *ngSwitchCase="chartTypeEnum.PIE_CHART">
115118
<app-pie-properties-options (pieOptions)="onPieOptionsChange($event)"
116119
*ngIf="visualization.chartConfig"
@@ -127,7 +130,7 @@ <h5 class="mb-0 label-header px-3 py-2">
127130
</ng-container>
128131
<ng-container *ngSwitchCase="chartTypeEnum.GOAL_CHART">
129132
<app-goal-properties-option (goalOptions)="onGoalOptionChange($event)"
130-
*ngIf="visualization.chartConfig"
133+
*ngIf="visualization.chartConfig && !sqlQuery"
131134
[mode]="mode"
132135
[visualization]="visualization">
133136
</app-goal-properties-option>

frontend/src/app/graphic-builder/chart-builder/chart-builder.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ export class ChartBuilderComponent implements OnInit, AfterViewChecked {
172172
}
173173

174174
saveVisualization() {
175+
if (this.isSqlMode && this.sqlQuery === '') {
176+
this.errorMessage = 'SQL Query cannot be empty';
177+
return;
178+
}
175179
const modal = this.modalService.open(VisualizationSaveComponent, {centered: true});
176180
if (this.isSqlMode) {
177181
this.nullifyUnusedFields();

frontend/src/app/graphic-builder/shared/components/viewer/chart-view/chart-view.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import {Observable, of, Subject} from 'rxjs';
33
import {catchError, filter, switchMap, takeUntil, tap} from 'rxjs/operators';
44
import {UtmToastService} from '../../../../../shared/alert/utm-toast.service';
55
import {DashboardBehavior} from '../../../../../shared/behaviors/dashboard.behavior';
6-
import {TimeFilterBehavior} from "../../../../../shared/behaviors/time-filter.behavior";
6+
import {TimeFilterBehavior} from '../../../../../shared/behaviors/time-filter.behavior';
77
import {ChartFactory} from '../../../../../shared/chart/factories/echart-factory/chart-factory';
88
import {VisualizationType} from '../../../../../shared/chart/types/visualization.type';
99
import {
1010
ElasticFilterDefaultTime
1111
} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
12+
import {ChartBuilderQueryLanguageEnum} from '../../../../../shared/enums/chart-builder-query-language.enum';
1213
import {ChartTypeEnum} from '../../../../../shared/enums/chart-type.enum';
1314
import {ElasticOperatorsEnum} from '../../../../../shared/enums/elastic-operators.enum';
1415
import {RefreshService, RefreshType} from '../../../../../shared/services/util/refresh.service';
@@ -20,7 +21,6 @@ import {RunVisualizationService} from '../../../services/run-visualization.servi
2021
import {UtmChartClickActionService} from '../../../services/utm-chart-click-action.service';
2122
import {rebuildVisualizationFilterTime} from '../../../util/chart-filter/chart-filter.util';
2223
import {resolveDefaultVisualizationTime} from '../../../util/visualization/visualization-render.util';
23-
import {ChartBuilderQueryLanguageEnum} from "../../../../../shared/enums/chart-builder-query-language.enum";
2424
import EChartOption = echarts.EChartOption;
2525
// @ts-ignore
2626
require('echarts-wordcloud');
@@ -80,7 +80,8 @@ export class ChartViewComponent implements OnInit, OnDestroy {
8080
.subscribe(id => {
8181
if (id && this.chartId === id) {
8282
this.refreshService.sendRefresh(this.refreshType);
83-
this.defaultTime = this.visualization.filterType ? resolveDefaultVisualizationTime(this.visualization) : null;
83+
this.defaultTime = this.visualization.filterType && this.visualization.queryLanguage !== ChartBuilderQueryLanguageEnum.SQL ?
84+
resolveDefaultVisualizationTime(this.visualization) : new ElasticFilterDefaultTime('now-30d', 'now');
8485
}
8586
});
8687

@@ -122,7 +123,8 @@ export class ChartViewComponent implements OnInit, OnDestroy {
122123
});
123124

124125
if (!this.defaultTime) {
125-
this.defaultTime = this.visualization.filterType ? resolveDefaultVisualizationTime(this.visualization) : null;
126+
this.defaultTime = this.visualization.filterType ? resolveDefaultVisualizationTime(this.visualization)
127+
: new ElasticFilterDefaultTime('now-30d', 'now');
126128
this.refreshService.sendRefresh(this.refreshType);
127129
}
128130
}

frontend/src/app/graphic-builder/shared/components/viewer/goal-view/goal-view.component.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import {Observable, of, Subject} from 'rxjs';
33
import {catchError, filter, switchMap, takeUntil, tap} from 'rxjs/operators';
44
import {UtmToastService} from '../../../../../shared/alert/utm-toast.service';
55
import {DashboardBehavior} from '../../../../../shared/behaviors/dashboard.behavior';
6+
import {TimeFilterBehavior} from '../../../../../shared/behaviors/time-filter.behavior';
67
import {EchartClickAction} from '../../../../../shared/chart/types/action/echart-click-action';
78
import {UtmGoalOption} from '../../../../../shared/chart/types/charts/goal/utm-goal-option';
89
import {MetricResponse} from '../../../../../shared/chart/types/metric/metric-response';
910
import {VisualizationType} from '../../../../../shared/chart/types/visualization.type';
10-
import {ElasticFilterDefaultTime} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
11+
import {
12+
ElasticFilterDefaultTime
13+
} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
14+
import {ChartBuilderQueryLanguageEnum} from '../../../../../shared/enums/chart-builder-query-language.enum';
1115
import {ChartTypeEnum} from '../../../../../shared/enums/chart-type.enum';
1216
import {RefreshService, RefreshType} from '../../../../../shared/services/util/refresh.service';
1317
import {TimeFilterType} from '../../../../../shared/types/time-filter.type';
@@ -18,7 +22,6 @@ import {RunVisualizationService} from '../../../services/run-visualization.servi
1822
import {UtmChartClickActionService} from '../../../services/utm-chart-click-action.service';
1923
import {rebuildVisualizationFilterTime} from '../../../util/chart-filter/chart-filter.util';
2024
import {resolveDefaultVisualizationTime} from '../../../util/visualization/visualization-render.util';
21-
import {TimeFilterBehavior} from "../../../../../shared/behaviors/time-filter.behavior";
2225

2326
@Component({
2427
selector: 'app-goal-view',
@@ -66,7 +69,9 @@ export class GoalViewComponent implements OnInit, OnDestroy {
6669
.subscribe((id) => {
6770
if (id && this.chartId === id) {
6871
this.refreshService.sendRefresh(this.refreshType);
69-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
72+
this.defaultTime = this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
73+
resolveDefaultVisualizationTime(this.visualization)
74+
: new ElasticFilterDefaultTime('now-30d', 'now');
7075
}
7176
});
7277
this.dashboardBehavior.$filterDashboard
@@ -94,27 +99,14 @@ export class GoalViewComponent implements OnInit, OnDestroy {
9499
});
95100

96101
if (!this.defaultTime) {
97-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
102+
this.defaultTime = this.visualization.filterType ? resolveDefaultVisualizationTime(this.visualization)
103+
: new ElasticFilterDefaultTime('now-30d', 'now');
98104
this.refreshService.sendRefresh(this.refreshType);
99105
}
100106
}
101107

102108
runVisualization() {
103109
this.runningChart = true;
104-
/*this.runVisualizationService.run(this.visualization).subscribe(data => {
105-
this.runningChart = false;
106-
this.runned.emit('runned');
107-
this.data = data;
108-
this.extractGoals();
109-
this.error = false;
110-
}, error => {
111-
this.runningChart = false;
112-
this.error = true;
113-
this.runned.emit('runned');
114-
this.toastService.showError('Error',
115-
'Error occurred while running visualization');
116-
});*/
117-
118110
return this.runVisualizationService.run(this.visualization)
119111
.pipe(
120112
tap((data) => {
@@ -150,9 +142,9 @@ export class GoalViewComponent implements OnInit, OnDestroy {
150142
this.goals = [];
151143
const config: UtmGoalOption[] = this.visualization.chartConfig;
152144
if (data) {
153-
for (const d of data) {
154-
const metricIndex = this.visualization.aggregationType.metrics.findIndex(value => Number(value.id) === Number(d.metricId));
155-
const optionIndex = config.findIndex(value => Number(value.metricId) === Number(d.metricId));
145+
data.forEach((d, index) => {
146+
const metricId = isNaN(Number(d.metricId)) ? index + 1 : Number(d.metricId);
147+
const optionIndex = config.findIndex(value => Number(value.metricId) === metricId);
156148
const max = (config[optionIndex].max ? config[optionIndex].max : this.calcTotal(data));
157149
const goal = new UtmGoalOption(Number(d.metricId),
158150
this.calcPercent(max, d.value, config[optionIndex].decimal),
@@ -164,11 +156,13 @@ export class GoalViewComponent implements OnInit, OnDestroy {
164156
config[optionIndex].cap,
165157
config[optionIndex].type,
166158
config[optionIndex].thresholds,
167-
d.bucketKey ? d.bucketKey : extractMetricLabel(d.metricId, this.visualization),
159+
this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
160+
d.bucketKey ? d.bucketKey : extractMetricLabel(d.metricId, this.visualization)
161+
: d.metricId,
168162
config[optionIndex].foregroundColor
169163
);
170164
this.goals.push(goal);
171-
}
165+
});
172166
}
173167
return this.goals;
174168
}

frontend/src/app/graphic-builder/shared/components/viewer/map-view/map-view.component.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {UtmScatterMapOptionType} from '../../../../../shared/chart/types/charts/
1111
import {LeafletMapType} from '../../../../../shared/chart/types/map/leaflet/leaflet-map.type';
1212
import {VisualizationType} from '../../../../../shared/chart/types/visualization.type';
1313
import {ElasticFilterDefaultTime} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
14+
import {ChartBuilderQueryLanguageEnum} from '../../../../../shared/enums/chart-builder-query-language.enum';
1415
import {ChartTypeEnum} from '../../../../../shared/enums/chart-type.enum';
1516
import {RefreshService, RefreshType} from '../../../../../shared/services/util/refresh.service';
1617
import {TimeFilterType} from '../../../../../shared/types/time-filter.type';
@@ -295,7 +296,9 @@ export class MapViewComponent implements OnInit, AfterViewInit, OnDestroy {
295296
.subscribe(id => {
296297
if (id && this.chartId === id) {
297298
this.refreshService.sendRefresh(this.refreshType);
298-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
299+
this.defaultTime = this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
300+
resolveDefaultVisualizationTime(this.visualization)
301+
: new ElasticFilterDefaultTime('now-30d', 'now');
299302
}
300303
});
301304
this.dashboardBehavior.$filterDashboard
@@ -323,7 +326,9 @@ export class MapViewComponent implements OnInit, AfterViewInit, OnDestroy {
323326
});
324327

325328
if (!this.defaultTime) {
326-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
329+
this.defaultTime = this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
330+
resolveDefaultVisualizationTime(this.visualization)
331+
: new ElasticFilterDefaultTime('now-30d', 'now');
327332
this.refreshService.sendRefresh(this.refreshType);
328333
}
329334
}
@@ -392,7 +397,7 @@ export class MapViewComponent implements OnInit, AfterViewInit, OnDestroy {
392397
'padding-top:10px' +
393398
'font: 13px / 20px Poppins, sans-serif;' +
394399
'pointer-events: none;">' +
395-
getBucketLabel(0, this.visualization) +
400+
this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ? getBucketLabel(0, this.visualization) : '' +
396401
'<br>' +
397402
'<span style="display:inline-block;' +
398403
'margin-right:5px;' +

frontend/src/app/graphic-builder/shared/components/viewer/metric-view/metric-view.component.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2+
import {Observable, of, Subject} from 'rxjs';
3+
import {catchError, filter, switchMap, takeUntil, tap} from 'rxjs/operators';
24
import {UtmToastService} from '../../../../../shared/alert/utm-toast.service';
35
import {DashboardBehavior} from '../../../../../shared/behaviors/dashboard.behavior';
6+
import {TimeFilterBehavior} from '../../../../../shared/behaviors/time-filter.behavior';
47
import {EchartClickAction} from '../../../../../shared/chart/types/action/echart-click-action';
58
import {MetricResponse} from '../../../../../shared/chart/types/metric/metric-response';
69
import {VisualizationType} from '../../../../../shared/chart/types/visualization.type';
7-
import {
8-
ElasticFilterDefaultTime
9-
} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
10+
import {ElasticFilterDefaultTime} from '../../../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
11+
import {ChartBuilderQueryLanguageEnum} from '../../../../../shared/enums/chart-builder-query-language.enum';
1012
import {ChartTypeEnum} from '../../../../../shared/enums/chart-type.enum';
13+
import {RefreshService, RefreshType} from '../../../../../shared/services/util/refresh.service';
1114
import {TimeFilterType} from '../../../../../shared/types/time-filter.type';
1215
import {mergeParams, sanitizeFilters} from '../../../../../shared/util/elastic-filter.util';
1316
import {extractMetricLabel} from '../../../../chart-builder/chart-property-builder/shared/functions/visualization-util';
@@ -16,10 +19,7 @@ import {RunVisualizationService} from '../../../services/run-visualization.servi
1619
import {UtmChartClickActionService} from '../../../services/utm-chart-click-action.service';
1720
import {rebuildVisualizationFilterTime} from '../../../util/chart-filter/chart-filter.util';
1821
import {resolveDefaultVisualizationTime} from '../../../util/visualization/visualization-render.util';
19-
import {Observable, of, Subject} from "rxjs";
20-
import {RefreshService, RefreshType} from "../../../../../shared/services/util/refresh.service";
21-
import {catchError, filter, switchMap, takeUntil, tap} from 'rxjs/operators';
22-
import {TimeFilterBehavior} from "../../../../../shared/behaviors/time-filter.behavior";
22+
2323

2424
@Component({
2525
selector: 'app-metric-view',
@@ -121,6 +121,8 @@ export class MetricViewComponent implements OnInit, OnDestroy {
121121
this.toastService.showError('Error',
122122
'Error occurred while running visualization');
123123
});*/
124+
this.visualization.queryLanguage = this.visualization.sqlQuery ? ChartBuilderQueryLanguageEnum.SQL
125+
: ChartBuilderQueryLanguageEnum.DSL;
124126

125127
return this.runVisualizationService.run(this.visualization)
126128
.pipe(
@@ -170,8 +172,12 @@ export class MetricViewComponent implements OnInit, OnDestroy {
170172
const optionIndex = this.visualization.chartConfig.findIndex(value => Number(value.metricId) === Number(d.metricId));
171173
kpi.push({
172174
value: d.value,
173-
label: extractMetricLabel(this.visualization.aggregationType.metrics[metricIndex].id, this.visualization),
174-
group: this.extractGroupName(d.bucketKey),
175+
label: this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
176+
extractMetricLabel(this.visualization.aggregationType.metrics[metricIndex].id, this.visualization)
177+
: d.metricId ? d.metricId : '',
178+
group: this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
179+
this.extractGroupName(d.bucketKey)
180+
: d.bucketId + ' - ' + d.bucketKey,
175181
bucketKey: d.bucketKey,
176182
color: optionIndex > -1 ? this.visualization.chartConfig[optionIndex].color : null,
177183
icon: optionIndex > -1 ? this.visualization.chartConfig[optionIndex].icon : null,

frontend/src/app/graphic-builder/shared/components/viewer/table-view/table-view.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
*ngFor="let column of data.columns"
4949
[sortable]="column.split('->')[0]"
5050
appColumnSortable class="font-weight-semibold cursor-pointer" scope="col">
51-
{{column.split('->')[1]}}
51+
{{ column.split('->')[1] || column.split('->')[0] }}
5252
</th>
5353
</tr>
5454
</thead>

frontend/src/app/graphic-builder/shared/components/viewer/table-view/table-view.component.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {Observable, of, Subject} from 'rxjs';
1313
import {catchError, filter, map, switchMap, takeUntil, tap} from 'rxjs/operators';
1414
import {UtmToastService} from '../../../../../shared/alert/utm-toast.service';
1515
import {DashboardBehavior} from '../../../../../shared/behaviors/dashboard.behavior';
16-
import {TimeFilterBehavior} from "../../../../../shared/behaviors/time-filter.behavior";
16+
import {TimeFilterBehavior} from '../../../../../shared/behaviors/time-filter.behavior';
1717
import {EchartClickAction} from '../../../../../shared/chart/types/action/echart-click-action';
1818
import {UtmTableOptionType} from '../../../../../shared/chart/types/charts/table/utm-table-option.type';
1919
import {TableBuilderResponseType} from '../../../../../shared/chart/types/response/table-builder-response.type';
@@ -22,6 +22,7 @@ import {ElasticFilterDefaultTime} from '../../../../../shared/components/utm/fil
2222
import {SortableDirective} from '../../../../../shared/directives/sortable/sortable.directive';
2323
import {SortDirection} from '../../../../../shared/directives/sortable/type/sort-direction.type';
2424
import {SortEvent} from '../../../../../shared/directives/sortable/type/sort-event';
25+
import {ChartBuilderQueryLanguageEnum} from '../../../../../shared/enums/chart-builder-query-language.enum';
2526
import {ChartTypeEnum} from '../../../../../shared/enums/chart-type.enum';
2627
import {ChartValueSeparator} from '../../../../../shared/enums/chart-value-separator';
2728
import {RefreshService, RefreshType} from '../../../../../shared/services/util/refresh.service';
@@ -98,7 +99,9 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
9899
.subscribe(id => {
99100
if (id && this.chartId === id) {
100101
this.refreshService.sendRefresh(this.refreshType);
101-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
102+
this.defaultTime = this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
103+
resolveDefaultVisualizationTime(this.visualization)
104+
: new ElasticFilterDefaultTime('now-30d', 'now');
102105
}
103106
});
104107
this.dashboardBehavior.$filterDashboard
@@ -126,7 +129,9 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
126129
});
127130

128131
if (!this.defaultTime) {
129-
this.defaultTime = resolveDefaultVisualizationTime(this.visualization);
132+
this.defaultTime = this.visualization.queryLanguage === ChartBuilderQueryLanguageEnum.DSL ?
133+
resolveDefaultVisualizationTime(this.visualization)
134+
: new ElasticFilterDefaultTime('now-30d', 'now');
130135
this.refreshService.sendRefresh(this.refreshType);
131136
}
132137
}

0 commit comments

Comments
 (0)