Skip to content
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@

<dependencyManagement>
<dependencies>
<!-- TODO remove when gridsuite-dependencies is released with this version -->
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-ws-commons</artifactId>
<version>1.29.0</version>
</dependency>

<!-- imports -->
<dependency>
<groupId>org.gridsuite</groupId>
Expand Down
29 changes: 18 additions & 11 deletions src/main/java/org/gridsuite/filter/server/FilterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.IFilterAttributes;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.exceptions.FilterCycleException;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
import org.gridsuite.filter.server.dto.EquipmentTypesByFilterId;
import org.gridsuite.filter.server.dto.FilterAttributes;
import org.gridsuite.filter.server.dto.FiltersWithEquipmentTypes;
import org.gridsuite.filter.server.dto.IdsByGroup;
import org.gridsuite.filter.server.error.FilterBusinessErrorCode;
import org.gridsuite.filter.server.error.FilterException;
import org.gridsuite.filter.server.repositories.proxies.AbstractFilterRepositoryProxy;
import org.gridsuite.filter.server.service.DirectoryService;
import org.gridsuite.filter.server.utils.FilterWithEquipmentTypesUtils;
Expand Down Expand Up @@ -61,7 +64,7 @@ public class FilterService {

public List<IFilterAttributes> getFilters() {
return this.repositoriesService.getFiltersAttributes()
.map(IFilterAttributes.class::cast) // cast because generics are invariants
.map(IFilterAttributes.class::cast) // cast because generics are invariants
.toList();
}

Expand Down Expand Up @@ -109,7 +112,7 @@ public List<AbstractFilter> createFilters(List<AbstractFilter> filters) {
}

Map<AbstractFilterRepositoryProxy<?, ?>, List<AbstractFilter>> repositoryFiltersMap = filters.stream()
.collect(Collectors.groupingBy(this.repositoriesService::getRepositoryFromType));
.collect(Collectors.groupingBy(this.repositoriesService::getRepositoryFromType));

List<AbstractFilter> createdFilters = new ArrayList<>();
repositoryFiltersMap.forEach((repository, subFilters) -> createdFilters.addAll(repository.insertAll(subFilters)));
Expand Down Expand Up @@ -150,7 +153,7 @@ public Map<UUID, UUID> duplicateFilters(List<UUID> filterUuids) {
});

Map<AbstractFilterRepositoryProxy<?, ?>, List<AbstractFilter>> repositoryFiltersMap = sourceFilters.stream()
.collect(Collectors.groupingBy(this.repositoriesService::getRepositoryFromType));
.collect(Collectors.groupingBy(this.repositoriesService::getRepositoryFromType));

repositoryFiltersMap.forEach(AbstractFilterRepositoryProxy::insertAll);

Expand All @@ -170,7 +173,11 @@ private AbstractFilter doUpdateFilter(UUID id, AbstractFilter newFilter, String
FilterLoader filterLoader = uuids -> uuids.stream()
.map(uuid -> uuid.equals(id) ? newFilter : this.repositoriesService.getFilter(uuid).orElse(null))
.toList();
FilterCycleDetector.checkNoCycle(newFilter, filterLoader);
try {
FilterCycleDetector.checkNoCycle(newFilter, filterLoader);
} catch (FilterCycleException exception) {
throw new FilterException(FilterBusinessErrorCode.FILTER_CYCLE_DETECTED, exception.getMessage());
}

AbstractFilter modifiedOrCreatedFilter;
if (filterOpt.get().getType() == newFilter.getType()) { // filter type has not changed
Expand Down Expand Up @@ -301,13 +308,13 @@ public Map<String, Long> getIdentifiablesCountByGroup(IdsByGroup idsByGroup, UUI
Objects.requireNonNull(idsByGroup);
final FilterLoader filterLoader = this.repositoriesService.getFilterLoader();
return idsByGroup.getIds().entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> this.repositoriesService.getFilters(entry.getValue()).stream()
.mapToLong(f -> getIdentifiableAttributes(f, networkUuid, variantId, filterLoader).size())
.sum()
)
);
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> this.repositoriesService.getFilters(entry.getValue()).stream()
.mapToLong(f -> getIdentifiableAttributes(f, networkUuid, variantId, filterLoader).size())
.sum()
)
);
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2025, 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.server;

