Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@
</dependency>

<!-- Compilation dependencies -->
<dependency>
<groupId>org.gridsuite</groupId>
<artifactId>gridsuite-filter</artifactId>
<!-- TODO : remove version when lib is released -->
<version>1.9.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand Down Expand Up @@ -214,5 +220,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-jetty12</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,53 @@ public ResponseEntity<Void> modifyIdentifierContingencyList(
}
}

@PostMapping(value = "/filters-contingency-lists", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create an filter base contingency list")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter based contingency list has been created successfully")})
public ResponseEntity<PersistentContingencyList> createFilterBasedContingencyList(@RequestParam(required = false, value = "id") UUID id,
@RequestBody FilterBasedContingencyList filterBasedContingencyList) {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(service.createFilterBasedContingencyList(id, filterBasedContingencyList));
}

@PutMapping(value = "/filters-contingency-lists/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Modify a filter based contingency list")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter based contingency list have been modified successfully")})
public ResponseEntity<Void> modifyFilterBasedContingencyList(
@PathVariable UUID id,
@RequestBody FilterBasedContingencyList contingencyList,
@RequestHeader("userId") String userId) {
try {
service.modifyFilterBasedContingencyList(id, contingencyList, userId);
return ResponseEntity.ok().build();
} catch (EntityNotFoundException ignored) {
return ResponseEntity.notFound().build();
}
}
Comment on lines +162 to +175
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PUT insert new lines in filter_metadata but not clean old lines, to verify annotation orphanRemoval option of OneToMany.
However, I see lines in relation table are removed


@PostMapping(value = "/filters-contingency-lists", params = "duplicateFrom")
@Operation(summary = "Create a filter based contingency list from another existing one")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the correct sentence is from an existing one or another one

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok changed

@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter based contingency list have been duplicated successfully"),
@ApiResponse(responseCode = "404", description = "Source form contingency list not found")})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@ApiResponse(responseCode = "404", description = "Source form contingency list not found")})
@ApiResponse(responseCode = "404", description = "Source from contingency list not found")})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's a mistake

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes i should replace by filter based contingency list

public ResponseEntity<UUID> duplicateFilterBasedContingencyList(@RequestParam("duplicateFrom") UUID contingencyListsId) {
return service.duplicateFilterBasedContingencyList(contingencyListsId).map(contingencyList -> ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(contingencyList))
.orElse(ResponseEntity.notFound().build());
}

@GetMapping(value = "/filters-contingency-lists/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get filter based contingency list by id")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter based contingency list"),
@ApiResponse(responseCode = "404", description = "The filter based contingency list does not exists")})
public ResponseEntity<PersistentContingencyList> getFilterBasedContingencylist(@PathVariable("id") UUID id) {
return service.getFilterBasedContingencyList(id).map(contingencyList -> ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(contingencyList))
.orElse(ResponseEntity.notFound().build());
}

@PostMapping(value = "/form-contingency-lists", params = "duplicateFrom")
@Operation(summary = "Create a form contingency list from another existing one")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The form contingency list have been duplicated successfully"),
Expand Down
140 changes: 123 additions & 17 deletions src/main/java/org/gridsuite/actions/server/ContingencyListService.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* 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.actions.server.dto;

import com.powsybl.contingency.contingency.list.ContingencyList;
import com.powsybl.iidm.network.Network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.gridsuite.actions.server.utils.ContingencyListType;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@Getter
@NoArgsConstructor
@Schema(description = "filter based contingency list")
public class FilterBasedContingencyList extends AbstractContingencyList {

@Schema(description = "filters list")
private List<FilterMetaData> filters;

public FilterBasedContingencyList(UUID uuid, Instant date, List<FilterMetaData> filterList) {
super(new ContingencyListMetadataImpl(uuid, ContingencyListType.FILTERS, date));
this.filters = filterList;
}

@Override
public ContingencyList toPowsyblContingencyList(Network network) {
return null;
}

@Override
public Map<String, Set<String>> getNotFoundElements(Network network) {
return Map.of();
}
}
29 changes: 29 additions & 0 deletions src/main/java/org/gridsuite/actions/server/dto/FilterMetaData.java
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.actions.server.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Metadata on filters composing filter based contingency list")
public class FilterMetaData {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

record?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok changed the class was renamed to FilterAttributes

@Schema(description = "filter uuid in filter data base")
private UUID id;

@Schema(description = "filter name")
private String name;

@Schema(description = "equipment type")
private String equipmentType;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, storing filter metadata on the actions server creates a risk: if a filter’s name or equipment type is updated, this information won’t stay synchronized.

These metadata should be enriched at service level when GET filter's info

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* 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.actions.server.entities;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.gridsuite.actions.server.dto.FilterBasedContingencyList;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "filter_based_contingency_list")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Table(name = "filter_based_contingency_list")
@Table(name = ""filter_based_contingency"")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No contingency list is on element in this table. We should stay coherent with the other contingency list names.

