Clear removed 'Field' values from Aggregation Event Definition conditions (#24221)

* convert cleared field values to null so backend handles them correctly

* add cl entry and test

* Backend fix

---------

Co-authored-by: Zack King <zack.king@graylog.com>
This commit is contained in:
Ryan Carroll
2025-11-17 16:15:48 -06:00
committed by GitHub
parent ee08121e61
commit 55fce407e1
4 changed files with 48 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
type = "fixed"
message = "Fixed issue in Aggregation event definitions where 'Field' value selections were not cleared correctly."
pulls = ["24221"]
issues = ["24007"]

View File

@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import org.graylog.plugins.views.search.searchtypes.pivot.SeriesSpec;
import javax.annotation.Nullable;
@@ -70,9 +71,13 @@ public abstract class Count implements SeriesSpec, HasOptionalField {
@JsonProperty
public abstract Builder id(String id);
@JsonProperty
public abstract Builder field(@Nullable String field);
@JsonProperty("field")
public Builder nonEmptyField(@Nullable String field) {
return field(Strings.emptyToNull(field));
}
abstract Optional<String> id();
abstract Optional<String> field();

View File

@@ -176,4 +176,38 @@ describe('NumberRefExpression', () => {
],
});
});
it('should send null when aggregation field is cleared', async () => {
const expression = {
expr: 'number-ref',
ref: 'avg-took_ms',
};
const initialSeries = { id: 'avg-took_ms', type: 'avg', field: 'took_ms' };
const definition = eventDefinition([initialSeries]);
const handleChange = jest.fn();
render(
<NumberRefExpression
eventDefinition={definition}
aggregationFunctions={aggregationFunctions}
expression={expression}
formattedFields={formattedFields}
onChange={handleChange}
renderLabel={false}
validation={{ errors: {} }}
/>,
);
const fieldSelect = await screen.findByRole('combobox', { name: /select field/i });
await userEvent.click(fieldSelect);
await userEvent.keyboard('{Backspace}');
expect(handleChange).toHaveBeenCalledWith({
conditions: { expr: 'number-ref', ref: 'avg-' },
series: [
{ field: 'took_ms', id: 'avg-took_ms', type: 'avg' },
{ field: null, id: 'avg-', type: 'avg' },
],
});
});
});

View File

@@ -105,7 +105,9 @@ const NumberRefExpression = ({
const handleAggregationFieldChange = useCallback(
(nextField) => {
handleFieldChange('field', nextField);
const normalizedField = nextField === '' ? null : nextField;
handleFieldChange('field', normalizedField);
},
[handleFieldChange],
);