Skip to content

Commit 5ad29d2

Browse files
committed
fix(): Adapt the usage of the new query filter for custom dashboard
close #7091
1 parent 811cc77 commit 5ad29d2

File tree

24 files changed

+254
-199
lines changed

24 files changed

+254
-199
lines changed

Diff for: cli/src/main/resources/application.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ micronaut:
22
application:
33
name: kestra
44
# Disable Micronaut Open Telemetry
5-
otel:
6-
enabled: false
5+
76
router:
87
static-resources:
98
swagger:

Diff for: core/src/main/java/io/kestra/core/models/Label.java

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.kestra.core.utils.MapUtils;
44
import jakarta.validation.constraints.NotNull;
55

6+
import java.util.HashMap;
67
import java.util.List;
78
import java.util.Map;
89
import java.util.stream.Collectors;
@@ -46,4 +47,19 @@ public static List<Label> from(final Map<String, String> map) {
4647
.map(entry -> new Label(entry.getKey(), entry.getValue()))
4748
.toList();
4849
}
50+
51+
/**
52+
* Static helper method for converting a label string to a map.
53+
*
54+
* @param label The label string.
55+
* @return The map of key/value labels.
56+
*/
57+
public static Map<String, String> from(String label) {
58+
Map<String, String> map = new HashMap<>();
59+
String[] keyValueArray = label.split(":");
60+
if (keyValueArray.length == 2) {
61+
map.put(keyValueArray[0], keyValueArray[1]);
62+
}
63+
return map;
64+
}
4965
}

Diff for: core/src/main/java/io/kestra/core/models/QueryFilter.java

+71-29
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package io.kestra.core.models;
22

3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonValue;
6+
import io.kestra.core.models.dashboards.filters.*;
37
import io.kestra.core.utils.Enums;
48
import lombok.Builder;
59

6-
import java.util.*;
10+
import java.util.Arrays;
11+
import java.util.List;
12+
import java.util.Map;
713
import java.util.function.Function;
814
import java.util.stream.Collectors;
915