public class FilterBasedContingencyListEntity extends AbstractContingencyEntity {

@OneToMany(cascade = CascadeType.ALL)
private List<FilterMetaDataEntity> filtersListEntities;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should declare all annotations for this relation, ex; table name, column names, constraint name


public FilterBasedContingencyListEntity(FilterBasedContingencyList contingencyList) {
super();
if (CollectionUtils.isEmpty(contingencyList.getFilters())) {
return;
}

init(contingencyList);
}

private void init(FilterBasedContingencyList contingencyList) {
filtersListEntities = new ArrayList<>();
contingencyList.getFilters().forEach(f ->
filtersListEntities.add(
new FilterMetaDataEntity(UUID.randomUUID(), f.getId(), f.getName(), f.getEquipmentType()))
);
}

public FilterBasedContingencyListEntity update(FilterBasedContingencyList contingencyList) {
init(contingencyList);
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* 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.actions.server.entities;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.UUID;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "filter_metadata")
public class FilterMetaDataEntity {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;

@Column(name = "filter_id")
private UUID filterId;

@Column(name = "name")
private String name;

@Column(name = "equipment_type")
private String equipmentType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* 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.actions.server.repositories;

import org.gridsuite.actions.server.entities.FilterBasedContingencyListEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.UUID;

@Repository
public interface FilterBasedContingencyListRepository extends JpaRepository<FilterBasedContingencyListEntity, UUID> {
Integer deleteFilterBasedContingencyListEntityById(UUID id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2023, 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.actions.server.service;
import lombok.Getter;
import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
public class FilterService {
public static final String FILTER_END_POINT_EVALUATE_IDS = "/filters/evaluate/identifiables";
public static final String DELIMITER = "/";
public static final String FILTER_API_VERSION = "v1";

@Getter
private final String baseUri;
private final RestTemplate restTemplate;

@Autowired
public FilterService(@Value("${gridsuite.services.filter-server.base-uri:http://filter-server/}") String baseUri,
RestTemplate restTemplate) {
this.baseUri = baseUri;
this.restTemplate = restTemplate;
}

public List<IdentifiableAttributes> evaluateFilters(UUID networkUuid, String variantUuid, List<UUID> filtersUuid) {
Objects.requireNonNull(networkUuid);
Objects.requireNonNull(filtersUuid);
String endPointUrl = getBaseUri() + DELIMITER + FILTER_API_VERSION + FILTER_END_POINT_EVALUATE_IDS;

UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl);
uriComponentsBuilder.queryParam("networkUuid", networkUuid);
uriComponentsBuilder.queryParam("variantUuid", variantUuid);
uriComponentsBuilder.queryParam("ids", filtersUuid);
var uriComponent = uriComponentsBuilder.buildAndExpand();

ResponseEntity<FilteredIdentifiables> response = restTemplate.getForEntity(uriComponent.toUriString(), FilteredIdentifiables.class);
return response.getBody() != null ? response.getBody().getEquipmentIds() : List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@

public enum ContingencyListType {
FORM,
IDENTIFIERS
IDENTIFIERS,
FILTERS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.gridsuite.actions.server.utils;

/*
* 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/.
*/

import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.commons.report.ReportNodeDeserializer;
import com.powsybl.commons.report.ReportNodeJsonModule;
import com.powsybl.contingency.json.ContingencyJsonModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you took it from another service, maybe we can mutualize this bean?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes done


@Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();

//find and replace Jackson message converter with our own
for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
final HttpMessageConverter<?> httpMessageConverter = restTemplate.getMessageConverters().get(i);
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());
}
}

return restTemplate;
}

public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
return converter;
}

private ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS).build();
objectMapper.registerModule(new ContingencyJsonModule());
objectMapper.registerModule(new ReportNodeJsonModule());
objectMapper.setInjectableValues(new InjectableValues.Std().addValue(ReportNodeDeserializer.DICTIONARY_VALUE_ID, null));
return objectMapper;
}

@Bean
public ObjectMapper objectMapper() {
return createObjectMapper();
}

}

Loading
Loading