import com.powsybl.ws.commons.error.ServerNameProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
@Component
public class PropertyServerNameProvider implements ServerNameProvider {

private final String name;

public PropertyServerNameProvider(@Value("${server.name:filter-server}") String name) {
this.name = name;
}

@Override
public String serverName() {
return name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2025, 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.server.error;

import com.powsybl.ws.commons.error.BusinessErrorCode;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*
* Business error codes emitted by the filter service.
*/
public enum FilterBusinessErrorCode implements BusinessErrorCode {
FILTER_CYCLE_DETECTED("filter.filterCycleDetected");
private final String code;

FilterBusinessErrorCode(String code) {
this.code = code;
}

public String value() {
return code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2025, 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.server.error;

import com.powsybl.ws.commons.error.AbstractBusinessException;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

/**
* /**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*
* Filter server specific runtime exception enriched with a business error code.
*/
public class FilterException extends AbstractBusinessException {

private final FilterBusinessErrorCode errorCode;

public FilterException(FilterBusinessErrorCode errorCode, String message) {
super(Objects.requireNonNull(message, "message must not be null"));
this.errorCode = Objects.requireNonNull(errorCode, "errorCode must not be null");
}

@NotNull
@Override
public FilterBusinessErrorCode getBusinessErrorCode() {
return errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2025, 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.server.error;

import com.powsybl.ws.commons.error.AbstractBaseRestExceptionHandler;
import com.powsybl.ws.commons.error.ServerNameProvider;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends AbstractBaseRestExceptionHandler<FilterException, FilterBusinessErrorCode> {

public RestResponseEntityExceptionHandler(ServerNameProvider serverNameProvider) {
super(serverNameProvider);
}

@NotNull
@Override
protected FilterBusinessErrorCode getBusinessCode(FilterException ex) {
return ex.getBusinessErrorCode();
}

@Override
protected HttpStatus mapStatus(FilterBusinessErrorCode code) {
return switch (code) {
case FILTER_CYCLE_DETECTED -> HttpStatus.BAD_REQUEST;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2025, 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.server;

import org.gridsuite.filter.server.error.FilterBusinessErrorCode;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
class FilterBusinessErrorCodeTest {

@ParameterizedTest
@EnumSource(FilterBusinessErrorCode.class)
void valueStartsWithFilter(FilterBusinessErrorCode code) {
assertThat(code.value()).startsWith("filter.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ private void updateFiltersWithNoneExistingId(Map<UUID, AbstractFilter> filtersTo
mvc.perform(put(URL_TEMPLATE + "/batch")
.content(objectMapper.writeValueAsString(filtersToUpdateMap))
.contentType(APPLICATION_JSON))
.andExpect(status().isNotFound());
.andExpect(status().isInternalServerError());
}

private void deleteFilter(UUID filterId) throws Exception {
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/org/gridsuite/filter/server/FilterExceptionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2025, 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.server;

import org.gridsuite.filter.server.error.FilterBusinessErrorCode;
import org.gridsuite.filter.server.error.FilterException;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
class FilterExceptionTest {

@Test
void defaultConstructorStoresMessage() {
FilterException exception = new FilterException(FilterBusinessErrorCode.FILTER_CYCLE_DETECTED, "cycle");
assertThat(exception.getMessage()).isEqualTo("cycle");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2025, 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.server;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
class PropertyServerNameProviderTest {

@Test
void returnsConfiguredName() {
PropertyServerNameProvider provider = new PropertyServerNameProvider("filter-overridden");
assertThat(provider.serverName()).isEqualTo("filter-overridden");
}
}
Loading