This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 1605f6b47b fix(#3430): filtering for numerical dimension properties in
data explorer (#3436)
1605f6b47b is described below
commit 1605f6b47bce582b3e3dafa63cacf543ec320fde
Author: Philipp Zehnder <[email protected]>
AuthorDate: Thu Jan 23 16:57:47 2025 +0100
fix(#3430): filtering for numerical dimension properties in data explorer
(#3436)
* fix(#3430): Remove unused parameter in DataLakeUtils.ts
* fix(#3430): Add method to validate amount of table results in data
explorer
* fix(#3430): Add e2e test to validate functionality of filter dimension
property numbers
* fix(#3430): Refactor filter selection panel row and filter operator
according to data type
* fix(#3430): Numerical values are now handled as expected by filters
* fix(#3430): Numerical values are now handled as expected by filters
* fix(#3430): Change directory name to pass windows CI builds
* fix(#3430): Rename component to meet windows file name length restrictions
---
.../param/model/WhereClauseParams.java | 50 +++++--
ui/angular.json | 3 +-
.../datalake/filterNumericalStringProperties.csv | 3 +
ui/cypress/support/utils/connect/ConnectUtils.ts | 24 ++--
ui/cypress/support/utils/datalake/DataLakeUtils.ts | 46 ++++++-
.../utils/datalake/DataLakeWidgetTableUtils.ts | 12 +-
.../tests/datalake/deleteViewAndDashboard.spec.ts | 2 +-
ui/cypress/tests/datalake/deleteWidget.ts | 2 +-
.../filterNumericalStringProperties.spec.ts | 89 +++++++++++++
.../tests/datalake/missingDataInDataLake.spec.ts | 4 +-
.../tests/datalake/timeOrderDataView.spec.ts | 2 +-
.../tests/datalake/timeRangeSelectors.spec.ts | 2 +-
.../datalake/widgetDataConfiguration.smoke.spec.ts | 24 ++--
ui/cypress/tests/datalake/widgets/table.spec.ts | 5 +-
.../escape-number-filter.service.ts | 62 +++++++++
.../filter-selection-panel-row.component.html | 58 +++++++++
.../filter-selection-panel-row.component.ts} | 49 ++++---
...on-panel-row-operation-selection.component.html | 46 +++++++
...ion-panel-row-operation-selection.component.ts} | 30 +++--
...ion-panel-row-property-selection.component.html | 31 +++++
...tion-panel-row-property-selection.component.ts} | 35 +++--
...ion-panel-row-value-autocomplete.component.html | 42 ++++++
...ction-panel-row-value-autocomplete.component.ts | 57 ++++++++
...-selection-panel-row-value-input.component.html | 28 ++++
...er-selection-panel-row-value-input.component.ts | 55 ++++++++
.../filter-selection-panel.component.html | 143 ++-------------------
.../filter-selection-panel.component.ts | 9 +-
ui/src/app/data-explorer/data-explorer.module.ts | 10 ++
28 files changed, 697 insertions(+), 226 deletions(-)
diff --git
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
index 3381983268..01613487a5 100644
---
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
+++
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/param/model/WhereClauseParams.java
@@ -34,17 +34,21 @@ public class WhereClauseParams implements IQueryStatement {
private final List<FilterCondition> filterConditions;
- private WhereClauseParams(Long startTime,
- Long endTime,
- String whereConditions) {
+ private WhereClauseParams(
+ Long startTime,
+ Long endTime,
+ String whereConditions
+ ) {
this(startTime, endTime);
if (whereConditions != null) {
buildConditions(whereConditions);
}
}
- private WhereClauseParams(Long startTime,
- Long endTime) {
+ private WhereClauseParams(
+ Long startTime,
+ Long endTime
+ ) {
this.filterConditions = new ArrayList<>();
this.buildTimeConditions(startTime, endTime);
}
@@ -56,8 +60,10 @@ public class WhereClauseParams implements IQueryStatement {
}
}
- public static WhereClauseParams from(Long startTime,
- Long endTime) {
+ public static WhereClauseParams from(
+ Long startTime,
+ Long endTime
+ ) {
return new WhereClauseParams(startTime, endTime);
}
@@ -65,14 +71,18 @@ public class WhereClauseParams implements IQueryStatement {
return new WhereClauseParams(whereConditions);
}
- public static WhereClauseParams from(Long startTime,
- Long endTime,
- String whereConditions) {
+ public static WhereClauseParams from(
+ Long startTime,
+ Long endTime,
+ String whereConditions
+ ) {
return new WhereClauseParams(startTime, endTime, whereConditions);
}
- private void buildTimeConditions(Long startTime,
- Long endTime) {
+ private void buildTimeConditions(
+ Long startTime,
+ Long endTime
+ ) {
if (startTime == null) {
this.filterConditions.add(buildTimeBoundary(endTime, LT));
} else if (endTime == null) {
@@ -98,7 +108,9 @@ public class WhereClauseParams implements IQueryStatement {
}
private Object returnCondition(String inputCondition) {
- if (NumberUtils.isParsable(inputCondition)) {
+ if (isQuotedString(inputCondition)) {
+ return removeQuotes(inputCondition);
+ } else if (NumberUtils.isParsable(inputCondition)) {
return Double.parseDouble(inputCondition);
} else if (isBoolean(inputCondition)) {
return Boolean.parseBoolean(inputCondition);
@@ -107,6 +119,18 @@ public class WhereClauseParams implements IQueryStatement {
}
}
+ private boolean isQuotedString(String input) {
+ if (input.startsWith("\"") && input.endsWith("\"")) {
+ String content = removeQuotes(input);
+ return NumberUtils.isParsable(content);
+ }
+ return false;
+ }
+
+ private String removeQuotes(String input) {
+ return input.substring(1, input.length() - 1);
+ }
+
private boolean isBoolean(String input) {
return "true".equalsIgnoreCase(input) || "false".equalsIgnoreCase(input);
}
diff --git a/ui/angular.json b/ui/angular.json
index 17b7a423cc..384b9b88b4 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -189,7 +189,8 @@
},
"schematics": {
"@schematics/angular:component": {
- "style": "scss"
+ "style": "scss",
+ "standalone": false
}
},
"cli": {
diff --git a/ui/cypress/fixtures/datalake/filterNumericalStringProperties.csv
b/ui/cypress/fixtures/datalake/filterNumericalStringProperties.csv
new file mode 100644
index 0000000000..3cf0902c26
--- /dev/null
+++ b/ui/cypress/fixtures/datalake/filterNumericalStringProperties.csv
@@ -0,0 +1,3 @@
+timestamp;dimensionKey;v1
+1737123058000;1.0;a
+1737123059000;2.0;20
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts
b/ui/cypress/support/utils/connect/ConnectUtils.ts
index 52d2ec6c88..40956ebffb 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -48,6 +48,8 @@ export class ConnectUtils {
ConnectEventSchemaUtils.addTimestampProperty();
}
+ ConnectUtils.configureDimensionProperties(adapterConfiguration);
+
ConnectEventSchemaUtils.finishEventSchemaConfiguration();
ConnectUtils.startAdapter(
@@ -66,6 +68,20 @@ export class ConnectUtils {
ConnectUtils.configureAdapter(adapterConfiguration);
+ ConnectUtils.configureDimensionProperties(adapterConfiguration);
+
+ if (adapterConfiguration.timestampProperty) {
+ ConnectEventSchemaUtils.markPropertyAsTimestamp(
+ adapterConfiguration.timestampProperty,
+ );
+ }
+
+ ConnectEventSchemaUtils.finishEventSchemaConfiguration();
+ }
+
+ private static configureDimensionProperties(
+ adapterConfiguration: AdapterInput,
+ ) {
if (adapterConfiguration.dimensionProperties.length > 0) {
adapterConfiguration.dimensionProperties.forEach(
dimensionPropertyName => {
@@ -75,14 +91,6 @@ export class ConnectUtils {
},
);
}
-
- if (adapterConfiguration.timestampProperty) {
- ConnectEventSchemaUtils.markPropertyAsTimestamp(
- adapterConfiguration.timestampProperty,
- );
- }
-
- ConnectEventSchemaUtils.finishEventSchemaConfiguration();
}
public static addMachineDataSimulator(
diff --git a/ui/cypress/support/utils/datalake/DataLakeUtils.ts
b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
index 664c2ccd1c..e9b87103d5 100644
--- a/ui/cypress/support/utils/datalake/DataLakeUtils.ts
+++ b/ui/cypress/support/utils/datalake/DataLakeUtils.ts
@@ -69,7 +69,6 @@ export class DataLakeUtils {
public static loadDataIntoDataLake(
dataSet: string,
- wait = true,
format: 'csv' | 'json_array' = 'csv',
) {
// Create adapter with dataset
@@ -182,7 +181,9 @@ export class DataLakeUtils {
}
public static saveDataViewConfiguration() {
- cy.dataCy('save-data-view-btn', { timeout: 10000 }).click();
+ cy.dataCy('save-data-view-btn', { timeout: 10000 }).click({
+ force: true,
+ });
}
public static saveDashboardConfiguration() {
@@ -279,6 +280,43 @@ export class DataLakeUtils {
}
}
+ /**
+ * This method validates that the defined filter options are available in
the UI
+ * @param expectedFilterOptions
+ */
+ public static validateFilterOptions(
+ expectedFilterOptions: ('=' | '<' | '<=' | '>=' | '>' | '!=')[],
+ ) {
+ cy.dataCy('design-panel-data-settings-filter-operator')
+ .click()
+ .dataCy('operator-', {}, true)
+ .should('have.length', expectedFilterOptions.length);
+
+ expectedFilterOptions.forEach(option => {
+ const escapedOption = option.replace(/([=<>!])/g, '\\$1');
+ cy.dataCy('operator-' + escapedOption).should('be.visible');
+ });
+
+ cy.dataCy('design-panel-data-settings-filter-operator').click({
+ force: true,
+ });
+ }
+
+ public static validateAutoCompleteOptions(options: string[]) {
+ cy.dataCy('design-panel-data-settings-filter-value')
+ .click({ force: true })
+ .dataCy('autocomplete-value-', {}, true)
+ .should('have.length', options.length);
+
+ options.forEach(option => {
+ cy.dataCy('autocomplete-value-' + option).should('be.visible');
+ });
+
+ cy.dataCy('design-panel-data-settings-filter-value').click({
+ force: true,
+ });
+ }
+
/**
* In the data set panel select all property fields
*/
@@ -310,7 +348,9 @@ export class DataLakeUtils {
}
public static dataConfigRemoveFilter() {
- cy.dataCy('design-panel-data-settings-remove-filter').first().click();
+ cy.dataCy('design-panel-data-settings-remove-filter')
+ .first()
+ .click({ force: true });
}
public static clickGroupBy(propertyName: string) {
diff --git a/ui/cypress/support/utils/datalake/DataLakeWidgetTableUtils.ts
b/ui/cypress/support/utils/datalake/DataLakeWidgetTableUtils.ts
index 733f8616a0..d09177c6ea 100644
--- a/ui/cypress/support/utils/datalake/DataLakeWidgetTableUtils.ts
+++ b/ui/cypress/support/utils/datalake/DataLakeWidgetTableUtils.ts
@@ -17,13 +17,17 @@
*/
export class DataLakeWidgetTableUtils {
+ public static dataExplorerTableRowTimestamp() {
+ return cy.dataCy('data-explorer-table-row-timestamp', {
+ timeout: 10000,
+ });
+ }
+
/**
* Checks how many rows are visible within the table widget in the data
explorer
* @param amount of expected rows
*/
- public static checkRows(amount: number) {
- cy.dataCy('data-explorer-table-row-timestamp', {
- timeout: 10000,
- }).should('have.length', amount);
+ public static checkAmountOfRows(amount: number) {
+ this.dataExplorerTableRowTimestamp().should('have.length', amount);
}
}
diff --git a/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts
b/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts
index fb4526f187..620d5158dd 100644
--- a/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts
+++ b/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts
@@ -20,7 +20,7 @@ import { DataLakeUtils } from
'../../support/utils/datalake/DataLakeUtils';
describe('Test Deletion of Data View and Dashboard', () => {
beforeEach('Setup Test', () => {
cy.initStreamPipesTest();
- DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false);
+ DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv');
});
it('Perform Test', () => {
diff --git a/ui/cypress/tests/datalake/deleteWidget.ts
b/ui/cypress/tests/datalake/deleteWidget.ts
index ee5de09d95..a51db31f9a 100644
--- a/ui/cypress/tests/datalake/deleteWidget.ts
+++ b/ui/cypress/tests/datalake/deleteWidget.ts
@@ -20,7 +20,7 @@ import { DataLakeUtils } from
'../../support/utils/datalake/DataLakeUtils';
describe('Test Table View in Data Explorer', () => {
beforeEach('Setup Test', () => {
cy.initStreamPipesTest();
- DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false);
+ DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv');
});
it('Perform Test', () => {
diff --git a/ui/cypress/tests/datalake/filterNumericalStringProperties.spec.ts
b/ui/cypress/tests/datalake/filterNumericalStringProperties.spec.ts
new file mode 100644
index 0000000000..003e5c23c0
--- /dev/null
+++ b/ui/cypress/tests/datalake/filterNumericalStringProperties.spec.ts
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { DataLakeUtils } from '../../support/utils/datalake/DataLakeUtils';
+import { DataLakeWidgetTableUtils } from
'../../support/utils/datalake/DataLakeWidgetTableUtils';
+import { DataLakeFilterConfig } from
'../../support/model/DataLakeFilterConfig';
+import { AdapterBuilder } from '../../support/builder/AdapterBuilder';
+import { ConnectBtns } from '../../support/utils/connect/ConnectBtns';
+import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { FileManagementUtils } from '../../support/utils/FileManagementUtils';
+
+describe('Validate that filter works for numerical dimension property', () => {
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+
+ FileManagementUtils.addFile(
+ 'datalake/filterNumericalStringProperties.csv',
+ );
+ const adapterInput = AdapterBuilder.create('File_Stream')
+ .setName('Test Adapter')
+ .setTimestampProperty('timestamp')
+ .addDimensionProperty('dimensionKey')
+ .setStoreInDataLake()
+ .setFormat('csv')
+ .addFormatInput('input', ConnectBtns.csvDelimiter(), ';')
+ .addFormatInput('checkbox', ConnectBtns.csvHeader(), 'check')
+ .build();
+ ConnectUtils.testAdapter(adapterInput);
+ });
+
+ it('Perform Test', () => {
+ DataLakeUtils.goToDatalake();
+ DataLakeUtils.createAndEditDataView();
+
+ // create table widget and select time range
+ const startDate = new Date(1737029442000);
+ const endDate = new Date(1742220659000);
+
+ DataLakeUtils.clickOrderBy('descending');
+
+ DataLakeUtils.openVisualizationConfig();
+ DataLakeUtils.selectVisualizationType('Table');
+ DataLakeUtils.selectTimeRange(startDate, endDate);
+ cy.wait(1000);
+
+ // validate data in table
+ DataLakeWidgetTableUtils.checkAmountOfRows(2);
+
+ // select filter for tag
+ DataLakeUtils.selectDataConfig();
+ var filterConfig = new DataLakeFilterConfig('dimensionKey', '1.0',
'=');
+ DataLakeUtils.dataConfigAddFilter(filterConfig);
+
+ // validate data in table is filtered
+ DataLakeWidgetTableUtils.checkAmountOfRows(1);
+
+ // remove filter
+ DataLakeUtils.dataConfigRemoveFilter();
+
+ DataLakeUtils.selectDataConfig();
+
+ filterConfig = new DataLakeFilterConfig('v1', '20', '=');
+ DataLakeUtils.dataConfigAddFilter(filterConfig);
+
+ // validate data in table is filtered
+ DataLakeWidgetTableUtils.checkAmountOfRows(1);
+
+ // remove filter
+ DataLakeUtils.dataConfigRemoveFilter();
+
+ // validate data again
+ DataLakeWidgetTableUtils.checkAmountOfRows(2);
+ });
+});
diff --git a/ui/cypress/tests/datalake/missingDataInDataLake.spec.ts
b/ui/cypress/tests/datalake/missingDataInDataLake.spec.ts
index f547c61448..77e57710b1 100644
--- a/ui/cypress/tests/datalake/missingDataInDataLake.spec.ts
+++ b/ui/cypress/tests/datalake/missingDataInDataLake.spec.ts
@@ -34,13 +34,13 @@ describe('Test missing properties in data lake', () => {
it('Test table with missing properties', () => {
DataLakeUtils.addDataViewAndTableWidget(dataViewName, 'Persist');
- DataLakeWidgetTableUtils.checkRows(5);
+ DataLakeWidgetTableUtils.checkAmountOfRows(5);
DataLakeUtils.selectDataConfig();
cy.dataCy('data-explorer-ignore-missing-values-checkbox')
.children()
.click();
- DataLakeWidgetTableUtils.checkRows(3);
+ DataLakeWidgetTableUtils.checkAmountOfRows(3);
});
});
diff --git a/ui/cypress/tests/datalake/timeOrderDataView.spec.ts
b/ui/cypress/tests/datalake/timeOrderDataView.spec.ts
index 1b1ee0f222..17a19ae70f 100644
--- a/ui/cypress/tests/datalake/timeOrderDataView.spec.ts
+++ b/ui/cypress/tests/datalake/timeOrderDataView.spec.ts
@@ -22,7 +22,7 @@ import { DataLakeBtns } from
'../../support/utils/datalake/DataLakeBtns';
describe('Test Time Order in Data Explorer', () => {
beforeEach('Setup Test', () => {
cy.initStreamPipesTest();
- DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false);
+ DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv');
DataLakeUtils.goToDatalake();
DataLakeUtils.createAndEditDataView();
});
diff --git a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
index 71f2356ed9..3688e7f789 100644
--- a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
+++ b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
@@ -46,7 +46,7 @@ describe('Test Time Range Selectors in Data Explorer', () => {
before('Setup Tests', () => {
cy.initStreamPipesTest();
- DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false);
+ DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv');
});
it('Perform Test', () => {
diff --git a/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
b/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
index 911cdc6b5a..3b9eb2f858 100644
--- a/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
+++ b/ui/cypress/tests/datalake/widgetDataConfiguration.smoke.spec.ts
@@ -33,7 +33,7 @@ describe('Test Table View in Data Explorer', () => {
DataLakeUtils.addDataViewAndTableWidget('NewWidget', 'Persist');
// Validate that X lines are available
- DataLakeWidgetTableUtils.checkRows(10);
+ DataLakeWidgetTableUtils.checkAmountOfRows(10);
// Go back to data configuration
DataLakeUtils.selectDataConfig();
@@ -44,26 +44,30 @@ describe('Test Table View in Data Explorer', () => {
// Test number
let filterConfig = new DataLakeFilterConfig('randomnumber', '22', '=');
DataLakeUtils.dataConfigAddFilter(filterConfig);
- DataLakeWidgetTableUtils.checkRows(2);
+ DataLakeWidgetTableUtils.checkAmountOfRows(2);
+ DataLakeUtils.validateFilterOptions(['=', '<', '<=', '>=', '>', '!=']);
DataLakeUtils.dataConfigRemoveFilter();
- DataLakeWidgetTableUtils.checkRows(10);
+ DataLakeWidgetTableUtils.checkAmountOfRows(10);
// Test number greater then
filterConfig = new DataLakeFilterConfig('randomnumber', '50', '>');
DataLakeUtils.dataConfigAddFilter(filterConfig);
- DataLakeWidgetTableUtils.checkRows(5);
+ DataLakeWidgetTableUtils.checkAmountOfRows(5);
+ DataLakeUtils.validateFilterOptions(['=', '<', '<=', '>=', '>', '!=']);
DataLakeUtils.dataConfigRemoveFilter();
// Test number smaller then
filterConfig = new DataLakeFilterConfig('randomnumber', '50', '<');
DataLakeUtils.dataConfigAddFilter(filterConfig);
- DataLakeWidgetTableUtils.checkRows(5);
+ DataLakeWidgetTableUtils.checkAmountOfRows(5);
DataLakeUtils.dataConfigRemoveFilter();
// Test boolean
filterConfig = new DataLakeFilterConfig('randombool', 'true', '=');
DataLakeUtils.dataConfigAddFilter(filterConfig);
- DataLakeWidgetTableUtils.checkRows(6);
+ DataLakeWidgetTableUtils.checkAmountOfRows(6);
+ DataLakeUtils.validateFilterOptions(['=', '!=']);
+ DataLakeUtils.validateAutoCompleteOptions(['true', 'false']);
DataLakeUtils.dataConfigRemoveFilter();
// Test string & if filter is persisted correctly
@@ -71,10 +75,12 @@ describe('Test Table View in Data Explorer', () => {
DataLakeUtils.checkIfFilterIsSet(0);
DataLakeUtils.dataConfigAddFilter(filterConfig);
DataLakeUtils.checkIfFilterIsSet(1);
- DataLakeWidgetTableUtils.checkRows(4);
+ DataLakeWidgetTableUtils.checkAmountOfRows(4);
+ DataLakeUtils.validateFilterOptions(['=', '!=']);
+ DataLakeUtils.validateAutoCompleteOptions(['a', 'b', 'c']);
DataLakeUtils.saveAndReEditWidget('NewWidget');
DataLakeUtils.checkIfFilterIsSet(1);
- DataLakeWidgetTableUtils.checkRows(4);
+ DataLakeWidgetTableUtils.checkAmountOfRows(4);
DataLakeUtils.dataConfigRemoveFilter();
/**
@@ -89,7 +95,7 @@ describe('Test Table View in Data Explorer', () => {
cy.dataCy('data-explorer-table-row-randomtext', { timeout: 10000 })
.first({ timeout: 10000 })
.contains('c', { timeout: 10000 });
- DataLakeWidgetTableUtils.checkRows(10);
+ DataLakeWidgetTableUtils.checkAmountOfRows(10);
DataLakeUtils.saveAndReEditWidget('NewWidget');
cy.dataCy('data-explorer-group-by-randomtext')
.find('input')
diff --git a/ui/cypress/tests/datalake/widgets/table.spec.ts
b/ui/cypress/tests/datalake/widgets/table.spec.ts
index 4648835e77..62c820f7b9 100644
--- a/ui/cypress/tests/datalake/widgets/table.spec.ts
+++ b/ui/cypress/tests/datalake/widgets/table.spec.ts
@@ -17,6 +17,7 @@
*/
import { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+import { DataLakeWidgetTableUtils } from
'../../../support/utils/datalake/DataLakeWidgetTableUtils';
describe('Test Table View in Data Explorer', () => {
beforeEach('Setup Test', () => {
@@ -27,8 +28,6 @@ describe('Test Table View in Data Explorer', () => {
DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Table');
// Check if table is displayed correctly
- cy.dataCy('data-explorer-table-row-timestamp', {
- timeout: 10000,
- }).should('have.length', 10);
+ DataLakeWidgetTableUtils.checkAmountOfRows(10);
});
});
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/escape-number-filter.service.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/escape-number-filter.service.ts
new file mode 100644
index 0000000000..78042aff72
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/escape-number-filter.service.ts
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class EscapeNumberFilterService {
+ // Method to remove enclosing double quotes
+ removeEnclosingQuotes(value: string): string {
+ return value?.replace(/^"|"$/g, '');
+ }
+
+ // Updates the filter value based on the field type and input value.
+ // Ensures that numeric values are wrapped in double quotes to prevent
parsing issues on the backend.
+ // This check is necessary because the filter value is transmitted as a
triple [field, operator, value],
+ // which causes the type information to be lost. Once the API is changed
to retain type information,
+ // this service can be removed.
+ escapeIfNumberValue(
+ filter: any,
+ value: string,
+ tagValues: Map<string, string[]>,
+ ): string {
+ const isTagValueKey = this.checkIfFilterOnTagValue(filter, tagValues);
+ const isNumericValue = this.checkIfNumericalValue(value);
+
+ if (isNumericValue && (isTagValueKey || !filter?.field?.numeric)) {
+ return `"${value}"`;
+ } else {
+ return value;
+ }
+ }
+
+ private checkIfFilterOnTagValue(
+ filter: any,
+ tagValues: Map<string, string[]>,
+ ): boolean {
+ return (
+ tagValues.has(filter?.field?.runtimeName) &&
+ tagValues.get(filter.field.runtimeName)?.length > 0
+ );
+ }
+
+ private checkIfNumericalValue(value: string): boolean {
+ return !isNaN(Number(value));
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
new file mode 100644
index 0000000000..4401ad9ccf
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.html
@@ -0,0 +1,58 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<div fxFlex="100" fxLayout="column" class="form-field-small">
+ <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
+ <sp-filter-selection-panel-row-property-selection
+ [filter]="filter"
+ [possibleFields]="possibleFields"
+ (update)="updateParentComponent()"
+ >
+ </sp-filter-selection-panel-row-property-selection>
+
+ <sp-filter-selection-panel-row-operation-selection
+ [filter]="filter"
+ (update)="updateParentComponent()"
+ >
+ </sp-filter-selection-panel-row-operation-selection>
+
+ @if (!filter.field || !tagValues.has(filter.field.runtimeName)) {
+ <sp-filter-selection-panel-row-value-input
+ [filter]="filter"
+ (update)="updateParentComponent()"
+ >
+ </sp-filter-selection-panel-row-value-input>
+ } @else {
+ <sp-filter-selection-panel-row-value-autocomplete
+ [filter]="filter"
+ [tagValues]="tagValues"
+ (update)="updateParentComponent()"
+ >
+ </sp-filter-selection-panel-row-value-autocomplete>
+ }
+
+ <button
+ mat-icon-button
+ color="accent"
+ (click)="remove()"
+ data-cy="design-panel-data-settings-remove-filter"
+ >
+ <i class="material-icons">remove</i>
+ </button>
+ </div>
+</div>
diff --git a/ui/cypress/tests/datalake/widgets/table.spec.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
similarity index 51%
copy from ui/cypress/tests/datalake/widgets/table.spec.ts
copy to
ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
index 4648835e77..9e7cc75810 100644
--- a/ui/cypress/tests/datalake/widgets/table.spec.ts
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component.ts
@@ -16,19 +16,36 @@
*
*/
-import { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
-
-describe('Test Table View in Data Explorer', () => {
- beforeEach('Setup Test', () => {
- DataLakeUtils.initDataLakeTests();
- });
-
- it('Perform Test', () => {
- DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Table');
-
- // Check if table is displayed correctly
- cy.dataCy('data-explorer-table-row-timestamp', {
- timeout: 10000,
- }).should('have.length', 10);
- });
-});
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { FieldConfig, SelectedFilter } from '@streampipes/platform-services';
+
+@Component({
+ selector: 'sp-filter-selection-panel-row',
+ templateUrl: './filter-selection-panel-row.component.html',
+})
+export class FilterSelectionPanelRowComponent {
+ @Input()
+ public filter: SelectedFilter;
+
+ @Input()
+ public possibleFields: FieldConfig[];
+
+ @Input()
+ public tagValues: Map<string, string[]>;
+
+ @Output()
+ public update = new EventEmitter<void>();
+
+ @Output()
+ public removeFilter = new EventEmitter<number>();
+
+ constructor() {}
+
+ updateParentComponent() {
+ this.update.emit();
+ }
+
+ remove() {
+ this.removeFilter.emit();
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.html
new file mode 100644
index 0000000000..3af11dd781
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.html
@@ -0,0 +1,46 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+<mat-form-field color="accent" class="w-80-px mr-5" appearance="outline">
+ <mat-select
+ [(value)]="filter.operator"
+ panelClass="form-field-small min-w-100"
+ (selectionChange)="updateParentComponent()"
+ data-cy="design-panel-data-settings-filter-operator"
+ >
+ <mat-option [value]="'='" data-cy="operator-=">
+ <span class="pipeline-name">=</span>
+ </mat-option>
+ @if (filter.field && filter.field['numeric']) {
+ <mat-option [value]="'<'" data-cy="operator-<">
+ <span class="pipeline-name"><</span>
+ </mat-option>
+ <mat-option [value]="'<='" data-cy="operator-<=">
+ <span class="pipeline-name"><=</span>
+ </mat-option>
+ <mat-option [value]="'>='" data-cy="operator->=">
+ <span class="pipeline-name">>=</span>
+ </mat-option>
+ <mat-option [value]="'>'" data-cy="operator->">
+ <span class="pipeline-name">></span>
+ </mat-option>
+ }
+ <mat-option [value]="'!='" data-cy="operator-!=">
+ <span class="pipeline-name">!=</span>
+ </mat-option>
+ </mat-select>
+</mat-form-field>
diff --git a/ui/cypress/tests/datalake/widgets/table.spec.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
similarity index 59%
copy from ui/cypress/tests/datalake/widgets/table.spec.ts
copy to
ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
index 4648835e77..1ba9c766a1 100644
--- a/ui/cypress/tests/datalake/widgets/table.spec.ts
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component.ts
@@ -15,20 +15,22 @@
* limitations under the License.
*
*/
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
-import { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+@Component({
+ selector: 'sp-filter-selection-panel-row-operation-selection',
+ templateUrl:
+ './filter-selection-panel-row-operation-selection.component.html',
+})
+export class FilterSelectionPanelRowOperationSelectionComponent {
+ @Input()
+ public filter: SelectedFilter;
-describe('Test Table View in Data Explorer', () => {
- beforeEach('Setup Test', () => {
- DataLakeUtils.initDataLakeTests();
- });
+ @Output()
+ public update = new EventEmitter<void>();
- it('Perform Test', () => {
- DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Table');
-
- // Check if table is displayed correctly
- cy.dataCy('data-explorer-table-row-timestamp', {
- timeout: 10000,
- }).should('have.length', 10);
- });
-});
+ updateParentComponent() {
+ this.update.emit();
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
new file mode 100644
index 0000000000..23c7ba5c26
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.html
@@ -0,0 +1,31 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+<mat-form-field color="accent" class="w-140-px mr-5" appearance="outline">
+ <mat-label>Field</mat-label>
+ <mat-select
+ [(value)]="filter.field"
+ (selectionChange)="updateParentComponent()"
+ [compareWith]="compare"
+ panelClass="form-field-small min-w-200"
+ data-cy="design-panel-data-settings-filter-field"
+ >
+ @for (field of possibleFields; track field) {
+ <mat-option [value]="field">{{ field.runtimeName }} </mat-option>
+ }
+ </mat-select>
+</mat-form-field>
diff --git a/ui/cypress/tests/datalake/widgets/table.spec.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
similarity index 51%
copy from ui/cypress/tests/datalake/widgets/table.spec.ts
copy to
ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
index 4648835e77..f1f874cfb3 100644
--- a/ui/cypress/tests/datalake/widgets/table.spec.ts
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component.ts
@@ -15,20 +15,29 @@
* limitations under the License.
*
*/
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { FieldConfig, SelectedFilter } from '@streampipes/platform-services';
-import { DataLakeUtils } from '../../../support/utils/datalake/DataLakeUtils';
+@Component({
+ selector: 'sp-filter-selection-panel-row-property-selection',
+ templateUrl:
+ './filter-selection-panel-row-property-selection.component.html',
+})
+export class FilterSelectionPanelRowPropertySelectionComponent {
+ @Input()
+ public filter: SelectedFilter;
-describe('Test Table View in Data Explorer', () => {
- beforeEach('Setup Test', () => {
- DataLakeUtils.initDataLakeTests();
- });
+ @Input()
+ public possibleFields: FieldConfig[];
- it('Perform Test', () => {
- DataLakeUtils.addDataViewAndWidget('view', 'Persist', 'Table');
+ @Output()
+ public update = new EventEmitter<void>();
- // Check if table is displayed correctly
- cy.dataCy('data-explorer-table-row-timestamp', {
- timeout: 10000,
- }).should('have.length', 10);
- });
-});
+ updateParentComponent() {
+ this.update.emit();
+ }
+
+ compare(available: FieldConfig, selected: FieldConfig) {
+ return available.runtimeName === selected.runtimeName;
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.html
new file mode 100644
index 0000000000..81ff27305b
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.html
@@ -0,0 +1,42 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+<mat-form-field color="accent" appearance="outline" class="w-140-px mr-5">
+ <input
+ type="text"
+ placeholder="Value"
+ aria-label="Number"
+ matInput
+ [(ngModel)]="value"
+ (change)="updateParentComponent()"
+ data-cy="design-panel-data-settings-filter-value"
+ [matAutocomplete]="auto"
+ />
+ <mat-autocomplete
+ #auto="matAutocomplete"
+ (optionSelected)="updateParentComponent()"
+ >
+ @for (option of tagValues.get(filter.field.runtimeName); track option)
{
+ <mat-option
+ [attr.data-cy]="'autocomplete-value-' + option"
+ [value]="option"
+ >
+ {{ option }}
+ </mat-option>
+ }
+ </mat-autocomplete>
+</mat-form-field>
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.ts
new file mode 100644
index 0000000000..aef2842318
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component.ts
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
+import { EscapeNumberFilterService } from '../escape-number-filter.service';
+
+@Component({
+ selector: 'sp-filter-selection-panel-row-value-autocomplete',
+ templateUrl:
+ './filter-selection-panel-row-value-autocomplete.component.html',
+})
+export class FilterSelectionPanelRowValueAutocompleteComponent
+ implements OnInit
+{
+ @Input()
+ public filter: SelectedFilter;
+
+ @Input()
+ public tagValues: Map<string, string[]>;
+
+ @Output()
+ public update = new EventEmitter<void>();
+
+ public value: string;
+
+ constructor(private escapeNumberFilterService: EscapeNumberFilterService)
{}
+
+ ngOnInit(): void {
+ this.value = this.escapeNumberFilterService.removeEnclosingQuotes(
+ this.filter.value,
+ );
+ }
+
+ updateParentComponent() {
+ this.filter.value = this.escapeNumberFilterService.escapeIfNumberValue(
+ this.filter,
+ this.value,
+ this.tagValues,
+ );
+ this.update.emit();
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.html
new file mode 100644
index 0000000000..8f7c507b79
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.html
@@ -0,0 +1,28 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+<mat-form-field color="accent" appearance="outline" class="w-140-px mr-5">
+ <input
+ type="text"
+ placeholder="Value"
+ aria-label="Number"
+ matInput
+ [(ngModel)]="value"
+ (change)="updateParentComponent()"
+ data-cy="design-panel-data-settings-filter-value"
+ />
+</mat-form-field>
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.ts
new file mode 100644
index 0000000000..2d6031672b
--- /dev/null
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component.ts
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { SelectedFilter } from '@streampipes/platform-services';
+import { EscapeNumberFilterService } from '../escape-number-filter.service';
+
+@Component({
+ selector: 'sp-filter-selection-panel-row-value-input',
+ templateUrl: './filter-selection-panel-row-value-input.component.html',
+})
+export class FilterSelectionPanelRowValueInputComponent implements OnInit {
+ @Input()
+ public filter: SelectedFilter;
+
+ // This is only required to correctly escape numbers
+ @Input()
+ public tagValues: Map<string, string[]>;
+
+ @Output()
+ public update = new EventEmitter<void>();
+
+ public value: string;
+
+ constructor(private escapeNumberFilterService: EscapeNumberFilterService)
{}
+
+ ngOnInit(): void {
+ this.value = this.escapeNumberFilterService.removeEnclosingQuotes(
+ this.filter.value,
+ );
+ }
+
+ updateParentComponent() {
+ this.filter.value = this.escapeNumberFilterService.escapeIfNumberValue(
+ this.filter,
+ this.value,
+ this.tagValues,
+ );
+ this.update.emit();
+ }
+}
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
index 2d5242eaf3..f2dd8b0d27 100644
---
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.html
@@ -31,134 +31,19 @@
</button>
</div>
<div fxLayout="column">
- <div
- *ngFor="
- let filter of sourceConfig.queryConfig.selectedFilters;
- let i = index
- "
- fxFlex="100"
- fxLayout="column"
- >
- <div fxFlex="100" fxLayout="column" class="form-field-small">
- <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
- <mat-form-field
- color="accent"
- class="w-140-px mr-5"
- appearance="outline"
- >
- <mat-label>Field</mat-label>
- <mat-select
- [(value)]="filter.field"
- (selectionChange)="updateWidget()"
- [compareWith]="compare"
- panelClass="form-field-small min-w-200"
- data-cy="design-panel-data-settings-filter-field"
- >
- <mat-option
- *ngFor="
- let field of
sourceConfig.queryConfig.fields
- "
- [value]="field"
- >{{ field.runtimeName }}
- </mat-option>
- </mat-select>
- </mat-form-field>
- <mat-form-field
- color="accent"
- class="w-80-px mr-5"
- appearance="outline"
- >
- <mat-select
- [(value)]="filter.operator"
- panelClass="form-field-small min-w-100"
- (selectionChange)="updateWidget()"
-
data-cy="design-panel-data-settings-filter-operator"
- >
- <mat-option [value]="'='">
- <span class="pipeline-name">=</span>
- </mat-option>
- <mat-option [value]="'<'">
- <span class="pipeline-name"><</span>
- </mat-option>
- <mat-option [value]="'<='">
- <span class="pipeline-name"><=</span>
- </mat-option>
- <mat-option [value]="'>='">
- <span class="pipeline-name">>=</span>
- </mat-option>
- <mat-option [value]="'>'">
- <span class="pipeline-name">></span>
- </mat-option>
- <mat-option [value]="'!='">
- <span class="pipeline-name">!=</span>
- </mat-option>
- </mat-select>
- </mat-form-field>
- <mat-form-field
- color="accent"
- appearance="outline"
- class="w-140-px mr-5"
- *ngIf="
- !filter.field ||
- !tagValues.has(filter.field.runtimeName)
- "
- >
- <mat-label>Value</mat-label>
- <input
- type="text"
- placeholder="Value"
- aria-label="Number"
- matInput
- [(ngModel)]="filter.value"
- (change)="updateWidget()"
- data-cy="design-panel-data-settings-filter-value"
- />
- </mat-form-field>
- <mat-form-field
- color="accent"
- appearance="outline"
- class="w-140-px mr-5"
- *ngIf="
- filter.field &&
- tagValues.has(filter.field.runtimeName)
- "
- >
- <input
- type="text"
- placeholder="Value"
- aria-label="Number"
- matInput
- [(ngModel)]="filter.value"
- (change)="updateWidget()"
- data-cy="design-panel-data-settings-filter-value"
- [matAutocomplete]="auto"
- />
- <mat-autocomplete
- #auto="matAutocomplete"
- (optionSelected)="updateWidget()"
- >
- <mat-option
- *ngFor="
- let value of tagValues.get(
- filter.field.runtimeName
- )
- "
- [value]="value"
- >
- {{ value }}
- </mat-option>
- </mat-autocomplete>
- </mat-form-field>
- <button
- mat-icon-button
- color="accent"
- (click)="remove(sourceConfig, i)"
- data-cy="design-panel-data-settings-remove-filter"
- >
- <i class="material-icons">remove</i>
- </button>
- </div>
- </div>
- </div>
+ @for (
+ filter of sourceConfig.queryConfig.selectedFilters;
+ track filter;
+ let i = $index
+ ) {
+ <sp-filter-selection-panel-row
+ [filter]="filter"
+ [possibleFields]="sourceConfig.queryConfig.fields"
+ [tagValues]="tagValues"
+ (update)="updateWidget()"
+ (removeFilter)="remove(i)"
+ >
+ </sp-filter-selection-panel-row>
+ }
</div>
</sp-configuration-box>
diff --git
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
index 042d2515e2..fc251508c5 100644
---
a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
+++
b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel.component.ts
@@ -19,7 +19,6 @@
import { Component, Input, OnInit } from '@angular/core';
import {
DatalakeRestService,
- FieldConfig,
SelectedFilter,
SourceConfig,
} from '@streampipes/platform-services';
@@ -88,8 +87,8 @@ export class FilterSelectionPanelComponent implements OnInit {
this.updateWidget();
}
- remove(sourceConfig: any, index: number) {
- sourceConfig.queryConfig.selectedFilters.splice(index, 1);
+ remove(index: number) {
+ this.sourceConfig.queryConfig.selectedFilters.splice(index, 1);
this.widgetConfigService.notify({
refreshData: true,
@@ -113,8 +112,4 @@ export class FilterSelectionPanelComponent implements
OnInit {
});
}
}
-
- compare(available: FieldConfig, selected: FieldConfig) {
- return available.runtimeName === selected.runtimeName;
- }
}
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts
b/ui/src/app/data-explorer/data-explorer.module.ts
index 32991008ed..f296389868 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -125,6 +125,11 @@ import { DataExplorerDataViewPreviewComponent } from
'./components/dashboard/das
import { DataExplorerDashboardToolbarComponent } from
'./components/dashboard/dashboard-toolbar/dashboard-toolbar.component';
import { OrderSelectionPanelComponent } from
'./components/data-view/data-view-designer-panel/data-settings/order-selection-panel/order-selection-panel.component';
import { GaugeWidgetConfigComponent } from
'./components/widgets/gauge/config/gauge-widget-config.component';
+import { FilterSelectionPanelRowComponent } from
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/filter-selection-panel-row.component';
+import { FilterSelectionPanelRowPropertySelectionComponent } from
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-property-selection/filter-selection-panel-row-property-selection.component';
+import { FilterSelectionPanelRowOperationSelectionComponent } from
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-operation-selection/filter-selection-panel-row-operation-selection.component';
+import { FilterSelectionPanelRowValueInputComponent } from
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input/filter-selection-panel-row-value-input.component';
+import { FilterSelectionPanelRowValueAutocompleteComponent } from
'./components/data-view/data-view-designer-panel/data-settings/filter-selection-panel/filter-selection-panel-row/panel-row-value-input-autocomplete/filter-selection-panel-row-value-autocomplete.component';
@NgModule({
imports: [
@@ -224,6 +229,7 @@ import { GaugeWidgetConfigComponent } from
'./components/widgets/gauge/config/ga
FieldSelectionPanelComponent,
FieldSelectionComponent,
FilterSelectionPanelComponent,
+ FilterSelectionPanelRowComponent,
GaugeWidgetConfigComponent,
GroupConfigurationComponent,
ImageWidgetComponent,
@@ -268,6 +274,10 @@ import { GaugeWidgetConfigComponent } from
'./components/widgets/gauge/config/ga
SpEchartsWidgetAppearanceConfigComponent,
SpTimeSeriesAppearanceConfigComponent,
SpDataZoomConfigComponent,
+ FilterSelectionPanelRowPropertySelectionComponent,
+ FilterSelectionPanelRowOperationSelectionComponent,
+ FilterSelectionPanelRowValueInputComponent,
+ FilterSelectionPanelRowValueAutocompleteComponent,
],
exports: [],
})