Skip to content

Commit

Permalink
Create time range quick access list for multiple time range types (#1…
Browse files Browse the repository at this point in the history
…5484)

* Create QuickAccessTimeRangeForm

* Add context for TimeRange Input

* Add summary and sortable list. Connect with BE

* Replace list. Add button to quick adding to the list

* fix title

* Adding type definitions.

* add filtrated list. Add presets to relative

* fix presets to relative

* Unify button naming.

* Unify positioning of preset value and description on configuration page.

* Make sure preset list items do not remount when chaning content.

* Remove not needed formik usage.

* Fix prop type error.

* fix timezone problem on update absoluter timereinge in configuration

* Only update presets on configuration page after submitting changes.

* fix TimeRangeInput test

* Add presets to other tabs

* Conversion from timerange options to timerange presets

* Migration instead of temporary solution. IDs introduced. All fields of timerange presets required. More detailed information on a reason of failed config save.

* ignore time limits when create a new timerange in configuration

* fix tsc

* fix test

* add test to RangePresetDropdown

* change icon and submit component in  TimeRangeAddToQuickListButton

* add test. check show/hide quick access button

* Improve accessibility

* add test for TimeRangeAddToQuickListButton

* Fixing attributes

* Fixing attributes

* fix issue with ariaLabel

* fix test issues

* Add QuickAccessTimeRangeForm test

* small improvements

* small improvements

* update telemetry

* Add changelog

* fix texting when no available presets

* refactoring

* refactoring

* Make quick access time range description required.

---------

Co-authored-by: Linus Pahl <[email protected]>
Co-authored-by: Lukasz Kaminski <[email protected]>
  • Loading branch information
3 people authored Jun 19, 2023
1 parent 68b716f commit e458db8
Show file tree
Hide file tree
Showing 38 changed files with 1,690 additions and 261 deletions.
4 changes: 4 additions & 0 deletions changelog/unreleased/issue-15426.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type = "added"
message = "Create time range quick access list for multiple time range types"

pulls = ["15484"]
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,27 @@
*/
package org.graylog2.indexer.searches.timerangepresets;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.graylog2.plugin.indexer.searches.timeranges.TimeRange;