@@ -13,33 +19,63 @@ public record QueryFilter(
1319
Op operation,
1420
Object value
1521
) {
16-
public enum Op {
17-
EQUALS("$eq"),
18-
NOT_EQUALS("$ne"),
19-
GREATER_THAN("$gte"),
20-
LESS_THAN("$lte"),
21-
IN("$in"),
22-
NOT_IN("$notIn"),
23-
STARTS_WITH("$startsWith"),
24-
ENDS_WITH("$endsWith"),
25-
CONTAINS("$contains"),
26-
REGEX("$regex");
27-
28-
private static final Map<String, Op> BY_VALUE = Arrays.stream(values())
29-
.collect(Collectors.toMap(Op::value, Function.identity()));
3022

31-
private final String value;
23+
@JsonCreator
24+
public QueryFilter(
25+
@JsonProperty("field") Field field,
26+
@JsonProperty("operation") Op operation,
27+
@JsonProperty("value") Object value
28+
) {
29+
this.field = field;
30+
this.operation = operation;
31+
this.value = value;
32+
}
3233

33-
Op(String value) {
34-
this.value = value;
35-
}
34+
public enum Op {
35+
EQUALS,
36+
NOT_EQUALS,
37+
GREATER_THAN,
38+
LESS_THAN,
39+
GREATER_THAN_OR_EQUAL_TO,
40+
LESS_THAN_OR_EQUAL_TO,
41+
IN,
42+
NOT_IN,
43+
STARTS_WITH,
44+
ENDS_WITH,
45+
CONTAINS,
46+
REGEX;
47+
}
3648

37-
public static Op fromString(String value) {
38-
return Enums.fromString(value, BY_VALUE, "operation");
39-
}
4049

41-
public String value() {
42-
return value;
50+
@SuppressWarnings("unchecked")
51+
public <T extends Enum<T>> AbstractFilter<T> toDashboardFilterBuilder(T field, Object value) {
52+
switch (this.operation) {
53+
case EQUALS:
54+
return EqualTo.<T>builder().field(field).value(value).build();
55+
case NOT_EQUALS:
56+
return NotEqualTo.<T>builder().field(field).value(value).build();
57+
case GREATER_THAN:
58+
return GreaterThan.<T>builder().field(field).value(value).build();
59+
case LESS_THAN:
60+
return LessThan.<T>builder().field(field).value(value).build();
61+
case GREATER_THAN_OR_EQUAL_TO:
62+
return GreaterThanOrEqualTo.<T>builder().field(field).value(value).build();
63+
case LESS_THAN_OR_EQUAL_TO:
64+
return LessThanOrEqualTo.<T>builder().field(field).value(value).build();
65+
case IN:
66+
return In.<T>builder().field(field).values((List<Object>) value).build();
67+
case NOT_IN:
68+
return NotIn.<T>builder().field(field).values((List<Object>) value).build();
69+
case STARTS_WITH:
70+
return StartsWith.<T>builder().field(field).value(value.toString()).build();
71+
case ENDS_WITH:
72+
return EndsWith.<T>builder().field(field).value(value.toString()).build();
73+
case CONTAINS:
74+
return Contains.<T>builder().field(field).value(value.toString()).build();
75+
case REGEX:
76+
return Regex.<T>builder().field(field).value(value.toString()).build();
77+
default:
78+
throw new IllegalArgumentException("Unsupported operation: " + this.operation);
4379
}
4480
}
4581

@@ -147,16 +183,17 @@ public List<Op> supportedOp() {
147183
this.value = value;
148184
}
149185

186+
@JsonCreator
150187
public static Field fromString(String value) {
151188
return Enums.fromString(value, BY_VALUE, "field");
152189
}
153190

191+
@JsonValue
154192
public String value() {
155193
return value;
156194
}
157195
}
158196

159-
160197
public enum Resource {
161198
FLOW {
162199
@Override
@@ -239,12 +276,17 @@ private static FieldOp toFieldInfo(Field field) {
239276
}
240277

241278
private static Operation toOperation(Op op) {
242-
return new Operation(op.name(), op.value());
279+
return new Operation(op.name(), op.name());
243280
}
244281
}
245282

246-
public record ResourceField(String name, List<FieldOp> fields) {}
247-
public record FieldOp(String name, String value, List<Operation> operations) {}
248-
public record Operation(String name, String value) {}
283+
public record ResourceField(String name, List<FieldOp> fields) {
284+
}
285+
286+
public record FieldOp(String name, String value, List<Operation> operations) {
287+
}
288+
289+
public record Operation(String name, String value) {
290+
}
249291

250292
}

Diff for: core/src/main/java/io/kestra/core/models/dashboards/DataFilter.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package io.kestra.core.models.dashboards;
22

3-
import com.fasterxml.jackson.annotation.JsonSubTypes;
4-
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5-
import com.fasterxml.jackson.annotation.JsonTypeName;
3+
import io.kestra.core.models.QueryFilter;
64
import io.kestra.core.models.annotations.Plugin;
75
import io.kestra.core.models.dashboards.filters.AbstractFilter;
86
import io.kestra.core.repositories.QueryBuilderInterface;
9-
import io.kestra.plugin.core.dashboard.data.Executions;
10-
import io.kestra.plugin.core.dashboard.data.Logs;
117
import jakarta.validation.constraints.NotBlank;
128
import jakarta.validation.constraints.NotNull;
139
import jakarta.validation.constraints.Pattern;
@@ -47,6 +43,6 @@ public Set<F> aggregationForbiddenFields() {
4743

4844
public abstract Class<? extends QueryBuilderInterface<F>> repositoryClass();
4945

50-
public abstract void setGlobalFilter(GlobalFilter globalFilter);
46+
public abstract void setGlobalFilter(List<QueryFilter> queryFilterList);
5147

5248
}

Diff for: core/src/main/java/io/kestra/plugin/core/dashboard/data/Executions.java

+33-15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import com.fasterxml.jackson.annotation.JsonInclude;
44
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import io.kestra.core.models.QueryFilter;
56
import io.kestra.core.models.annotations.Plugin;
67
import io.kestra.core.models.dashboards.ColumnDescriptor;
78
import io.kestra.core.models.dashboards.DataFilter;
8-
import io.kestra.core.models.dashboards.GlobalFilter;
9-
import io.kestra.core.models.dashboards.filters.*;
9+
import io.kestra.core.models.dashboards.filters.AbstractFilter;
10+
import io.kestra.core.models.dashboards.filters.Contains;
1011
import io.kestra.core.repositories.ExecutionRepositoryInterface;
1112
import io.kestra.core.repositories.QueryBuilderInterface;
1213
import io.kestra.core.validations.ExecutionsDataFilterValidation;
@@ -16,6 +17,7 @@
1617
import lombok.NoArgsConstructor;
1718
import lombok.experimental.SuperBuilder;
1819

20+
import java.time.ZonedDateTime;
1921
import java.util.ArrayList;
2022
import java.util.List;
2123

@@ -35,24 +37,40 @@ public Class<? extends QueryBuilderInterface<Executions.Fields>> repositoryClass
3537
}
3638

3739
@Override
38-
public void setGlobalFilter(GlobalFilter globalFilter) {
40+
public void setGlobalFilter(List<QueryFilter> filters) {
3941
List<AbstractFilter<Fields>> where = this.getWhere() != null ? new ArrayList<>(this.getWhere()) : new ArrayList<>();
4042

41-
if (globalFilter.getNamespace() != null) {
42-
where.removeIf(f -> f.getField().equals(Fields.NAMESPACE));
43-
where.add(EqualTo.<Executions.Fields>builder().field(Fields.NAMESPACE).value(globalFilter.getNamespace()).build());
43+
if (filters == null) {
44+
return;
4445
}
45-
if (globalFilter.getLabels() != null) {
46-
where.removeIf(f -> f.getField().equals(Fields.LABELS));
47-
where.add(Contains.<Executions.Fields>builder().field(Fields.LABELS).value(globalFilter.getLabels()).build());
46+
List<QueryFilter> namespaceFilters = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.NAMESPACE)).toList();
47+
if (!namespaceFilters.isEmpty()) {
48+
where.removeIf(filter -> filter.getField().equals(Executions.Fields.NAMESPACE));
49+
namespaceFilters.forEach(f -> {
50+
where.add(f.toDashboardFilterBuilder(Executions.Fields.NAMESPACE, f.value()));
51+
});
4852
}
49-
if (globalFilter.getStartDate() != null || globalFilter.getEndDate() != null) {
50-
where.removeIf(f -> f.getField().equals(Fields.START_DATE));
51-
if (globalFilter.getStartDate() != null) {
52-
where.add(GreaterThanOrEqualTo.<Executions.Fields>builder().field(Fields.START_DATE).value(globalFilter.getStartDate().toOffsetDateTime()).build());
53+
54+
List<QueryFilter> labelFilters = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.LABELS)).toList();
55+
if (!labelFilters.isEmpty()) {
56+
where.removeIf(filter -> filter.getField().equals(Fields.LABELS));
57+
labelFilters.forEach(f -> {
58+
where.add(Contains.<Executions.Fields>builder().field(Fields.LABELS).value(f.value()).build());
59+
});
60+
}
61+
62+
63+
QueryFilter startDate = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.START_DATE)).findFirst().orElse(null);
64+
QueryFilter endDate = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.END_DATE)).findFirst().orElse(null);
65+
66+
if (startDate != null || endDate != null) {
67+
if (startDate != null) {
68+
where.removeIf(f -> f.getField().equals(Fields.START_DATE));
69+
where.add(startDate.toDashboardFilterBuilder(Executions.Fields.START_DATE, (ZonedDateTime.parse(startDate.value().toString()).toInstant())));
5370
}
54-
if (globalFilter.getEndDate() != null) {
55-
where.add(LessThanOrEqualTo.<Executions.Fields>builder().field(Fields.START_DATE).value(globalFilter.getEndDate().toOffsetDateTime()).build());
71+
if (endDate != null) {
72+
where.removeIf(f -> f.getField().equals(Fields.END_DATE));
73+
where.add(endDate.toDashboardFilterBuilder(Fields.END_DATE, (ZonedDateTime.parse(endDate.value().toString()).toInstant())));
5674
}
5775
}
5876

Diff for: core/src/main/java/io/kestra/plugin/core/dashboard/data/Logs.java

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package io.kestra.plugin.core.dashboard.data;
22

33
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import io.kestra.core.models.QueryFilter;
45
import io.kestra.core.models.annotations.Plugin;
56
import io.kestra.core.models.dashboards.ColumnDescriptor;
67
import io.kestra.core.models.dashboards.DataFilter;
7-
import io.kestra.core.models.dashboards.GlobalFilter;
88
import io.kestra.core.models.dashboards.filters.AbstractFilter;
9-
import io.kestra.core.models.dashboards.filters.EqualTo;
10-
import io.kestra.core.models.dashboards.filters.GreaterThanOrEqualTo;
11-
import io.kestra.core.models.dashboards.filters.LessThanOrEqualTo;
129
import io.kestra.core.repositories.LogRepositoryInterface;
1310
import io.kestra.core.repositories.QueryBuilderInterface;
1411
import io.swagger.v3.oas.annotations.media.Schema;
@@ -17,6 +14,7 @@
1714
import lombok.NoArgsConstructor;
1815
import lombok.experimental.SuperBuilder;
1916

17+
import java.time.ZonedDateTime;
2018
import java.util.ArrayList;
2119
import java.util.List;
2220
import java.util.Set;
@@ -35,20 +33,31 @@ public Class<? extends QueryBuilderInterface<Logs.Fields>> repositoryClass() {
3533
}
3634

3735
@Override
38-
public void setGlobalFilter(GlobalFilter globalFilter) {
36+
public void setGlobalFilter(List<QueryFilter> filters) {
3937
List<AbstractFilter<Fields>> where = this.getWhere() != null ? new ArrayList<>(this.getWhere()) : new ArrayList<>();
4038

41-
if (globalFilter.getNamespace() != null) {
42-
where.removeIf(f -> f.getField().equals(Fields.NAMESPACE));
43-
where.add(EqualTo.<Fields>builder().field(Fields.NAMESPACE).value(globalFilter.getNamespace()).build());
39+
if (filters == null) {
40+
return;
4441
}
45-
if (globalFilter.getStartDate() != null || globalFilter.getEndDate() != null) {
42+
43+
List<QueryFilter> namespaceFilters = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.NAMESPACE)).toList();
44+
if (!namespaceFilters.isEmpty()) {
45+
where.removeIf(filter -> filter.getField().equals(Logs.Fields.NAMESPACE));
46+
namespaceFilters.forEach(f -> {
47+
where.add(f.toDashboardFilterBuilder(Logs.Fields.NAMESPACE, f.value()));
48+
});
49+
}
50+
51+
QueryFilter startDate = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.START_DATE)).findFirst().orElse(null);
52+
QueryFilter endDate = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.END_DATE)).findFirst().orElse(null);
53+
54+
if (startDate != null || endDate != null) {
4655
where.removeIf(f -> f.getField().equals(Fields.DATE));
47-
if (globalFilter.getStartDate() != null) {
48-
where.add(GreaterThanOrEqualTo.<Fields>builder().field(Fields.DATE).value(globalFilter.getStartDate().toInstant()).build());
56+
if (startDate != null) {
57+
where.add(startDate.toDashboardFilterBuilder(Fields.DATE, ZonedDateTime.parse(startDate.value().toString()).toInstant()));
4958
}
50-
if (globalFilter.getEndDate() != null) {
51-
where.add(LessThanOrEqualTo.<Fields>builder().field(Fields.DATE).value(globalFilter.getEndDate().toInstant()).build());
59+
if (endDate != null) {
60+
where.add(endDate.toDashboardFilterBuilder(Fields.DATE, (ZonedDateTime.parse(endDate.value().toString()).toInstant())));
5261
}
5362
}
5463

Diff for: core/src/main/java/io/kestra/plugin/core/dashboard/data/Metrics.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package io.kestra.plugin.core.dashboard.data;
22

33
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import io.kestra.core.models.QueryFilter;
45
import io.kestra.core.models.annotations.Plugin;
56
import io.kestra.core.models.dashboards.ColumnDescriptor;
67
import io.kestra.core.models.dashboards.DataFilter;
7-
import io.kestra.core.models.dashboards.GlobalFilter;
88
import io.kestra.core.models.dashboards.filters.AbstractFilter;
99
import io.kestra.core.models.dashboards.filters.EqualTo;
1010
import io.kestra.core.repositories.MetricRepositoryInterface;
@@ -32,12 +32,19 @@ public Class<? extends QueryBuilderInterface<Metrics.Fields>> repositoryClass()
3232
}
3333

3434
@Override
35-
public void setGlobalFilter(GlobalFilter globalFilter) {
35+
public void setGlobalFilter(List<QueryFilter> filters) {
3636
List<AbstractFilter<Fields>> where = this.getWhere() != null ? new ArrayList<>(this.getWhere()) : new ArrayList<>();
3737

38-
if (globalFilter.getNamespace() != null) {
39-
where.removeIf(f -> f.getField().equals(Fields.NAMESPACE));
40-
where.add(EqualTo.<Fields>builder().field(Fields.NAMESPACE).value(globalFilter.getNamespace()).build());
38+
if (filters == null) {
39+
return;
40+
}
41+
42+
List<QueryFilter> namespaceFilters = filters.stream().filter(f -> f.field().equals(QueryFilter.Field.NAMESPACE)).toList();
43+
if (!namespaceFilters.isEmpty()) {
44+
where.removeIf(filter -> filter.getField().equals(Metrics.Fields.NAMESPACE));
45+
namespaceFilters.forEach(f -> {
46+
where.add(EqualTo.<Metrics.Fields>builder().field(Metrics.Fields.NAMESPACE).value(f.value()).build());
47+
});
4148
}
4249

4350
this.setWhere(where);

0 commit comments

Comments
 (0)