diff --git a/pom.xml b/pom.xml
index 2a8b2fb..8b08366 100644
--- a/pom.xml
+++ b/pom.xml
@@ -141,6 +141,11 @@
hamcrest
test
+
+ org.assertj
+ assertj-core
+ test
+
com.powsybl
powsybl-iidm-test
diff --git a/src/main/java/org/gridsuite/filter/AbstractFilter.java b/src/main/java/org/gridsuite/filter/AbstractFilter.java
index 41942dd..b8457de 100644
--- a/src/main/java/org/gridsuite/filter/AbstractFilter.java
+++ b/src/main/java/org/gridsuite/filter/AbstractFilter.java
@@ -8,7 +8,9 @@
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import lombok.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -43,7 +45,7 @@ public abstract class AbstractFilter implements IFilterAttributes {
private UUID id;
- private Date modificationDate;
+ private Date modificationDate; // TODO use Instant like in servers (client not on same timezone than server)
private EquipmentType equipmentType;
diff --git a/src/main/java/org/gridsuite/filter/IFilterAttributes.java b/src/main/java/org/gridsuite/filter/IFilterAttributes.java
index d4799fb..b4d668e 100644
--- a/src/main/java/org/gridsuite/filter/IFilterAttributes.java
+++ b/src/main/java/org/gridsuite/filter/IFilterAttributes.java
@@ -10,13 +10,16 @@
import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.filter.utils.FilterType;
+import java.util.Date;
+import java.util.UUID;
+
/**
* @author Jacques Borsenberger
*/
public interface IFilterAttributes {
- java.util.UUID getId();
+ UUID getId();
- java.util.Date getModificationDate();
+ Date getModificationDate(); // TODO use Instant like in servers (client not on same timezone than server)
FilterType getType();
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/ExpertFilter.java b/src/main/java/org/gridsuite/filter/expertfilter/ExpertFilter.java
index 2a9b8f3..3bf4217 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/ExpertFilter.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/ExpertFilter.java
@@ -8,10 +8,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.iidm.network.TopologyKind;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.AbstractFilter;
import org.gridsuite.filter.expertfilter.expertrule.AbstractExpertRule;
@@ -24,11 +21,11 @@
/**
* @author Antoine Bouhours
*/
-@Getter
-@Setter
@SuperBuilder
@NoArgsConstructor
+@Data
@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
public class ExpertFilter extends AbstractFilter {
private AbstractExpertRule rules;
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/AbstractExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/AbstractExpertRule.java
index 72ccd07..ab046e0 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/AbstractExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/AbstractExpertRule.java
@@ -12,9 +12,8 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.powsybl.iidm.network.Identifiable;
import lombok.AllArgsConstructor;
-import lombok.Getter;
+import lombok.Data;
import lombok.NoArgsConstructor;
-import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -46,8 +45,7 @@
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@AllArgsConstructor
-@Getter
-@Setter
+@Data
@SuperBuilder
public abstract class AbstractExpertRule {
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/BooleanExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/BooleanExpertRule.java
index 2687960..16a1f83 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/BooleanExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/BooleanExpertRule.java
@@ -9,10 +9,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -30,8 +27,9 @@
*/
@AllArgsConstructor
@NoArgsConstructor
-@Getter
-@Setter
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class BooleanExpertRule extends AbstractExpertRule {
private Boolean value;
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/CombinatorExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/CombinatorExpertRule.java
index c869828..2c39f70 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/CombinatorExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/CombinatorExpertRule.java
@@ -9,7 +9,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
+import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -23,6 +26,9 @@
* @author Antoine Bouhours
*/
@NoArgsConstructor
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class CombinatorExpertRule extends AbstractExpertRule {
@Override
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/EnumExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/EnumExpertRule.java
index 332425e..7f0ac99 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/EnumExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/EnumExpertRule.java
@@ -9,7 +9,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
+import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -24,6 +27,9 @@
* @author Antoine Bouhours
*/
@NoArgsConstructor
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class EnumExpertRule extends StringExpertRule {
@Override
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/FilterUuidExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/FilterUuidExpertRule.java
index d551edd..4632ffe 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/FilterUuidExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/FilterUuidExpertRule.java
@@ -9,12 +9,15 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
+import lombok.ToString;
import lombok.experimental.SuperBuilder;
+import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
import org.gridsuite.filter.utils.expertfilter.DataType;
import org.gridsuite.filter.utils.expertfilter.ExpertFilterUtils;
-import org.gridsuite.filter.FilterLoader;
import java.util.Map;
import java.util.UUID;
@@ -25,6 +28,9 @@
* @author Franck Lecuyer
*/
@NoArgsConstructor
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class FilterUuidExpertRule extends StringExpertRule {
@Override
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/NumberExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/NumberExpertRule.java
index 72b8760..0a22199 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/NumberExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/NumberExpertRule.java
@@ -10,10 +10,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -31,8 +28,9 @@
*/
@AllArgsConstructor
@NoArgsConstructor
-@Getter
-@Setter
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class NumberExpertRule extends AbstractExpertRule {
private Double value;
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/PropertiesExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/PropertiesExpertRule.java
index 7c052e4..f729f02 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/PropertiesExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/PropertiesExpertRule.java
@@ -9,10 +9,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
@@ -30,8 +27,9 @@
*/
@AllArgsConstructor
@NoArgsConstructor
-@Getter
-@Setter
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class PropertiesExpertRule extends AbstractExpertRule {
private String propertyName;
@@ -64,12 +62,4 @@ public DataType getDataType() {
public String getStringValue() {
return this.getPropertyName();
}
-
- public String getPropertyName() {
- return this.propertyName;
- }
-
- public List getPropertyValues() {
- return this.propertyValues;
- }
}
diff --git a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/StringExpertRule.java b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/StringExpertRule.java
index 82feea3..adf3303 100644
--- a/src/main/java/org/gridsuite/filter/expertfilter/expertrule/StringExpertRule.java
+++ b/src/main/java/org/gridsuite/filter/expertfilter/expertrule/StringExpertRule.java
@@ -10,10 +10,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
import org.gridsuite.filter.FilterLoader;
@@ -34,8 +31,9 @@
*/
@AllArgsConstructor
@NoArgsConstructor
-@Getter
-@Setter
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
@SuperBuilder
public class StringExpertRule extends AbstractExpertRule {
private String value;
diff --git a/src/main/java/org/gridsuite/filter/globalfilter/AbstractGlobalFilterService.java b/src/main/java/org/gridsuite/filter/globalfilter/AbstractGlobalFilterService.java
new file mode 100644
index 0000000..6eeb68a
--- /dev/null
+++ b/src/main/java/org/gridsuite/filter/globalfilter/AbstractGlobalFilterService.java
@@ -0,0 +1,46 @@
+package org.gridsuite.filter.globalfilter;
+
+import com.powsybl.iidm.network.Network;
+import lombok.NonNull;
+import org.apache.commons.lang3.ObjectUtils;
+import org.gridsuite.filter.AbstractFilter;
+import org.gridsuite.filter.FilterLoader;
+import org.gridsuite.filter.utils.EquipmentType;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+public abstract class AbstractGlobalFilterService implements FilterLoader {
+ protected abstract Network getNetwork(@NonNull UUID networkUuid, @NonNull String variantId);
+
+ /** @see #getFilteredIds(UUID, String, GlobalFilter, List) */
+ protected List getFilteredIds(@NonNull final UUID networkUuid, @NonNull final String variantId,
+ @NonNull final GlobalFilter globalFilter, @NonNull final List equipmentTypes) {
+ return getFilteredIds(networkUuid, variantId, globalFilter, equipmentTypes, null);
+ }
+
+ /**
+ * Get filtered equipment IDs.
+ * @param networkUuid the network to load
+ * @param variantId the network variant to work on
+ * @param globalFilter the filter(s) to apply
+ * @param equipmentTypes the {@link EquipmentType equipment types} to filter
+ * @param genericFilters additional generic filters to apply
+ * @return the {@link List list} of {@link UUID IDs} of filtered {@link EquipmentType equipments}.
+ */
+ protected List getFilteredIds(@NonNull final UUID networkUuid, @NonNull final String variantId,
+ @NonNull final GlobalFilter globalFilter, @NonNull final List equipmentTypes,
+ @Nullable final List genericFilters) {
+ final Network network = getNetwork(networkUuid, variantId);
+ return GlobalFilterUtils.applyGlobalFilterOnNetwork(network, globalFilter, ObjectUtils.getIfNull(genericFilters, List::of), equipmentTypes, this)
+ // Filter equipments by type
+ .values()
+ .stream()
+ .filter(Objects::nonNull)
+ // Combine all results into one list
+ .flatMap(List::stream)
+ .toList();
+ }
+}
diff --git a/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilter.java b/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilter.java
new file mode 100644
index 0000000..981ce96
--- /dev/null
+++ b/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilter.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.filter.globalfilter;
+
+import com.powsybl.iidm.network.Country;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.FieldNameConstants;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author maissa Souissi
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@SuperBuilder
+@FieldNameConstants
+// TODO convert to record when loadflow-server and computation lib stop to extends it
+public class GlobalFilter {
+ private List nominalV;
+ private List countryCode;
+ private List genericFilter;
+ private Map> substationProperty;
+
+ /**
+ * @return {@code true} if all filter parameters are empty, else {@code false}.
+ */
+ public boolean isEmpty() {
+ return CollectionUtils.isEmpty(this.nominalV)
+ && CollectionUtils.isEmpty(this.countryCode)
+ && CollectionUtils.isEmpty(this.genericFilter)
+ && MapUtils.isEmpty(this.substationProperty)
+ && this.substationProperty.values().stream().allMatch(CollectionUtils::isEmpty);
+ }
+}
diff --git a/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilterUtils.java b/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilterUtils.java
new file mode 100644
index 0000000..7fba83f
--- /dev/null
+++ b/src/main/java/org/gridsuite/filter/globalfilter/GlobalFilterUtils.java
@@ -0,0 +1,214 @@
+package org.gridsuite.filter.globalfilter;
+
+import com.powsybl.iidm.network.Country;
+import com.powsybl.iidm.network.Identifiable;
+import com.powsybl.iidm.network.Network;
+import org.gridsuite.filter.AbstractFilter;
+import org.gridsuite.filter.FilterLoader;
+import org.gridsuite.filter.expertfilter.ExpertFilter;
+import org.gridsuite.filter.expertfilter.expertrule.*;
+import org.gridsuite.filter.utils.EquipmentType;
+import org.gridsuite.filter.utils.FiltersUtils;
+import org.gridsuite.filter.utils.TimeUtils;
+import org.gridsuite.filter.utils.UuidUtils;
+import org.gridsuite.filter.utils.expertfilter.CombinatorType;
+import org.gridsuite.filter.utils.expertfilter.ExpertFilterUtils;
+import org.gridsuite.filter.utils.expertfilter.FieldType;
+import org.gridsuite.filter.utils.expertfilter.OperatorType;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+import java.util.function.Predicate;
+
+public final class GlobalFilterUtils {
+ private GlobalFilterUtils() {
+ throw new IllegalCallerException("Utility class should not be instantiated");
+ }
+
+ @Nonnull
+ public static List getNominalVoltageFieldType(@Nonnull final EquipmentType equipmentType) {
+ return switch (equipmentType) {
+ case LINE, TWO_WINDINGS_TRANSFORMER -> List.of(FieldType.NOMINAL_VOLTAGE_1, FieldType.NOMINAL_VOLTAGE_2);
+ case VOLTAGE_LEVEL -> List.of(FieldType.NOMINAL_VOLTAGE);
+ default -> List.of();
+ };
+ }
+
+ /**
+ * Builds nominal voltage rules combining all relevant field types
+ * @see GlobalFilter#getNominalV()
+ */
+ @Nonnull
+ public static Optional buildNominalVoltageRules(
+ @Nonnull final List nominalVoltages, @Nonnull final EquipmentType equipmentType) {
+ final List fields = getNominalVoltageFieldType(equipmentType);
+ return ExpertFilterUtils.buildOrCombination(nominalVoltages.stream()
+ .filter(Predicate.not(String::isBlank))
+ .map(Double::valueOf)
+ .mapMulti((value, accumulator) -> {
+ for (final FieldType field : fields) {
+ accumulator.accept(NumberExpertRule.builder()
+ .value(value)
+ .field(field)
+ .operator(OperatorType.EQUALS)
+ .build());
+ }
+ }).toList());
+ }
+
+ @Nonnull
+ public static List getCountryCodeFieldType(@Nonnull final EquipmentType equipmentType) {
+ return switch (equipmentType) {
+ case VOLTAGE_LEVEL, TWO_WINDINGS_TRANSFORMER -> List.of(FieldType.COUNTRY);
+ case LINE -> List.of(FieldType.COUNTRY_1, FieldType.COUNTRY_2);
+ default -> List.of();
+ };
+ }
+
+ /**
+ * Builds country code rules combining all relevant field types
+ */
+ @Nonnull
+ public static Optional buildCountryCodeRules(
+ @Nonnull final List countryCodes, @Nonnull final EquipmentType equipmentType) {
+ final List fields = getCountryCodeFieldType(equipmentType);
+ return ExpertFilterUtils.buildOrCombination(countryCodes.stream()
+ .map(Country::name)
+ .mapMulti((countryCode, accumulator) -> {
+ for (final FieldType field : fields) {
+ accumulator.accept(EnumExpertRule.builder()
+ .value(countryCode)
+ .field(field)
+ .operator(OperatorType.EQUALS)
+ .build());
+ }
+ })
+ .toList());
+ }
+
+ @Nonnull
+ public static List getSubstationPropertiesFieldTypes(@Nullable final EquipmentType equipmentType) {
+ return equipmentType == EquipmentType.LINE
+ ? List.of(FieldType.SUBSTATION_PROPERTIES_1, FieldType.SUBSTATION_PROPERTIES_2)
+ : List.of(FieldType.SUBSTATION_PROPERTIES);
+ }
+
+ /**
+ * Builds substation property rules combining all relevant field types
+ */
+ @Nonnull
+ public static Optional buildSubstationPropertyRules(
+ @Nonnull final Map> properties, @Nonnull final EquipmentType equipmentType) {
+ final List fields = getSubstationPropertiesFieldTypes(equipmentType);
+ return ExpertFilterUtils.buildOrCombination(properties.entrySet()
+ .stream()
+ .mapMulti((entry, accumulator) -> {
+ for (final FieldType field : fields) {
+ accumulator.accept(PropertiesExpertRule.builder()
+ .combinator(CombinatorType.OR)
+ .operator(OperatorType.IN)
+ .field(field)
+ .propertyName(entry.getKey())
+ .propertyValues(entry.getValue())
+ .build());
+ }
+ })
+ .toList());
+ }
+
+ /**
+ * Builds expert filter from a {@link GlobalFilter global filter} for an {@link EquipmentType equipment type}.
+ */
+ @Nullable
+ public static ExpertFilter buildExpertFilter(@Nonnull final GlobalFilter globalFilter, @Nonnull final EquipmentType equipmentType) {
+ final List andRules = new ArrayList<>();
+
+ // Nominal voltage rules
+ if (globalFilter.getNominalV() != null) {
+ buildNominalVoltageRules(globalFilter.getNominalV(), equipmentType).ifPresent(andRules::add);
+ }
+
+ // Country code rules
+ if (globalFilter.getCountryCode() != null) {
+ buildCountryCodeRules(globalFilter.getCountryCode(), equipmentType).ifPresent(andRules::add);
+ }
+
+ // Substation property rules
+ if (globalFilter.getSubstationProperty() != null) {
+ buildSubstationPropertyRules(globalFilter.getSubstationProperty(), equipmentType).ifPresent(andRules::add);
+ }
+
+ return andRules.isEmpty() ? null : new ExpertFilter(UuidUtils.generateUUID(), TimeUtils.nowAsDate(), equipmentType,
+ CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(andRules).build());
+ }
+
+ @Nonnull
+ private static List filterNetwork(@Nonnull final AbstractFilter filter, @Nonnull final Network network, @Nonnull final FilterLoader filterLoader) {
+ return FiltersUtils.getIdentifiables(filter, network, filterLoader)
+ .stream()
+ .map(Identifiable::getId)
+ .toList();
+ }
+
+ /**
+ * Extracts {@link Identifiable#getId() equipment ID}s from a generic filter based on {@link EquipmentType equipment type}.
+ */
+ @Nonnull
+ public static List applyFilterOnNetwork(@Nonnull final AbstractFilter filter, @Nonnull final EquipmentType targetEquipmentType,
+ @Nonnull final Network network, @Nonnull final FilterLoader filterLoader) {
+ if (filter.getEquipmentType() == targetEquipmentType) {
+ return filterNetwork(filter, network, filterLoader);
+ } else if (filter.getEquipmentType() == EquipmentType.VOLTAGE_LEVEL) {
+ return filterNetwork(ExpertFilterUtils.buildExpertFilterWithVoltageLevelIdsCriteria(filter.getId(), targetEquipmentType), network, filterLoader);
+ }
+ return List.of();
+ }
+
+ /**
+ * Extracts filtered {@link Identifiable#getId() equipment ID}s by applying {@link ExpertFilter expert}
+ * and {@link AbstractFilter generic filter}s.
+ */
+ @Nonnull
+ public static List applyGlobalFilterOnNetwork(@Nonnull final Network network,
+ @Nonnull final GlobalFilter globalFilter, @Nonnull final List genericFilters,
+ @Nonnull final EquipmentType equipmentType, @Nonnull final FilterLoader filterLoader) {
+ List> allFilterResults = new ArrayList<>(1 + genericFilters.size());
+
+ // Extract IDs from expert filter
+ final ExpertFilter expertFilter = buildExpertFilter(globalFilter, equipmentType);
+ if (expertFilter != null) {
+ allFilterResults.add(filterNetwork(expertFilter, network, filterLoader));
+ }
+
+ // Extract IDs from generic filters
+ for (final AbstractFilter filter : genericFilters) {
+ final List filterResult = applyFilterOnNetwork(filter, equipmentType, network, filterLoader);
+ if (!filterResult.isEmpty()) {
+ allFilterResults.add(filterResult);
+ }
+ }
+
+ // Combine results with appropriate logic
+ // Expert filters use OR between them, generic filters use AND
+ return FiltersUtils.combineFilterResults(allFilterResults, !genericFilters.isEmpty());
+ }
+
+ /**
+ * Filters equipments by {@link EquipmentType type}
+ * @return map of {@link Identifiable#getId() equipment ID}s grouped by {@link EquipmentType equipment type}
+ */
+ @Nonnull
+ public static Map> applyGlobalFilterOnNetwork(@Nonnull final Network network,
+ @Nonnull final GlobalFilter globalFilter, @Nonnull final List genericFilters,
+ @Nonnull final List equipmentTypes, @Nonnull final FilterLoader filterLoader) {
+ Map> result = new EnumMap<>(EquipmentType.class);
+ for (final EquipmentType equipmentType : equipmentTypes) {
+ final List filteredIds = applyGlobalFilterOnNetwork(network, globalFilter, genericFilters, equipmentType, filterLoader);
+ if (!filteredIds.isEmpty()) {
+ result.put(equipmentType, filteredIds);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/gridsuite/filter/identifierlistfilter/IdentifierListFilter.java b/src/main/java/org/gridsuite/filter/identifierlistfilter/IdentifierListFilter.java
index 1f32490..eb49f4e 100644
--- a/src/main/java/org/gridsuite/filter/identifierlistfilter/IdentifierListFilter.java
+++ b/src/main/java/org/gridsuite/filter/identifierlistfilter/IdentifierListFilter.java
@@ -9,10 +9,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.iidm.network.IdentifiableType;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.AbstractFilter;
import org.gridsuite.filter.utils.EquipmentType;
@@ -20,16 +17,15 @@
import java.util.*;
-
/**
* @author Seddik Yengui
*/
-@NoArgsConstructor
-@Getter
-@Setter
@SuperBuilder
+@NoArgsConstructor
+@Data
@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
public class IdentifierListFilter extends AbstractFilter {
private List filterEquipmentsAttributes;
diff --git a/src/main/java/org/gridsuite/filter/utils/FiltersUtils.java b/src/main/java/org/gridsuite/filter/utils/FiltersUtils.java
index 77255fe..191705e 100644
--- a/src/main/java/org/gridsuite/filter/utils/FiltersUtils.java
+++ b/src/main/java/org/gridsuite/filter/utils/FiltersUtils.java
@@ -14,6 +14,8 @@
import org.gridsuite.filter.identifierlistfilter.IdentifierListFilter;
import org.gridsuite.filter.identifierlistfilter.IdentifierListFilterEquipmentAttributes;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -237,4 +239,30 @@ public static List> getIdentifiables(AbstractFilter filter, Netw
case SUBSTATION -> getSubstationList(network, filter, filterLoader);
};
}
+
+ /**
+ * Combines multiple filter results using AND or OR logic.
+ */
+ @Nonnull
+ public static List combineFilterResults(@Nullable final List> filterResults, final boolean useAndLogic) {
+ if (filterResults == null || filterResults.isEmpty()) {
+ return List.of();
+ }
+ if (filterResults.size() == 1) {
+ return filterResults.getFirst();
+ }
+ if (useAndLogic) {
+ // Intersection of all results
+ Set result = new HashSet<>(filterResults.getFirst());
+ for (int i = 1; i < filterResults.size(); i++) {
+ result.retainAll(filterResults.get(i));
+ }
+ return new ArrayList<>(result);
+ } else {
+ // Union of all results
+ Set result = new HashSet<>();
+ filterResults.forEach(result::addAll);
+ return new ArrayList<>(result);
+ }
+ }
}
diff --git a/src/main/java/org/gridsuite/filter/utils/TimeUtils.java b/src/main/java/org/gridsuite/filter/utils/TimeUtils.java
new file mode 100644
index 0000000..e1662ca
--- /dev/null
+++ b/src/main/java/org/gridsuite/filter/utils/TimeUtils.java
@@ -0,0 +1,32 @@
+package org.gridsuite.filter.utils;
+
+import com.google.common.annotations.VisibleForTesting;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Date;
+
+/**
+ * Utility class for datetime to permit during tests to mock time related operations.
+ * @apiNote This class is to permit tests to intercept date/time
+ */
+
+public final class TimeUtils {
+ private TimeUtils() {
+ throw new AssertionError("Utility class should not be instantiated");
+ }
+
+ @Setter(onMethod_ = {@VisibleForTesting})
+ @Getter
+ private static Clock clock = Clock.systemUTC();
+
+ public static Instant now() {
+ return Instant.now(clock);
+ }
+
+ public static Date nowAsDate() {
+ return Date.from(now());
+ }
+}
diff --git a/src/main/java/org/gridsuite/filter/utils/UuidUtils.java b/src/main/java/org/gridsuite/filter/utils/UuidUtils.java
new file mode 100644
index 0000000..4419e4c
--- /dev/null
+++ b/src/main/java/org/gridsuite/filter/utils/UuidUtils.java
@@ -0,0 +1,88 @@
+package org.gridsuite.filter.utils;
+
+import com.google.common.annotations.VisibleForTesting;
+import lombok.Setter;
+
+import java.util.UUID;
+import java.util.function.Supplier;
+
+/**
+ * Utility class for UUID to permit during tests to mock {@link UUID} generation.
+ * @apiNote This class is to permit tests to intercept {@link UUID} generation
+ */
+public final class UuidUtils {
+ private UuidUtils() {
+ throw new AssertionError("Utility class should not be instantiated");
+ }
+
+ @Setter(onMethod_ = {@VisibleForTesting})
+ private static Supplier uuidSupplier = UUID::randomUUID;
+
+ public static UUID generateUUID() {
+ return uuidSupplier.get();
+ }
+
+ /**
+ * Like {@link UUID#randomUUID()} generate UUID v4 variant 2, but not random.
+ * This utility function is intended to be used in tests to have predictable UUID.
+ * Some information on UUID:
+ *
+ * textual format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
+ * bytes: 0 1 2 3 4 5 6 7 8 9 10 ↓12 ↓14 ↓
+ * 11 13 15
+ * size: 4b-2b-2b-2b-6b
+ *
+ * Where
+ * - M = version field (4 bits).
+ * - N = variant field (3 bits).
+ * - All other bits = random (for v4).
+ *
+ * UUID bytes structure:
+ *
+ * /-------mostSigBits-------\ /--------leastSigBits---------\
+ * b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15
+ * |-----------| |-----| |-----| |-----| |-----------------------|
+ * random random v4+random variant+random random
+ * b6 = [0100 LLLL] ⇒ version 4
+ * b8 = [10xL LLLL] → [1000 LLLL] ⇒ RFC 4122 / IETF / Leach–Salz variant
+ *
+ *
+ * @param b0to3 the bits from b0 to b3
+ * @param b4to5 the bits from b4 and b5
+ * @param b7 the bits of b7
+ * @param nibblesB6lowB8low the upper nibble is for the lower half of b6
+ * and the lower nibble is for the lower half of b8
+ * @param b9 the bits of b9
+ * @param b10to11 the bits for b10 and b11
+ * @param b12to15 the bits for b12 to b15
+ * @return a UUID v4 in variant 2 with the bits passed
+ * @implNote Bits 5–4 of b8 are left to {@code 0} for simplicity,
+ * so the UUID is of the form {@code xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx}.
+ */
+ // regexp: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-8[0-9a-f]{3}-[0-9a-f]{12}$
+ @VisibleForTesting
+ public static UUID createUUID(final int b0to3, final short b4to5, final byte b7,
+ final byte nibblesB6lowB8low,
+ final byte b9, final short b10to11, final int b12to15) {
+ return new UUID(
+ // Construct mostSigBits directly
+ (b0to3 & 0xFFFFFFFFL) << 32
+ | (b4to5 & 0xFFFFL) << 16
+ | (0x40L | ((nibblesB6lowB8low >>> 4) & 0x0FL)) << 8 // version 4
+ | (b7 & 0xFFL),
+ // Construct leastSigBits directly
+ (0x80L | (nibblesB6lowB8low & 0xFL)) << 56 // variant 2
+ | (b9 & 0xFFL) << 48
+ | (b10to11 & 0xFFFFL) << 32
+ | (b12to15 & 0xFFFFFFFFL)
+ );
+ }
+
+ /**
+ * @see #createUUID(int, short, byte, byte, byte, short, int)
+ */
+ @VisibleForTesting
+ public static UUID createUUID(final int b12to15) {
+ return createUUID(0, (short) 0, (byte) 0, (byte) 0, (byte) 0, (short) 0, b12to15);
+ }
+}
diff --git a/src/main/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtils.java b/src/main/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtils.java
index c9a22ec..db70edd 100644
--- a/src/main/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtils.java
+++ b/src/main/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtils.java
@@ -13,10 +13,13 @@
import com.powsybl.iidm.network.extensions.StandbyAutomaton;
import org.apache.commons.collections4.CollectionUtils;
import org.gridsuite.filter.FilterLoader;
+import org.gridsuite.filter.expertfilter.ExpertFilter;
+import org.gridsuite.filter.expertfilter.expertrule.AbstractExpertRule;
+import org.gridsuite.filter.expertfilter.expertrule.CombinatorExpertRule;
+import org.gridsuite.filter.expertfilter.expertrule.FilterUuidExpertRule;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
-import org.gridsuite.filter.utils.FilterServiceUtils;
-import org.gridsuite.filter.utils.RegulationType;
+import org.gridsuite.filter.utils.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -614,7 +617,7 @@ public static List getFilterEquipments(Network network, Set FilterCycleDetector.checkNoCycle(filter, filterLoader));
List filterEquipments = FilterServiceUtils.getFilterEquipmentsFromUuid(network, uuid, filterLoader);
- cachedUuidFilters.put(uuid, !CollectionUtils.isEmpty(filterEquipments) ? filterEquipments.getFirst() : null);
+ cachedUuidFilters.put(uuid, CollectionUtils.isNotEmpty(filterEquipments) ? filterEquipments.getFirst() : null);
res.addAll(filterEquipments);
}
});
@@ -626,4 +629,31 @@ public static boolean isPartOf(Network network, String value, Set uuids,
return equipments.stream().flatMap(e -> e.getIdentifiableAttributes().stream()
.map(IdentifiableAttributes::getId)).collect(Collectors.toSet()).contains(value);
}
+
+ /**
+ * Build an "OR" rule from the rules passed.
+ * @param rules the rule(s) to be applied
+ * @return {@link Optional#empty() Empty} if no rule is passed,
+ * the {@link AbstractExpertRule rule} if the list has only 1 rule inside,
+ * otherwise an {@link CombinatorExpertRule OR combinator} with the rules.
+ */
+ @Nonnull
+ public static Optional buildOrCombination(@Nullable final List rules) {
+ if (rules == null || rules.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.of(rules.size() > 1 ? CombinatorExpertRule.builder().combinator(CombinatorType.OR).rules(rules).build() : rules.getFirst());
+ }
+
+ /**
+ * Builds expert filter with {@link VoltageLevel voltage level} IDs criteria.
+ */
+ @Nonnull
+ public static ExpertFilter buildExpertFilterWithVoltageLevelIdsCriteria(@Nonnull final UUID filterUuid, @Nonnull final EquipmentType equipmentType) {
+ return new ExpertFilter(UuidUtils.generateUUID(), TimeUtils.nowAsDate(), equipmentType,
+ CombinatorExpertRule.builder().combinator(CombinatorType.OR).rules(List.of(
+ FilterUuidExpertRule.builder().operator(OperatorType.IS_PART_OF).field(FieldType.VOLTAGE_LEVEL_ID_1).values(Set.of(filterUuid.toString())).build(),
+ FilterUuidExpertRule.builder().operator(OperatorType.IS_PART_OF).field(FieldType.VOLTAGE_LEVEL_ID_2).values(Set.of(filterUuid.toString())).build()
+ )).build());
+ }
}
diff --git a/src/test/java/org/gridsuite/filter/globalfilter/GlobalFilterUtilsTest.java b/src/test/java/org/gridsuite/filter/globalfilter/GlobalFilterUtilsTest.java
new file mode 100644
index 0000000..f6fcded
--- /dev/null
+++ b/src/test/java/org/gridsuite/filter/globalfilter/GlobalFilterUtilsTest.java
@@ -0,0 +1,355 @@
+package org.gridsuite.filter.globalfilter;
+
+import com.powsybl.iidm.network.Country;
+import com.powsybl.iidm.network.Identifiable;
+import com.powsybl.iidm.network.Network;
+import org.assertj.core.api.*;
+import org.gridsuite.filter.AbstractFilter;
+import org.gridsuite.filter.FilterLoader;
+import org.gridsuite.filter.expertfilter.ExpertFilter;
+import org.gridsuite.filter.expertfilter.expertrule.*;
+import org.gridsuite.filter.utils.EquipmentType;
+import org.gridsuite.filter.utils.FiltersUtils;
+import org.gridsuite.filter.utils.UuidUtils;
+import org.gridsuite.filter.utils.expertfilter.CombinatorType;
+import org.gridsuite.filter.utils.expertfilter.FieldType;
+import org.gridsuite.filter.utils.expertfilter.OperatorType;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+/* Methods dependencies:
+ * applyGlobalFilterOnNetwork
+ * ↳ applyGlobalFilterOnNetwork
+ * ↳ applyFilterOnNetwork
+ * ⇃ ↳ filterNetwork
+ * ↳ buildExpertFilter
+ * ↳ buildNominalVoltageRules
+ * ⇃ ↳ getNominalVoltageFieldType
+ * ↳ buildCountryCodeRules
+ * ⇃ ↳ getCountryCodeFieldType
+ * ↳ buildSubstationPropertyRules
+ * ↳ getSubstationPropertiesFieldTypes
+ */
+class GlobalFilterUtilsTest implements WithAssertions {
+ private static void testVariableOrCombinationRules(final Optional result, final int inputSize,
+ final Class ruleClass, final ThrowingConsumer[] singleRuleAsserts,
+ final Iterable multiAssertElements) {
+ final OptionalAssert assertion = Assertions.assertThat(result).as("result");
+ if (inputSize <= 0) {
+ assertion.isEmpty();
+ } else {
+ assertion.isPresent();
+ if (inputSize == 1) {
+ assertion.get(InstanceOfAssertFactories.type(ruleClass))
+ .as("expert rule")
+ .satisfies(singleRuleAsserts);
+ } else {
+ assertion.get(InstanceOfAssertFactories.type(CombinatorExpertRule.class))
+ .as("combinator expert rule")
+ .satisfies(cer -> Assertions.assertThat(cer.getCombinator()).as("combinator").isEqualTo(CombinatorType.OR))
+ .extracting(CombinatorExpertRule::getRules, InstanceOfAssertFactories.list(AbstractExpertRule.class)).as("expert rules")
+ .containsExactlyInAnyOrderElementsOf(multiAssertElements);
+ }
+ }
+ }
+
+ /** Trick Java to create generic array */
+ @SafeVarargs
+ private static ThrowingConsumer[] createAssertArray(final ThrowingConsumer... assertions) {
+ return assertions;
+ }
+
+ @Nested
+ @DisplayName("buildNominalVoltageRules(...)")
+ class BuildNominalVoltageRules {
+ @ParameterizedTest
+ @MethodSource({"expertRulesData"})
+ void shouldCreateExpertRules(final List nominalVoltages) {
+ testVariableOrCombinationRules(
+ GlobalFilterUtils.buildNominalVoltageRules(nominalVoltages, EquipmentType.VOLTAGE_LEVEL),
+ nominalVoltages.size(),
+ NumberExpertRule.class,
+ createAssertArray(
+ ner -> assertThat(ner.getValue()).as("value").hasToString(nominalVoltages.getFirst()),
+ ner -> assertThat(ner.getField()).as("field").isEqualTo(FieldType.NOMINAL_VOLTAGE),
+ ner -> assertThat(ner.getOperator()).as("operator").isEqualTo(OperatorType.EQUALS)
+ ),
+ nominalVoltages.stream().map(nv -> NumberExpertRule.builder().value(Double.valueOf(nv))
+ .field(FieldType.NOMINAL_VOLTAGE).operator(OperatorType.EQUALS).build()).collect(Collectors.toUnmodifiableList()));
+ }
+
+ private static Stream expertRulesData() {
+ return Stream.of(
+ Arguments.of(List.of()),
+ Arguments.of(List.of("300.0")),
+ Arguments.of(List.of("400.0", "225.0"))
+ );
+ }
+ }
+
+ @Nested
+ @DisplayName("buildCountryCodeRules(...)")
+ class BuildCountryCodeRules {
+ @ParameterizedTest
+ @MethodSource({"enumRulesData"})
+ void shouldCreateEnumRules(final List countries) {
+ testVariableOrCombinationRules(
+ GlobalFilterUtils.buildCountryCodeRules(countries, EquipmentType.VOLTAGE_LEVEL),
+ countries.size(),
+ EnumExpertRule.class,
+ createAssertArray(
+ ner -> assertThat(ner.getValue()).as("value").hasToString(countries.getFirst().name()),
+ ner -> assertThat(ner.getField()).as("field").isEqualTo(FieldType.COUNTRY),
+ ner -> assertThat(ner.getOperator()).as("operator").isEqualTo(OperatorType.EQUALS)
+ ),
+ (List) countries.stream().map(c -> EnumExpertRule.builder().value(c.name())
+ .field(FieldType.COUNTRY).operator(OperatorType.EQUALS).build()).toList());
+ }
+
+ private static Stream enumRulesData() {
+ return Stream.of(
+ Arguments.of(List.of()),
+ Arguments.of(List.of(Country.YT)),
+ Arguments.of(List.of(Country.FR, Country.DE))
+ );
+ }
+ }
+
+ @Nested
+ @DisplayName("buildSubstationPropertyRules(...)")
+ class BuildSubstationPropertyRules {
+ @ParameterizedTest
+ @MethodSource({"propertiesRulesData"})
+ void shouldCreateCorrectPropertiesRules(final Map> properties) {
+ testVariableOrCombinationRules(
+ GlobalFilterUtils.buildSubstationPropertyRules(properties, EquipmentType.VOLTAGE_LEVEL),
+ properties.size(),
+ PropertiesExpertRule.class,
+ createAssertArray(
+ per -> assertThat(per.getCombinator()).as("combinator").isEqualTo(CombinatorType.OR),
+ per -> assertThat(per.getOperator()).as("operator").isEqualTo(OperatorType.IN),
+ per -> assertThat(per.getField()).as("field").isEqualTo(FieldType.SUBSTATION_PROPERTIES),
+ per -> assertThat(per.getPropertyName()).as("property name").isEqualTo("prop1"),
+ per -> assertThat(per.getPropertyValues()).as("property values").containsExactlyInAnyOrderElementsOf(properties.values().iterator().next())
+ ),
+ (List) properties.entrySet().stream().map(e -> PropertiesExpertRule.builder().combinator(CombinatorType.OR)
+ .operator(OperatorType.IN).field(FieldType.SUBSTATION_PROPERTIES).propertyName(e.getKey()).propertyValues(e.getValue()).build()).toList());
+ }
+
+ private static Stream propertiesRulesData() {
+ return Stream.of(
+ Arguments.of(Map.of()),
+ Arguments.of(Map.of("prop1", List.of("value0"))),
+ Arguments.of(Map.of("prop1", List.of("value1", "value2"), "prop2", List.of("value3")))
+ );
+ }
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("nominalVoltageFieldTypeData")
+ void shouldReturnCorrectNominalVoltageFieldTypes(final EquipmentType equipmentType, final List expectedFields) {
+ assertThat(GlobalFilterUtils.getNominalVoltageFieldType(equipmentType))
+ .as("result").containsExactlyInAnyOrderElementsOf(expectedFields);
+ }
+
+ private static Stream nominalVoltageFieldTypeData() {
+ return Stream.of(
+ // Nominal voltage
+ Arguments.of(EquipmentType.LINE, List.of(FieldType.NOMINAL_VOLTAGE_1, FieldType.NOMINAL_VOLTAGE_2)),
+ Arguments.of(EquipmentType.TWO_WINDINGS_TRANSFORMER, List.of(FieldType.NOMINAL_VOLTAGE_1, FieldType.NOMINAL_VOLTAGE_2)),
+ Arguments.of(EquipmentType.VOLTAGE_LEVEL, List.of(FieldType.NOMINAL_VOLTAGE)),
+ Arguments.of(EquipmentType.GENERATOR, Collections.emptyList())
+ );
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("countryCodeFieldTypeData")
+ void shouldReturnCorrectCountryCodeFieldTypes(final EquipmentType equipmentType, final List expectedFields) {
+ assertThat(GlobalFilterUtils.getCountryCodeFieldType(equipmentType))
+ .as("result").containsExactlyInAnyOrderElementsOf(expectedFields);
+ }
+
+ private static Stream countryCodeFieldTypeData() {
+ return Stream.of(
+ // Country code
+ Arguments.of(EquipmentType.VOLTAGE_LEVEL, List.of(FieldType.COUNTRY)),
+ Arguments.of(EquipmentType.TWO_WINDINGS_TRANSFORMER, List.of(FieldType.COUNTRY)),
+ Arguments.of(EquipmentType.LINE, List.of(FieldType.COUNTRY_1, FieldType.COUNTRY_2)),
+ Arguments.of(EquipmentType.GENERATOR, Collections.emptyList())
+ );
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("substationPropertyFieldTypeData")
+ void shouldReturnCorrectSubstationPropertyFieldTypes(final EquipmentType equipmentType, final List expectedFields) {
+ assertThat(GlobalFilterUtils.getSubstationPropertiesFieldTypes(equipmentType))
+ .as("result").containsExactlyInAnyOrderElementsOf(expectedFields);
+ }
+
+ private static Stream substationPropertyFieldTypeData() {
+ return Stream.of(
+ // Substation properties
+ Arguments.of(EquipmentType.LINE, List.of(FieldType.SUBSTATION_PROPERTIES_1, FieldType.SUBSTATION_PROPERTIES_2)),
+ Arguments.of(EquipmentType.GENERATOR, List.of(FieldType.SUBSTATION_PROPERTIES))
+ );
+ }
+
+ @Nested
+ @DisplayName("buildExpertFilter(...)")
+ class BuildExpertFilter {
+ @Test
+ void shouldReturnNullWhenNoExpertFiltersProvided() {
+ final GlobalFilter globalFilter = new GlobalFilter(null, null, null, null);
+ assertThat(GlobalFilterUtils.buildExpertFilter(globalFilter, EquipmentType.GENERATOR))
+ .as("result").isNull();
+ }
+
+ @Test
+ void shouldReturnNullWhenNoRules() {
+ final GlobalFilter globalFilter = new GlobalFilter(List.of(), List.of(), List.of(), Map.of());
+ assertThat(GlobalFilterUtils.buildExpertFilter(globalFilter, EquipmentType.GENERATOR))
+ .as("result").isNull();
+ }
+
+ @Test
+ void shouldCreateFilterWhenRulesExist() {
+ final List nv = List.of("400.0");
+ final GlobalFilter globalFilter = Mockito.spy(new GlobalFilter(nv, null, null, null));
+ try (final MockedStatic mockedGFU = Mockito.mockStatic(GlobalFilterUtils.class, Mockito.CALLS_REAL_METHODS)) {
+ mockedGFU.when(() -> GlobalFilterUtils.buildNominalVoltageRules(nv, EquipmentType.GENERATOR))
+ .thenReturn(Optional.of(Mockito.mock(AbstractExpertRule.class)));
+ mockedGFU.clearInvocations(); //important because stubbing static method counts as call
+ assertThat(GlobalFilterUtils.buildExpertFilter(globalFilter, EquipmentType.GENERATOR)).as("result")
+ .isNotNull().satisfies(
+ ef -> assertThat(ef.getEquipmentType()).as("equipment type").isEqualTo(EquipmentType.GENERATOR),
+ ef -> assertThat(ef.getRules()).as("rule combinator")
+ .asInstanceOf(InstanceOfAssertFactories.type(CombinatorExpertRule.class))
+ .extracting(CombinatorExpertRule::getRules, InstanceOfAssertFactories.list(AbstractExpertRule.class))
+ .as("rules").hasSize(1)
+ );
+ Mockito.verify(globalFilter, Mockito.atLeastOnce()).getNominalV();
+ Mockito.verify(globalFilter, Mockito.atLeastOnce()).getCountryCode();
+ Mockito.verify(globalFilter, Mockito.atLeastOnce()).getSubstationProperty();
+ mockedGFU.verify(() -> GlobalFilterUtils.buildNominalVoltageRules(Mockito.anyList(), any(EquipmentType.class)), Mockito.times(1));
+ mockedGFU.verify(() -> GlobalFilterUtils.buildExpertFilter(any(GlobalFilter.class), any(EquipmentType.class)), Mockito.times(1));
+ mockedGFU.verifyNoMoreInteractions(); //check if forget to mock a method
+ Mockito.verifyNoMoreInteractions(globalFilter);
+ }
+ }
+ }
+
+ @Nested
+ @DisplayName("filterNetwork(...)")
+ class FilterNetwork {
+ @Test
+ void shouldReturnIdsFromFilteredNetwork() {
+ final Network network = Mockito.mock(Network.class);
+ final FilterLoader loader = Mockito.mock(FilterLoader.class);
+ final AbstractFilter filter = Mockito.mock(AbstractFilter.class);
+ when(filter.getEquipmentType()).thenReturn(EquipmentType.GENERATOR);
+ try (final MockedStatic mockedFU = Mockito.mockStatic(FiltersUtils.class, Mockito.CALLS_REAL_METHODS)) {
+ final Identifiable> i1 = Mockito.mock(Identifiable.class);
+ when(i1.getId()).thenReturn("id1");
+ final Identifiable> i2 = Mockito.mock(Identifiable.class);
+ when(i2.getId()).thenReturn("id2");
+ final List> attributes = List.of(i1, i2);
+ mockedFU.when(() -> FiltersUtils.getIdentifiables(filter, network, loader)).thenReturn(attributes);
+ mockedFU.clearInvocations(); //important because stubbing static method counts as call
+ assertThat(GlobalFilterUtils.filterNetwork(filter, network, loader)).as("result")
+ .containsExactlyInAnyOrder("id1", "id2");
+ Mockito.verify(i1, Mockito.atLeastOnce()).getId();
+ Mockito.verify(i2, Mockito.atLeastOnce()).getId();
+ Mockito.verify(filter, Mockito.atLeastOnce()).getEquipmentType();
+ Mockito.verifyNoMoreInteractions(filter, network, loader, i1, i2);
+ mockedFU.verify(() -> FiltersUtils.getIdentifiables(eq(filter), eq(network), eq(loader)), Mockito.times(1));
+ mockedFU.verifyNoMoreInteractions(); //check if forget to mock a method
+ }
+ }
+ }
+
+ @Nested
+ @DisplayName("applyFilterOnNetwork(...)")
+ class ApplyFilterOnNetwork {
+ @Test
+ void shouldReturnFilteredNetworkWhenSameEquipmentType() {
+ final Network network = Mockito.mock(Network.class);
+ final FilterLoader loader = Mockito.mock(FilterLoader.class);
+ final AbstractFilter filter = Mockito.mock(AbstractFilter.class);
+ when(filter.getEquipmentType()).thenReturn(EquipmentType.GENERATOR);
+ try (final MockedStatic mockedFU = Mockito.mockStatic(FiltersUtils.class, Mockito.CALLS_REAL_METHODS)) {
+ final Identifiable> gen1 = Mockito.mock(Identifiable.class);
+ when(gen1.getId()).thenReturn("gen1");
+ final Identifiable> gen2 = Mockito.mock(Identifiable.class);
+ when(gen2.getId()).thenReturn("gen2");
+ final List> attributes = List.of(gen1, gen2);
+ mockedFU.when(() -> FiltersUtils.getIdentifiables(filter, network, loader)).thenReturn(attributes);
+ mockedFU.clearInvocations(); //important because stubbing static method counts as call
+ assertThat(GlobalFilterUtils.applyFilterOnNetwork(filter, EquipmentType.GENERATOR, network, loader))
+ .as("result").containsExactlyInAnyOrder("gen1", "gen2");
+ Mockito.verify(filter, Mockito.atLeastOnce()).getEquipmentType();
+ Mockito.verify(gen1, Mockito.atLeastOnce()).getId();
+ Mockito.verify(gen2, Mockito.atLeastOnce()).getId();
+ Mockito.verifyNoMoreInteractions(filter, network, loader, gen1, gen2);
+ mockedFU.verify(() -> FiltersUtils.getIdentifiables(eq(filter), eq(network), eq(loader)), Mockito.atLeastOnce());
+ mockedFU.verifyNoMoreInteractions(); //check if forget to mock a method
+ }
+ }
+
+ @Test
+ void shouldBuildVoltageLevelFilterWhenVoltageLevelType() {
+ final Network network = Mockito.mock(Network.class);
+ final FilterLoader loader = Mockito.mock(FilterLoader.class);
+ final AbstractFilter filter = Mockito.mock(AbstractFilter.class);
+ when(filter.getEquipmentType()).thenReturn(EquipmentType.VOLTAGE_LEVEL);
+ final UUID filterUuid = UuidUtils.createUUID(0);
+ when(filter.getId()).thenReturn(filterUuid);
+ try (final MockedStatic mockedFU = Mockito.mockStatic(FiltersUtils.class, Mockito.CALLS_REAL_METHODS)) {
+ final Identifiable> line1 = Mockito.mock(Identifiable.class);
+ when(line1.getId()).thenReturn("line1");
+ final Identifiable> line2 = Mockito.mock(Identifiable.class);
+ when(line2.getId()).thenReturn("line2");
+ final List> attributes = List.of(line1, line2);
+ mockedFU.when(() -> FiltersUtils.getIdentifiables(any(ExpertFilter.class), eq(network), eq(loader))).thenReturn(attributes);
+ mockedFU.clearInvocations(); //important because stubbing static method counts as call
+ assertThat(GlobalFilterUtils.applyFilterOnNetwork(filter, EquipmentType.LINE, network, loader))
+ .as("result").containsExactlyInAnyOrder("line1", "line2");
+ Mockito.verify(filter, Mockito.atLeastOnce()).getEquipmentType();
+ Mockito.verify(filter, Mockito.atLeastOnce()).getId();
+ Mockito.verify(line1, Mockito.atLeastOnce()).getId();
+ Mockito.verify(line2, Mockito.atLeastOnce()).getId();
+ Mockito.verifyNoMoreInteractions(filter, network, loader, line1, line2);
+ mockedFU.verify(() -> FiltersUtils.getIdentifiables(any(ExpertFilter.class), eq(network), eq(loader)), Mockito.atLeastOnce());
+ mockedFU.verifyNoMoreInteractions(); //check if forget to mock a method
+ }
+ }
+
+ @Test
+ void shouldReturnEmptyWhenDifferentEquipmentType() {
+ final FilterLoader loader = Mockito.mock(FilterLoader.class);
+ final Network network = Mockito.mock(Network.class);
+ final AbstractFilter filter = Mockito.mock(AbstractFilter.class);
+ when(filter.getEquipmentType()).thenReturn(EquipmentType.LOAD);
+ assertThat(GlobalFilterUtils.applyFilterOnNetwork(filter, EquipmentType.GENERATOR, network, loader))
+ .as("result").isEmpty();
+ Mockito.verify(filter, Mockito.atLeastOnce()).getEquipmentType();
+ Mockito.verifyNoMoreInteractions(loader, network, filter);
+ }
+ }
+
+ //TODO applyGlobalFilterOnNetwork(single)
+ //TODO applyGlobalFilterOnNetwork(list)
+}
diff --git a/src/test/java/org/gridsuite/filter/utils/FiltersUtilsTest.java b/src/test/java/org/gridsuite/filter/utils/FiltersUtilsTest.java
index 6a9f279..f657ddc 100644
--- a/src/test/java/org/gridsuite/filter/utils/FiltersUtilsTest.java
+++ b/src/test/java/org/gridsuite/filter/utils/FiltersUtilsTest.java
@@ -9,42 +9,32 @@
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.*;
import org.apache.commons.collections4.CollectionUtils;
+import org.assertj.core.api.WithAssertions;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.expertfilter.expertrule.AbstractExpertRule;
import org.gridsuite.filter.expertfilter.expertrule.CombinatorExpertRule;
import org.gridsuite.filter.expertfilter.expertrule.StringExpertRule;
-import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
-import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables;
-import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
-import org.gridsuite.filter.identifierlistfilter.IdentifierListFilter;
-import org.gridsuite.filter.identifierlistfilter.IdentifierListFilterEquipmentAttributes;
+import org.gridsuite.filter.identifierlistfilter.*;
import org.gridsuite.filter.utils.expertfilter.CombinatorType;
import org.gridsuite.filter.utils.expertfilter.OperatorType;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.*;
-import static org.gridsuite.filter.utils.expertfilter.FieldType.*;
-import static org.gridsuite.filter.utils.expertfilter.OperatorType.*;
+import static org.gridsuite.filter.utils.expertfilter.FieldType.ID;
+import static org.gridsuite.filter.utils.expertfilter.FieldType.NAME;
+import static org.gridsuite.filter.utils.expertfilter.OperatorType.IS;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Franck Lecuyer
*/
-class FiltersUtilsTest {
- private Network network;
- private Network network2;
- private Network network3;
- private Network network4;
- private Network network5;
-
- private FilterLoader filterLoader;
-
- @BeforeEach
- void setUp() {
- network = EurostagTutorialExample1Factory.createWithMoreGenerators();
+class FiltersUtilsTest implements WithAssertions {
+ private final FilterLoader filterLoader = uuids -> null;
+
+ private static Network prepareNetwork() {
+ Network network = EurostagTutorialExample1Factory.createWithMoreGenerators();
network.getSubstation("P1").setProperty("region", "north");
network.getSubstation("P2").setProperty("region", "south");
network.getGenerator("GEN").setProperty("region", "north");
@@ -54,17 +44,18 @@ void setUp() {
network.getTwoWindingsTransformer("NHV2_NLOAD").setProperty("region", "south");
network.getLine("NHV1_NHV2_1").setProperty("region", "south");
network.getLine("NHV1_NHV2_2").setProperty("region", "south");
+ return network;
+ }
- network2 = HvdcTestNetwork.createVsc();
+ private static Network prepareHvdcNetwork() {
+ Network network2 = HvdcTestNetwork.createVsc();
network2.getSubstation("S2").setProperty("region", "north");
- network3 = SvcTestCaseFactory.createWithMoreSVCs();
- network4 = ShuntTestCaseFactory.create();
- network5 = ThreeWindingsTransformerNetworkFactory.create();
- filterLoader = uuids -> null;
+ return network2;
}
@Test
void testSubstationFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -95,6 +86,7 @@ void testSubstationFilter() {
@Test
void testVoltageLevelFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -124,6 +116,7 @@ void testVoltageLevelFilter() {
@Test
void testLineFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -155,6 +148,7 @@ void testLineFilter() {
@Test
void testTwoWindingsTransformerFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -184,6 +178,7 @@ void testTwoWindingsTransformerFilter() {
@Test
void testThreeWindingsTransformerFilter() {
+ final Network network5 = ThreeWindingsTransformerNetworkFactory.create();
// expert filter
ExpertFilter expertFilter = new ExpertFilter(
@@ -200,6 +195,7 @@ void testThreeWindingsTransformerFilter() {
@Test
void testGeneratorFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -232,6 +228,7 @@ void testGeneratorFilter() {
@Test
void testLoadFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -261,6 +258,7 @@ void testLoadFilter() {
@Test
void testBatteryFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -288,6 +286,7 @@ void testBatteryFilter() {
@Test
void testShuntCompensatorFilter() {
+ final Network network4 = ShuntTestCaseFactory.create();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -317,6 +316,7 @@ void testShuntCompensatorFilter() {
@Test
void testStaticVarCompensatorFilter() {
+ final Network network3 = SvcTestCaseFactory.createWithMoreSVCs();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -349,6 +349,7 @@ void testStaticVarCompensatorFilter() {
@Test
void testDanglingLineFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -376,6 +377,7 @@ void testDanglingLineFilter() {
@Test
void testBusbarSectionFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -403,6 +405,8 @@ void testBusbarSectionFilter() {
@Test
void testBusFilter() {
+ final Network network = prepareNetwork();
+
// expert filter only for bus
ExpertFilter expertFilter = new ExpertFilter(
UUID.randomUUID(),
@@ -417,6 +421,7 @@ void testBusFilter() {
@Test
void testLccConverterStationFilter() {
+ final Network network = prepareNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -444,6 +449,7 @@ void testLccConverterStationFilter() {
@Test
void testHvdcLineFilter() {
+ final Network network2 = prepareHvdcNetwork();
// identifier list filter
List filterEquipmentAttributes = List.of(
@@ -474,6 +480,7 @@ void testHvdcLineFilter() {
@Test
void testIdentifierListFilter() {
+ final Network network = prepareNetwork();
List filterEquipmentAttributes = List.of(
new IdentifierListFilterEquipmentAttributes("GEN", 30.),
new IdentifierListFilterEquipmentAttributes("notFound1", 50.),
@@ -513,6 +520,7 @@ void testIdentifierListFilter() {
@Test
void testFilterLoader() {
+ final Network network = prepareNetwork();
// with identifier list filter
List filterEquipmentAttributes = List.of(
new IdentifierListFilterEquipmentAttributes("GEN", 30.),
@@ -539,6 +547,7 @@ void testFilterLoader() {
@Test
void testEquipmentNameFilterNoMatch() {
+ final Network network2 = prepareHvdcNetwork();
List rules = new ArrayList<>();
rules.add(StringExpertRule.builder().field(NAME).operator(IS).value("unexisting name").build());
AbstractExpertRule parentRule = CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(rules).build();
@@ -555,6 +564,7 @@ void testEquipmentNameFilterNoMatch() {
@Test
void testEquipmentNameFilterWithMatch() {
+ final Network network2 = prepareHvdcNetwork();
List rules = new ArrayList<>();
rules.add(StringExpertRule.builder().field(NAME).operator(IS).value("Converter1").build());
AbstractExpertRule parentRule = CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(rules).build();
@@ -570,6 +580,7 @@ void testEquipmentNameFilterWithMatch() {
@Test
void testEquipmentNameFilterWithNullValueInEquipments() {
+ final Network network = prepareNetwork();
List rules = new ArrayList<>();
rules.add(StringExpertRule.builder().field(NAME).operator(IS).value("some name").build());
AbstractExpertRule parentRule = CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(rules).build();
@@ -583,4 +594,27 @@ void testEquipmentNameFilterWithNullValueInEquipments() {
// in this network, VL equipments have null name => no match
assertEquals(0, identifiables.size());
}
+
+ @Test
+ void shouldReturnEmptyWhenCombineFilterResultsInputIsEmpty() {
+ assertThat(FiltersUtils.combineFilterResults(List.of(), true)).as("result").isEmpty();
+ }
+
+ @Test
+ void shouldReturnIntersectionWhenUsingAndLogic() {
+ assertThat(FiltersUtils.combineFilterResults(Arrays.asList(
+ List.of("item1", "item2", "item3"),
+ List.of("item2", "item3", "item4"),
+ List.of("item2", "item5")), true))
+ .as("result").singleElement().isEqualTo("item2");
+ }
+
+ @Test
+ void shouldReturnUnionWhenUsingOrLogic() {
+ assertThat(FiltersUtils.combineFilterResults(Arrays.asList(
+ List.of("item1", "item2"),
+ List.of("item3", "item4"),
+ List.of("item5")), false))
+ .as("result").containsExactlyInAnyOrder("item1", "item2", "item3", "item4", "item5");
+ }
}
diff --git a/src/test/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtilsTest.java b/src/test/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtilsTest.java
new file mode 100644
index 0000000..2de5c5c
--- /dev/null
+++ b/src/test/java/org/gridsuite/filter/utils/expertfilter/ExpertFilterUtilsTest.java
@@ -0,0 +1,71 @@
+package org.gridsuite.filter.utils.expertfilter;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.assertj.core.api.OptionalAssert;
+import org.assertj.core.api.WithAssertions;
+import org.gridsuite.filter.expertfilter.expertrule.AbstractExpertRule;
+import org.gridsuite.filter.expertfilter.expertrule.CombinatorExpertRule;
+import org.gridsuite.filter.expertfilter.expertrule.FilterUuidExpertRule;
+import org.gridsuite.filter.utils.EquipmentType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.mockito.Mockito.mock;
+
+class ExpertFilterUtilsTest implements WithAssertions {
+ @ParameterizedTest
+ @MethodSource("orCombinationData")
+ void shouldCreateOrCombination(final List rules, final boolean expectEmpty, final boolean expectSingle) {
+ final OptionalAssert assertOpt = assertThat(ExpertFilterUtils.buildOrCombination(rules)).as("result");
+ if (expectEmpty) {
+ assertOpt.isEmpty();
+ } else {
+ assertOpt.isPresent();
+ final var assertRule = assertOpt.get().as("rule");
+ if (expectSingle) {
+ assertRule.isEqualTo(rules.getFirst());
+ } else {
+ assertRule.asInstanceOf(InstanceOfAssertFactories.type(CombinatorExpertRule.class)).satisfies(
+ cer -> assertThat(cer.getCombinator()).as("combinator").isEqualTo(CombinatorType.OR),
+ cer -> assertThat(cer.getRules()).as("rules").containsExactlyInAnyOrderElementsOf(rules)
+ );
+ }
+ }
+ }
+
+ private static Stream orCombinationData() {
+ return Stream.of(
+ Arguments.of(List.of(), true, false),
+ Arguments.of(List.of(mock(AbstractExpertRule.class)), false, true),
+ Arguments.of(List.of(mock(AbstractExpertRule.class), mock(AbstractExpertRule.class)), false, false)
+ );
+ }
+
+ @Test
+ void shouldCreateExpertFilterWithVoltageLevelIdsCriteria() {
+ final UUID filterUuid = UUID.randomUUID();
+ final EquipmentType equipmentType = EquipmentType.LINE;
+ assertThat(ExpertFilterUtils.buildExpertFilterWithVoltageLevelIdsCriteria(filterUuid, equipmentType)).as("result").isNotNull().satisfies(
+ ef -> assertThat(ef.getEquipmentType()).as("equipmentType").isEqualTo(equipmentType),
+ ef -> assertThat(ef.getRules()).as("expert rules").isNotNull()
+ .asInstanceOf(InstanceOfAssertFactories.type(CombinatorExpertRule.class))
+ .satisfies(
+ cer -> assertThat(cer.getCombinator()).as("combinator").isEqualTo(CombinatorType.OR),
+ cer -> assertThat(cer.getRules()).as("combinator rules").hasSize(2)
+ .allSatisfy(er -> assertThat(er).as("expert rule")
+ .asInstanceOf(InstanceOfAssertFactories.type(FilterUuidExpertRule.class))
+ .satisfies(fuer -> assertThat(fuer.getOperator()).as("operator").isEqualTo(OperatorType.IS_PART_OF))
+ .extracting(FilterUuidExpertRule::getValues, InstanceOfAssertFactories.set(String.class)).as("UUIDs")
+ .containsExactly(filterUuid.toString()))
+ .map(AbstractExpertRule::getField).as("fields")
+ .containsExactly(FieldType.VOLTAGE_LEVEL_ID_1, FieldType.VOLTAGE_LEVEL_ID_2)
+ )
+ );
+ }
+}