public record TimerangePreset(@JsonProperty("timerange") TimeRange timeRange,
@JsonProperty("description") String description) {
import java.util.UUID;

public record TimerangePreset(
@JsonProperty(value = "id", required = true) String id,
@JsonProperty(value = "timerange", required = true) TimeRange timeRange,
@JsonProperty(value = "description", required = true) String description) {

@JsonCreator
public TimerangePreset(@JsonProperty(value = "id", required = true) String id,
@JsonProperty(value = "timerange", required = true) TimeRange timeRange,
@JsonProperty(value = "description", required = true) String description) {
this.id = id;
this.timeRange = timeRange;
this.description = description;
}

public TimerangePreset(TimeRange timeRange, String description) {
this(UUID.randomUUID().toString(), timeRange, description);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog2.indexer.searches.timerangepresets.conversion;

import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.joda.time.Period;

import java.util.function.Function;

public class PeriodToRelativeRangeConverter implements Function<Period, RelativeRange> {

@Override
public RelativeRange apply(final Period period) {
if (period != null) {
return RelativeRange.Builder.builder()
.from(period.toStandardSeconds().getSeconds())
.build();
} else {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog2.indexer.searches.timerangepresets.conversion;


import org.graylog2.indexer.searches.timerangepresets.TimerangePreset;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.joda.time.Period;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class TimerangeOptionsToTimerangePresetsConversion {

private final Function<Period, RelativeRange> periodConverter;

@Inject
public TimerangeOptionsToTimerangePresetsConversion(final PeriodToRelativeRangeConverter periodConverter) {
this.periodConverter = periodConverter;
}

public List<TimerangePreset> convert(final Map<Period, String> timerangeOptions) {
if (timerangeOptions == null) {
return List.of();
}
return timerangeOptions.entrySet()
.stream()
.map(entry -> new TimerangePreset(
periodConverter.apply(entry.getKey()),
entry.getValue())
)
.collect(Collectors.toList());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ protected void configure() {
addMigration(V20230220095500_MigrateStartPageObjectReferencesToGRNbyRemoval.class);
addMigration(V20230213160000_EncryptedInputConfigMigration.class);
addMigration(V20230210102500_UniqueUserMigration.class);
addMigration(V202305221200_MigrateTimerangeOptionsToTimerangePresets.class);
addMigration(V20230523160600_PopulateEventDefinitionState.class);
addMigration(V20230531135500_MigrateRemoveObsoleteItemsFromGrantsCollection.class);
addMigration(V20230601104500_AddSourcesPageV2.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog2.migrations;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.google.auto.value.AutoValue;
import org.graylog.autovalue.WithBeanGetter;
import org.graylog2.indexer.searches.SearchesClusterConfig;
import org.graylog2.indexer.searches.timerangepresets.TimerangePreset;
import org.graylog2.indexer.searches.timerangepresets.conversion.TimerangeOptionsToTimerangePresetsConversion;
import org.graylog2.plugin.cluster.ClusterConfigService;
import org.joda.time.Period;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class V202305221200_MigrateTimerangeOptionsToTimerangePresets extends Migration {

private static final Logger LOG = LoggerFactory.getLogger(V202305221200_MigrateTimerangeOptionsToTimerangePresets.class);

private final ClusterConfigService clusterConfigService;
private final TimerangeOptionsToTimerangePresetsConversion conversion;

@Inject
public V202305221200_MigrateTimerangeOptionsToTimerangePresets(final ClusterConfigService clusterConfigService,
final TimerangeOptionsToTimerangePresetsConversion conversion) {
this.clusterConfigService = clusterConfigService;
this.conversion = conversion;
}

@Override
public ZonedDateTime createdAt() {
return ZonedDateTime.parse("2023-05-22T12:00:00Z");
}

@Override
public void upgrade() {
if (clusterConfigService.get(V202305221200_MigrateTimerangeOptionsToTimerangePresets.MigrationCompleted.class) != null) {
LOG.debug("Migration already completed.");
return;
}

final SearchesClusterConfig searchesClusterConfig = clusterConfigService.get(SearchesClusterConfig.class);
if (searchesClusterConfig != null) {
final Map<Period, String> relativeTimerangeOptions = searchesClusterConfig.relativeTimerangeOptions();
if (relativeTimerangeOptions != null && !relativeTimerangeOptions.isEmpty()) {
final List<TimerangePreset> converted = conversion.convert(relativeTimerangeOptions);
List<TimerangePreset> quickAccessTimerangePresets = searchesClusterConfig.quickAccessTimerangePresets();
List<TimerangePreset> newQuickAccessTimerangePresets = new ArrayList<>();
if (quickAccessTimerangePresets != null) {
newQuickAccessTimerangePresets.addAll(quickAccessTimerangePresets);
}
newQuickAccessTimerangePresets.addAll(converted);

final SearchesClusterConfig newConfig = searchesClusterConfig
.toBuilder()
.quickAccessTimerangePresets(newQuickAccessTimerangePresets)
.build();
clusterConfigService.write(newConfig);
clusterConfigService.write(MigrationCompleted.create());
LOG.info("Migration created " + relativeTimerangeOptions.size() + " new entries in quickAccessTimerangePresets list, based on relativeTimerangeOptions list");
return;
}
}
LOG.info("Migration was not needed, no relativeTimerangeOptions data to move");
}

@JsonAutoDetect
@AutoValue
@WithBeanGetter
public static abstract class MigrationCompleted {

@JsonCreator
public static V202305221200_MigrateTimerangeOptionsToTimerangePresets.MigrationCompleted create() {
return new AutoValue_V202305221200_MigrateTimerangeOptionsToTimerangePresets_MigrationCompleted();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private Object parseConfigObject(String configClass, InputStream body, Class<?>
try {
object = objectMapper.readValue(body, cls);
} catch (Exception e) {
final String msg = "Couldn't parse cluster configuration \"" + configClass + "\".";
final String msg = "Couldn't parse cluster configuration \"" + configClass + "\". The problem was : " + e.getMessage();
LOG.error(msg, e);
throw new BadRequestException(msg);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog2.indexer.searches.timerangepresets.conversion;

import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.joda.time.Period;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;

class PeriodToRelativeRangeConverterTest {

private PeriodToRelativeRangeConverter converter;

@BeforeEach
void setUp() {
converter = new PeriodToRelativeRangeConverter();
}

@Test
void testReturnsNullOnNullInput() {
assertNull(converter.apply(null));
}

@Test
void testSecondConversion() {
final RelativeRange result = converter.apply(Period.seconds(5));
verifyResult(result, 5);
}

@Test
void testMinuteConversion() {
final RelativeRange result = converter.apply(Period.minutes(30));
verifyResult(result, 1800);
}

@Test
void testHourConversion() {
final RelativeRange result = converter.apply(Period.hours(2));
verifyResult(result, 7200);
}

@Test
void testDayConversion() {
final RelativeRange result = converter.apply(Period.days(2));
verifyResult(result, 172800);
}

@Test
void testMixedPeriodConversion() {
final RelativeRange result = converter.apply(Period.hours(1).plusMinutes(10).plusSeconds(7));
verifyResult(result, 4207);
}

private void verifyResult(final RelativeRange result, final int expectedFromField) {
assertThat(result)
.isNotNull()
.satisfies(range -> {
assertThat(range.range()).isEmpty();
assertThat(range.from()).isPresent().hasValue(expectedFromField);
});
}

}
Loading

0 comments on commit e458db8

Please sign in to comment.