diff --git a/src/main/java/org/gridsuite/filter/server/FilterController.java b/src/main/java/org/gridsuite/filter/server/FilterController.java index 59371a21..c6ef88e5 100644 --- a/src/main/java/org/gridsuite/filter/server/FilterController.java +++ b/src/main/java/org/gridsuite/filter/server/FilterController.java @@ -74,9 +74,19 @@ public ResponseEntity createFilter(@RequestParam("id") UUID filt .body(service.createFilter(filter)); } + @PostMapping(value = "/filters/batch", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Create filters from given ids") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Filters have been successfully created")}) + public ResponseEntity> createFilters(@RequestBody Map filtersToCreateMap) { + filtersToCreateMap.forEach((uuid, expertFilter) -> expertFilter.setId(uuid)); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(service.createFilters(filtersToCreateMap.values().stream().toList())); + } + @PostMapping(value = "/filters", params = "duplicateFrom") @Operation(summary = "Duplicate a filter") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter has been successfully created"), + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter with given id has been successfully duplicated"), @ApiResponse(responseCode = "404", description = "Source filter not found")}) public ResponseEntity duplicateFilter(@RequestParam("duplicateFrom") UUID filterId) { return service.duplicateFilter(filterId).map(newFilterId -> ResponseEntity.ok() @@ -85,18 +95,38 @@ public ResponseEntity duplicateFilter(@RequestParam("duplicateFrom") UUID .orElse(ResponseEntity.notFound().build()); } + @PostMapping(value = "/filters/duplicate/batch") + @Operation(summary = "Duplicate filters from given ids") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Filters of given ids have been successfully duplicated"), + @ApiResponse(responseCode = "404", description = "Source filter not found")}) + public ResponseEntity> duplicateFilters(@RequestBody List filterUuids) { + Map uuidsMap = service.duplicateFilters(filterUuids); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(uuidsMap); + } + @PutMapping(value = "/filters/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Modify a filter") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter has been successfully modified")}) - public ResponseEntity changeFilter(@PathVariable UUID id, @RequestBody AbstractFilter filter, @RequestHeader("userId") String userId) { + @Operation(summary = "Update a filter from a given id and the whole new filter object") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter has been successfully updated")}) + public ResponseEntity updateFilter(@PathVariable UUID id, @RequestBody AbstractFilter filter, @RequestHeader("userId") String userId) { try { - service.changeFilter(id, filter, userId); - return ResponseEntity.ok().build(); + AbstractFilter updatedFilter = service.updateFilter(id, filter, userId); + return ResponseEntity.ok().body(updatedFilter); } catch (EntityNotFoundException ignored) { return ResponseEntity.notFound().build(); } } + @PutMapping(value = "/filters/batch", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Update filters in batch from a given map of each filter id and the corresponding whole new filter object") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Filters have been successfully updated")}) + public ResponseEntity> updateFilters(@RequestBody Map filtersToUpdateMap) { + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(service.updateFilters(filtersToUpdateMap)); + } + @DeleteMapping(value = "/filters/{id}") @Operation(summary = "delete the filter") @ApiResponse(responseCode = "200", description = "The filter has been deleted") @@ -105,6 +135,14 @@ public ResponseEntity deleteFilter(@PathVariable("id") UUID id) { return ResponseEntity.ok().build(); } + @DeleteMapping(value = "/filters") + @Operation(summary = "delete the filters") + @ApiResponse(responseCode = "200", description = "The filters have been deleted") + public ResponseEntity deleteFilters(@RequestBody List ids) { + service.deleteFilters(ids); + return ResponseEntity.ok().build(); + } + @GetMapping(value = "/filters/metadata") @Operation(summary = "get filters metadata") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "filters metadata"), diff --git a/src/main/java/org/gridsuite/filter/server/FilterService.java b/src/main/java/org/gridsuite/filter/server/FilterService.java index dc091df0..9bcf0a17 100644 --- a/src/main/java/org/gridsuite/filter/server/FilterService.java +++ b/src/main/java/org/gridsuite/filter/server/FilterService.java @@ -10,6 +10,7 @@ import com.powsybl.iidm.network.Network; import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.network.store.client.PreloadingStrategy; +import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.filter.AbstractFilter; import org.gridsuite.filter.FilterLoader; import org.gridsuite.filter.IFilterAttributes; @@ -19,39 +20,11 @@ import org.gridsuite.filter.server.dto.IdsByGroup; import org.gridsuite.filter.server.entities.AbstractFilterEntity; import org.gridsuite.filter.server.repositories.FilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.BatteryFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.BusBarSectionFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.DanglingLineFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.GeneratorFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.HvdcLineFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.LccConverterStationFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.LineFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.LoadFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.ShuntCompensatorFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.StaticVarCompensatorFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.SubstationFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.ThreeWindingsTransformerFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.TwoWindingsTransformerFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.VoltageLevelFilterRepository; -import org.gridsuite.filter.server.repositories.criteriafilter.VscConverterStationFilterRepository; +import org.gridsuite.filter.server.repositories.criteriafilter.*; import org.gridsuite.filter.server.repositories.expertfilter.ExpertFilterRepository; import org.gridsuite.filter.server.repositories.identifierlistfilter.IdentifierListFilterRepository; import org.gridsuite.filter.server.repositories.proxies.AbstractFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.BatteryFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.BusBarSectionFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.DanglingLineFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.GeneratorFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.HvdcLineFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.LccConverterStationFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.LineFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.LoadFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.ShuntCompensatorFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.StaticVarCompensatorFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.SubstationFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.ThreeWindingsTransformerFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.TwoWindingsTransformerFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.VoltageLevelFilterRepositoryProxy; -import org.gridsuite.filter.server.repositories.proxies.criteriafilter.VscConverterStationFilterRepositoryProxy; +import org.gridsuite.filter.server.repositories.proxies.criteriafilter.*; import org.gridsuite.filter.server.repositories.proxies.expertfiler.ExpertFilterRepositoryProxy; import org.gridsuite.filter.server.repositories.proxies.identifierlistfilter.IdentifierListFilterRepositoryProxy; import org.gridsuite.filter.server.repositories.proxies.scriptfilter.ScriptFilterRepositoryProxy; @@ -60,9 +33,9 @@ import org.gridsuite.filter.utils.FilterServiceUtils; import org.gridsuite.filter.utils.FilterType; import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; @@ -81,6 +54,7 @@ public class FilterService { private static final String FILTER_LIST = "Filter list "; private static final String NOT_FOUND = " not found"; + public static final String FILTER_UUIDS_NOT_FOUND = "Some filter uuids have not bean found"; private final Map> filterRepositories = new HashMap<>(); @@ -88,6 +62,8 @@ public class FilterService { private final NotificationService notificationService; + private final FilterService self; + public FilterService(final ScriptFilterRepository scriptFiltersRepository, final LineFilterRepository lineFilterRepository, final GeneratorFilterRepository generatorFilterRepository, @@ -107,7 +83,8 @@ public FilterService(final ScriptFilterRepository scriptFiltersRepository, final IdentifierListFilterRepository identifierListFilterRepository, final ExpertFilterRepository expertFilterRepository, NetworkStoreService networkStoreService, - NotificationService notificationService) { + NotificationService notificationService, + @Lazy FilterService self) { filterRepositories.put(EquipmentType.LINE.name(), new LineFilterRepositoryProxy(lineFilterRepository)); filterRepositories.put(EquipmentType.GENERATOR.name(), new GeneratorFilterRepositoryProxy(generatorFilterRepository)); filterRepositories.put(EquipmentType.LOAD.name(), new LoadFilterRepositoryProxy(loadFilterRepository)); @@ -131,6 +108,7 @@ public FilterService(final ScriptFilterRepository scriptFiltersRepository, filterRepositories.put(FilterType.EXPERT.name(), new ExpertFilterRepositoryProxy(expertFilterRepository)); this.networkStoreService = networkStoreService; this.notificationService = notificationService; + this.self = self; } public List getFilters() { @@ -156,14 +134,28 @@ public List getFilters(List ids) { .stream() .flatMap(repository -> repository.getFilters(ids) .stream()) - .collect(Collectors.toList()); + .toList(); } - @Transactional(propagation = Propagation.REQUIRED) + @Transactional public AbstractFilter createFilter(F filter) { return getRepository(filter).insert(filter); } + @Transactional + public List createFilters(List filters) { + if (CollectionUtils.isEmpty(filters)) { + return Collections.emptyList(); + } + + Map, List> repositoryFiltersMap = filters.stream() + .collect(Collectors.groupingBy(this::getRepository)); + + List createdFilters = new ArrayList<>(); + repositoryFiltersMap.forEach((repository, subFilters) -> createdFilters.addAll(repository.insertAll(subFilters))); + return createdFilters; + } + @Transactional public Optional duplicateFilter(UUID sourceFilterId) { Optional sourceFilterOptional = getFilter(sourceFilterId); @@ -171,13 +163,42 @@ public Optional duplicateFilter(UUID sourceFilterId) { UUID newFilterId = UUID.randomUUID(); AbstractFilter sourceFilter = sourceFilterOptional.get(); sourceFilter.setId(newFilterId); - createFilter(sourceFilter); + self.createFilter(sourceFilter); return Optional.of(newFilterId); } return Optional.empty(); } - private AbstractFilterRepositoryProxy> getRepository(AbstractFilter filter) { + /** + * @return Map of uuids of copied filters and uuids of new filters + */ + @Transactional + public Map duplicateFilters(List filterUuids) { + Map uuidsMap = new HashMap<>(); + + List sourceFilters = getFilters(filterUuids); + + // check whether found all + if (sourceFilters.isEmpty() || sourceFilters.size() != filterUuids.size()) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, FILTER_UUIDS_NOT_FOUND); + } + + sourceFilters.forEach(sourceFilter -> { + UUID newFilterId = UUID.randomUUID(); + uuidsMap.put(sourceFilter.getId(), newFilterId); + sourceFilter.setId(newFilterId); + }); + + Map, List> repositoryFiltersMap = sourceFilters.stream() + .collect(Collectors.groupingBy(this::getRepository)); + + repositoryFiltersMap.forEach(AbstractFilterRepositoryProxy::insertAll); + + return uuidsMap; + } + + private AbstractFilterRepositoryProxy> getRepository(AbstractFilter filter) { if (!filter.getType().equals(FilterType.CRITERIA)) { return filterRepositories.get(filter.getType().name()); } @@ -185,24 +206,37 @@ public Optional duplicateFilter(UUID sourceFilterId) { } @Transactional - public void changeFilter(UUID id, F newFilter, String userId) { - Optional f = getFilter(id); - if (f.isPresent()) { - if (getRepository(f.get()) == getRepository(newFilter)) { // filter type has not changed - getRepository(newFilter).modify(id, newFilter); + public AbstractFilter updateFilter(UUID id, F newFilter, String userId) { + Optional filterOpt = getFilter(id); + AbstractFilter modifiedOrCreatedFilter; + if (filterOpt.isPresent()) { + if (getRepository(filterOpt.get()) == getRepository(newFilter)) { // filter type has not changed + modifiedOrCreatedFilter = getRepository(newFilter).modify(id, newFilter); } else { // filter type has changed - if (f.get().getType() == FilterType.SCRIPT || newFilter.getType() == FilterType.SCRIPT) { + if (filterOpt.get().getType() == FilterType.SCRIPT || newFilter.getType() == FilterType.SCRIPT) { throw new PowsyblException(WRONG_FILTER_TYPE); } else { - getRepository(f.get()).deleteById(id); + getRepository(filterOpt.get()).deleteById(id); newFilter.setId(id); - createFilter(newFilter); + modifiedOrCreatedFilter = self.createFilter(newFilter); } } } else { throw new ResponseStatusException(HttpStatus.NOT_FOUND, FILTER_LIST + id + NOT_FOUND); } - notificationService.emitElementUpdated(id, userId); + + if (userId != null) { + notificationService.emitElementUpdated(id, userId); + } + + return modifiedOrCreatedFilter; + } + + @Transactional + public List updateFilters(Map filtersToUpdateMap) { + return filtersToUpdateMap.keySet().stream() + .map(filterUuid -> self.updateFilter(filterUuid, filtersToUpdateMap.get(filterUuid), null)) + .toList(); } public void deleteFilter(UUID id) { @@ -212,6 +246,11 @@ public void deleteFilter(UUID id) { } } + public void deleteFilters(List ids) { + Objects.requireNonNull(ids); + filterRepositories.values().forEach(repository -> repository.deleteAllByIds(ids)); + } + public void deleteAll() { filterRepositories.values().forEach(AbstractFilterRepositoryProxy::deleteAll); } diff --git a/src/main/java/org/gridsuite/filter/server/repositories/FilterRepository.java b/src/main/java/org/gridsuite/filter/server/repositories/FilterRepository.java index f247cee6..c61549ec 100644 --- a/src/main/java/org/gridsuite/filter/server/repositories/FilterRepository.java +++ b/src/main/java/org/gridsuite/filter/server/repositories/FilterRepository.java @@ -30,4 +30,7 @@ public interface FilterRepository extends JpaRep @Transactional Integer removeById(UUID id); + + @Transactional + void deleteAllByIdIn(List ids); } diff --git a/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java b/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java index 7f48aa0d..a9c66139 100644 --- a/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java +++ b/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java @@ -8,11 +8,10 @@ package org.gridsuite.filter.server.repositories.proxies; import com.powsybl.commons.PowsyblException; - import org.gridsuite.filter.AbstractFilter; import org.gridsuite.filter.criteriafilter.*; import org.gridsuite.filter.server.dto.FilterAttributes; -import org.gridsuite.filter.server.entities.*; +import org.gridsuite.filter.server.entities.AbstractFilterEntity; import org.gridsuite.filter.server.entities.criteriafilter.*; import org.gridsuite.filter.server.repositories.FilterMetadata; import org.gridsuite.filter.server.repositories.FilterRepository; @@ -123,15 +122,24 @@ public AbstractFilter insert(AbstractFilter f) { return toDto(getRepository().save(fromDto(f))); } - public void modify(UUID id, AbstractFilter f) { + public List insertAll(List filters) { + List savedFilterEntities = getRepository().saveAll(filters.stream().map(this::fromDto).toList()); + return savedFilterEntities.stream().map(this::toDto).toList(); + } + + public AbstractFilter modify(UUID id, AbstractFilter f) { f.setId(id); - toDto(getRepository().save(fromDto(f))); + return toDto(getRepository().save(fromDto(f))); } public boolean deleteById(UUID id) { return getRepository().removeById(id) != 0; } + public void deleteAllByIds(List ids) { + getRepository().deleteAllByIdIn(ids); + } + public void deleteAll() { getRepository().deleteAll(); } diff --git a/src/main/java/org/gridsuite/filter/server/repositories/proxies/expertfiler/ExpertFilterRepositoryProxy.java b/src/main/java/org/gridsuite/filter/server/repositories/proxies/expertfiler/ExpertFilterRepositoryProxy.java index 5d44d15f..31169506 100644 --- a/src/main/java/org/gridsuite/filter/server/repositories/proxies/expertfiler/ExpertFilterRepositoryProxy.java +++ b/src/main/java/org/gridsuite/filter/server/repositories/proxies/expertfiler/ExpertFilterRepositoryProxy.java @@ -57,97 +57,97 @@ public AbstractFilter toDto(ExpertFilterEntity filterEntity) { .build(); } - public static AbstractExpertRule entityToDto(ExpertRuleEntity filterEntity) { - switch (filterEntity.getDataType()) { + public static AbstractExpertRule entityToDto(ExpertRuleEntity expertRuleEntity) { + switch (expertRuleEntity.getDataType()) { case COMBINATOR -> { return CombinatorExpertRule.builder() - .combinator(filterEntity.getCombinator()) - .field(filterEntity.getField()) - .operator(filterEntity.getOperator()) - .rules(entitiesToDto(filterEntity.getRules())) + .combinator(expertRuleEntity.getCombinator()) + .field(expertRuleEntity.getField()) + .operator(expertRuleEntity.getOperator()) + .rules(entitiesToDto(expertRuleEntity.getRules())) .build(); } case BOOLEAN -> { - ExpertRuleValueEntity booleanFilterEntity = (ExpertRuleValueEntity) filterEntity; + ExpertRuleValueEntity booleanExpertRuleEntity = (ExpertRuleValueEntity) expertRuleEntity; BooleanExpertRule.BooleanExpertRuleBuilder ruleBuilder = BooleanExpertRule.builder() - .field(booleanFilterEntity.getField()) - .operator(booleanFilterEntity.getOperator()); - if (booleanFilterEntity.getValue() != null) { - ruleBuilder.value(Boolean.parseBoolean(booleanFilterEntity.getValue())); + .field(booleanExpertRuleEntity.getField()) + .operator(booleanExpertRuleEntity.getOperator()); + if (booleanExpertRuleEntity.getValue() != null) { + ruleBuilder.value(Boolean.parseBoolean(booleanExpertRuleEntity.getValue())); } return ruleBuilder.build(); } case NUMBER -> { - ExpertRuleValueEntity numberFilterEntity = (ExpertRuleValueEntity) filterEntity; + ExpertRuleValueEntity numberExpertEntity = (ExpertRuleValueEntity) expertRuleEntity; NumberExpertRule.NumberExpertRuleBuilder ruleBuilder = NumberExpertRule.builder() - .field(filterEntity.getField()) - .operator(filterEntity.getOperator()); - if (numberFilterEntity.getValue() != null) { - if (isMultipleCriteriaOperator(numberFilterEntity.getOperator())) { // for multiple values - ruleBuilder.values(Stream.of(numberFilterEntity.getValue().split(",")).map(Double::valueOf).collect(Collectors.toSet())); + .field(numberExpertEntity.getField()) + .operator(numberExpertEntity.getOperator()); + if (numberExpertEntity.getValue() != null) { + if (isMultipleCriteriaOperator(numberExpertEntity.getOperator())) { // for multiple values + ruleBuilder.values(Stream.of(numberExpertEntity.getValue().split(",")).map(Double::valueOf).collect(Collectors.toSet())); } else { // for single value - ruleBuilder.value(Double.valueOf(numberFilterEntity.getValue())); + ruleBuilder.value(Double.valueOf(numberExpertEntity.getValue())); } } return ruleBuilder.build(); } case STRING -> { - ExpertRuleValueEntity stringFilterEntity = (ExpertRuleValueEntity) filterEntity; + ExpertRuleValueEntity stringExpertRuleEntity = (ExpertRuleValueEntity) expertRuleEntity; StringExpertRule.StringExpertRuleBuilder ruleBuilder = StringExpertRule.builder() - .field(filterEntity.getField()) - .operator(filterEntity.getOperator()); - if (stringFilterEntity.getValue() != null) { - if (isMultipleCriteriaOperator(stringFilterEntity.getOperator())) { // for multiple values - ruleBuilder.values(Stream.of(stringFilterEntity.getValue().split(",")).collect(Collectors.toSet())); + .field(stringExpertRuleEntity.getField()) + .operator(stringExpertRuleEntity.getOperator()); + if (stringExpertRuleEntity.getValue() != null) { + if (isMultipleCriteriaOperator(stringExpertRuleEntity.getOperator())) { // for multiple values + ruleBuilder.values(Stream.of(stringExpertRuleEntity.getValue().split(",")).collect(Collectors.toSet())); } else { // for single value - ruleBuilder.value(stringFilterEntity.getValue()); + ruleBuilder.value(stringExpertRuleEntity.getValue()); } } return ruleBuilder.build(); } case ENUM -> { - ExpertRuleValueEntity enumFilterEntity = (ExpertRuleValueEntity) filterEntity; + ExpertRuleValueEntity enumExpertRuleEntity = (ExpertRuleValueEntity) expertRuleEntity; EnumExpertRule.EnumExpertRuleBuilder ruleBuilder = EnumExpertRule.builder() - .field(enumFilterEntity.getField()) - .operator(enumFilterEntity.getOperator()); - if (enumFilterEntity.getValue() != null) { - if (isMultipleCriteriaOperator(enumFilterEntity.getOperator())) { // for multiple values - ruleBuilder.values(Stream.of(enumFilterEntity.getValue().split(",")).collect(Collectors.toSet())); + .field(enumExpertRuleEntity.getField()) + .operator(enumExpertRuleEntity.getOperator()); + if (enumExpertRuleEntity.getValue() != null) { + if (isMultipleCriteriaOperator(enumExpertRuleEntity.getOperator())) { // for multiple values + ruleBuilder.values(Stream.of(enumExpertRuleEntity.getValue().split(",")).collect(Collectors.toSet())); } else { // for single value - ruleBuilder.value(enumFilterEntity.getValue()); + ruleBuilder.value(enumExpertRuleEntity.getValue()); } } return ruleBuilder.build(); } case FILTER_UUID -> { - ExpertRuleValueEntity filterUuidFilterEntity = (ExpertRuleValueEntity) filterEntity; + ExpertRuleValueEntity filterUuidExpertRuleEntity = (ExpertRuleValueEntity) expertRuleEntity; FilterUuidExpertRule.FilterUuidExpertRuleBuilder ruleBuilder = FilterUuidExpertRule.builder() - .field(filterEntity.getField()) - .operator(filterEntity.getOperator()); - if (filterUuidFilterEntity.getValue() != null) { - if (isMultipleCriteriaOperator(filterUuidFilterEntity.getOperator())) { // for multiple values - ruleBuilder.values(Stream.of(filterUuidFilterEntity.getValue().split(",")).collect(Collectors.toSet())); + .field(filterUuidExpertRuleEntity.getField()) + .operator(filterUuidExpertRuleEntity.getOperator()); + if (filterUuidExpertRuleEntity.getValue() != null) { + if (isMultipleCriteriaOperator(filterUuidExpertRuleEntity.getOperator())) { // for multiple values + ruleBuilder.values(Stream.of(filterUuidExpertRuleEntity.getValue().split(",")).collect(Collectors.toSet())); } else { // for single value - ruleBuilder.value(filterUuidFilterEntity.getValue()); + ruleBuilder.value(filterUuidExpertRuleEntity.getValue()); } } return ruleBuilder.build(); } case PROPERTIES -> { - ExpertRulePropertiesEntity propertiesFilterEntity = (ExpertRulePropertiesEntity) filterEntity; + ExpertRulePropertiesEntity propertiesExpertRuleEntity = (ExpertRulePropertiesEntity) expertRuleEntity; return PropertiesExpertRule.builder() - .field(propertiesFilterEntity.getField()) - .operator(propertiesFilterEntity.getOperator()) - .propertyValues(propertiesFilterEntity.getPropertyValues()) - .propertyName(propertiesFilterEntity.getPropertyName()) + .field(propertiesExpertRuleEntity.getField()) + .operator(propertiesExpertRuleEntity.getOperator()) + .propertyValues(propertiesExpertRuleEntity.getPropertyValues()) + .propertyName(propertiesExpertRuleEntity.getPropertyName()) .build(); } default -> - throw new PowsyblException("Unknown rule data type: " + filterEntity.getDataType() + ", supported data types are: " + Arrays.stream(DataType.values()).map(Enum::name).collect(Collectors.joining(", "))); + throw new PowsyblException("Unknown rule data type: " + expertRuleEntity.getDataType() + ", supported data types are: " + Arrays.stream(DataType.values()).map(Enum::name).collect(Collectors.joining(", "))); } } diff --git a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java index 63f9d909..5805d111 100644 --- a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java +++ b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java @@ -39,7 +39,10 @@ import org.gridsuite.filter.server.repositories.proxies.AbstractFilterRepositoryProxy; import org.gridsuite.filter.server.utils.FieldsMatcher; import org.gridsuite.filter.server.utils.MatcherJson; -import org.gridsuite.filter.utils.*; +import org.gridsuite.filter.server.utils.assertions.Assertions; +import org.gridsuite.filter.utils.EquipmentType; +import org.gridsuite.filter.utils.FilterType; +import org.gridsuite.filter.utils.RangeType; import org.gridsuite.filter.utils.expertfilter.CombinatorType; import org.gridsuite.filter.utils.expertfilter.FieldType; import org.gridsuite.filter.utils.expertfilter.OperatorType; @@ -59,11 +62,13 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.util.*; +import java.util.stream.Stream; import static org.apache.commons.lang3.StringUtils.join; import static org.gridsuite.filter.server.repositories.proxies.AbstractFilterRepositoryProxy.WRONG_FILTER_TYPE; @@ -244,14 +249,14 @@ public void testLineFilter() throws Exception { assertThrows("Network '" + NETWORK_NOT_FOUND_UUID + "' not found", ServletException.class, () -> mvc.perform(get(URL_TEMPLATE + "/" + filterId1 + "/export?networkUuid=" + NETWORK_NOT_FOUND_UUID) .contentType(APPLICATION_JSON))); - mvc.perform(get(URL_TEMPLATE + "/" + filterId1 + "/export?networkUuid=" + NETWORK_UUID) + mvc.perform(get(URL_TEMPLATE + "/" + filterId1 + "/export").param("networkUuid", NETWORK_UUID.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) .andExpect(content().json("[{\"id\":\"NHV1_NHV2_1\",\"type\":\"LINE\"}]")); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", filterId1) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", filterId1.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -289,7 +294,7 @@ public void testLineFilter() throws Exception { matchFilterInfos(filterAttributes.get(0), filterId1, FilterType.CRITERIA, EquipmentType.HVDC_LINE, modificationDate); filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", filterId1) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", filterId1.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -308,7 +313,7 @@ public void testLineFilter() throws Exception { modifyFormFilter(filterId1, generatorFormFilter, userId); filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", filterId1) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", filterId1.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -702,9 +707,8 @@ public void testSubstationFilter() throws Exception { } @Test - public void testDuplicateFilter() throws Exception { + public void testLineFilterCrud() throws Exception { UUID filterId1 = UUID.fromString("99999999-e0c4-413a-8e3e-78e9027d300f"); - Date modificationDate = new Date(); LineFilter lineFilter = LineFilter.builder().equipmentID("equipmentID").equipmentName("equipmentName") .substationName1("substationName1") .substationName2("substationName2").countries1(COUNTRIES1).countries2(COUNTRIES2) @@ -712,13 +716,67 @@ public void testDuplicateFilter() throws Exception { .nominalVoltage2(new NumericalFilter(RangeType.EQUALITY, 6., null)) .build(); CriteriaFilter lineCriteriaFilter = new CriteriaFilter( - filterId1, - modificationDate, + null, + new Date(), lineFilter ); + + // --- insert filter --- // insertFilter(filterId1, lineCriteriaFilter); - mvc.perform(post("/" + FilterApi.API_VERSION + "/filters?duplicateFrom=" + filterId1)).andExpect(status().isOk()); + + // check the inserted filter + lineCriteriaFilter.setId(filterId1); checkFormFilter(filterId1, lineCriteriaFilter); + + // --- duplicate filter -- // + UUID newFilterId1 = duplicateFilter(filterId1); + + // check the duplicated filter whether it is matched to the original + lineCriteriaFilter.setId(newFilterId1); + checkFormFilter(newFilterId1, lineCriteriaFilter); + + // --- modify filter --- // + LineFilter lineFilter2 = LineFilter.builder().equipmentID("equipmentID").equipmentName("equipmentName") + .substationName1("substationName1") + .substationName2("substationName2").countries1(COUNTRIES2).countries2(COUNTRIES1) + .nominalVoltage1(new NumericalFilter(RangeType.RANGE, 4., 9.)) + .nominalVoltage2(new NumericalFilter(RangeType.EQUALITY, 5., null)) + .build(); + CriteriaFilter lineCriteriaFilter2 = new CriteriaFilter( + null, + new Date(), + lineFilter2 + ); + updateFilter(filterId1, lineCriteriaFilter2, "userId"); + + // check the modified filter + lineCriteriaFilter2.setId(filterId1); + checkFormFilter(filterId1, lineCriteriaFilter2); + + // --- modify filter with equipment type changed --- // + GeneratorFilter generatorFilter = GeneratorFilter.builder().equipmentID("eqId1").equipmentName("gen1") + .substationName("s1") + .countries(new TreeSet<>(Set.of("FR", "BE"))) + .nominalVoltage(new NumericalFilter(RangeType.RANGE, 50., null)) + .build(); + CriteriaFilter generatorCriteriaFilter = new CriteriaFilter( + null, + new Date(), + generatorFilter + ); + updateFilter(filterId1, generatorCriteriaFilter, "userId"); + + // check the modified filter + generatorCriteriaFilter.setId(filterId1); + checkFormFilter(filterId1, generatorCriteriaFilter); + + // --- delete filters --- // + deleteFilter(filterId1); + deleteFilter(newFilterId1); + + // check empty after delete all + List allFilters = getAllFilters(); + Assertions.assertThat(allFilters).isEmpty(); } @Test @@ -868,7 +926,7 @@ public void testExportFilters() throws Exception { params.add("variantId", VARIANT_ID_1); List filterEquipments = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/export").params(params) + mvc.perform(get(URL_TEMPLATE + "/export").params(params) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -950,7 +1008,7 @@ public void testGetIdentifiablesCount() throws Exception { "ids[g4]", List.of(UUID.randomUUID().toString()) )); Map identifiablesCount = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/identifiables-count") + mvc.perform(get(URL_TEMPLATE + "/identifiables-count") .params(params) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) @@ -1001,14 +1059,14 @@ private void checkIdentifiableAttributes(List identifiab } private void checkIdentifierListFilterExportAndMetadata(UUID filterId, String expectedJson, EquipmentType equipmentType) throws Exception { - mvc.perform(get(URL_TEMPLATE + "/" + filterId + "/export?networkUuid=" + NETWORK_UUID) + mvc.perform(get(URL_TEMPLATE + "/" + filterId + "/export").param("networkUuid", NETWORK_UUID.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) .andExpect(content().json(expectedJson)); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", filterId) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", filterId.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1022,14 +1080,14 @@ private void checkIdentifierListFilterExportAndMetadata(UUID filterId, String ex } private void checkExpertFilterExportAndMetadata(UUID filterId, String expectedJson, EquipmentType equipmentType) throws Exception { - mvc.perform(get(URL_TEMPLATE + "/" + filterId + "/export?networkUuid=" + NETWORK_UUID) + mvc.perform(get(URL_TEMPLATE + "/" + filterId + "/export").param("networkUuid", NETWORK_UUID.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) .andExpect(content().json(expectedJson)); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", filterId) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", filterId.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1041,11 +1099,11 @@ private void checkExpertFilterExportAndMetadata(UUID filterId, String expectedJs assertEquals(FilterType.EXPERT, filterAttributes.get(0).getType()); assertEquals(equipmentType, filterAttributes.get(0).getEquipmentType()); - mvc.perform(delete(URL_TEMPLATE + "/" + filterId)).andExpect(status().isOk()); + deleteFilter(filterId); } private void checkFilterEvaluating(AbstractFilter filter, String expectedJson) throws Exception { - mvc.perform(post(URL_TEMPLATE + "/evaluate?networkUuid=" + NETWORK_UUID) + mvc.perform(post(URL_TEMPLATE + "/evaluate").param("networkUuid", NETWORK_UUID.toString()) .content(objectMapper.writeValueAsString(filter)) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) @@ -1061,6 +1119,71 @@ private AbstractFilter insertFilter(UUID filterId, AbstractFilter filter) throws return objectMapper.readValue(response, AbstractFilter.class); } + private List insertFilters(Map filtersToCreateMap) throws Exception { + String response = mvc.perform(post(URL_TEMPLATE + "/batch") + .content(objectMapper.writeValueAsString(filtersToCreateMap)) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + return objectMapper.readValue(response, new TypeReference<>() { }); + } + + private UUID duplicateFilter(UUID filterId) throws Exception { + String response = mvc.perform(post(URL_TEMPLATE).param("duplicateFrom", filterId.toString())) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + return objectMapper.readValue(response, UUID.class); + } + + private Map duplicateFilters(List sourceFilterUuids) throws Exception { + String response = mvc.perform(post(URL_TEMPLATE + "/duplicate/batch") + .content(objectMapper.writeValueAsString(sourceFilterUuids)) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + return objectMapper.readValue(response, new TypeReference<>() { }); + } + + private void updateFilter(UUID filterId, AbstractFilter filter, String userId) throws Exception { + mvc.perform(put(URL_TEMPLATE + "/" + filterId) + .content(objectMapper.writeValueAsString(filter)) + .contentType(APPLICATION_JSON) + .header(USER_ID_HEADER, userId)) + .andExpect(status().isOk()); + checkElementUpdatedMessageSent(filterId, userId); + } + + private List updateFilters(Map filtersToUpdateMap) throws Exception { + String response = mvc.perform(put(URL_TEMPLATE + "/batch") + .content(objectMapper.writeValueAsString(filtersToUpdateMap)) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + return objectMapper.readValue(response, new TypeReference<>() { }); + } + + private void updateFiltersWithNoneExistingId(Map filtersToUpdateMap) throws Exception { + mvc.perform(put(URL_TEMPLATE + "/batch") + .content(objectMapper.writeValueAsString(filtersToUpdateMap)) + .contentType(APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } + + private void deleteFilter(UUID filterId) throws Exception { + mvc.perform(delete(URL_TEMPLATE + "/" + filterId)).andExpect(status().isOk()); + } + + private void deleteFilters(List filterUuids) throws Exception { + mvc.perform(delete(URL_TEMPLATE) + .content(objectMapper.writeValueAsString(filterUuids)) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + private List getAllFilters() throws Exception { + String response = mvc.perform(get(URL_TEMPLATE) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + return objectMapper.readValue(response, new TypeReference<>() { }); + } + private void modifyFormFilter(UUID filterId, AbstractFilter newFilter, String userId) throws Exception { mvc.perform(put(URL_TEMPLATE + "/" + filterId) .content(objectMapper.writeValueAsString(newFilter)) @@ -1147,7 +1270,7 @@ private void insertInjectionFilter(EquipmentType equipmentType, UUID id, String checkFormFilter(id, injectionFilter); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1159,13 +1282,18 @@ private void insertInjectionFilter(EquipmentType equipmentType, UUID id, String assertEquals(FilterType.CRITERIA, filterAttributes.get(0).getType()); assertEquals(equipmentType, filterAttributes.get(0).getEquipmentType()); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } + + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) .andExpect(content().json(expectedJsonExport)); - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + deleteFilter(id); } private void insertTransformerFilter(EquipmentType equipmentType, UUID id, String equipmentID, String equipmentName, @@ -1206,7 +1334,7 @@ private void insertTransformerFilter(EquipmentType equipmentType, UUID id, Strin checkFormFilter(id, transformerFilter); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1218,13 +1346,18 @@ private void insertTransformerFilter(EquipmentType equipmentType, UUID id, Strin assertEquals(FilterType.CRITERIA, filterAttributes.get(0).getType()); assertEquals(equipmentType, filterAttributes.get(0).getEquipmentType()); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) - .andExpect(content().json(expectedJsonExport)); + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } + + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) + .andExpect(content().json(expectedJsonExport)); - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + deleteFilter(id); } private void insertHvdcLineFilter(UUID id, String equipmentID, String equipmentName, @@ -1257,7 +1390,7 @@ private void insertHvdcLineFilter(UUID id, String equipmentID, String equipmentN matchFilterInfos(filterAttributes.get(0), id, FilterType.CRITERIA, EquipmentType.HVDC_LINE, modificationDate); filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1267,16 +1400,21 @@ private void insertHvdcLineFilter(UUID id, String equipmentID, String equipmentN assertEquals(1, filterAttributes.size()); matchFilterInfos(filterAttributes.get(0), id, FilterType.CRITERIA, EquipmentType.HVDC_LINE, modificationDate); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) - .andExpect(content().json(expectedJsonExport)); + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) + .andExpect(content().json(expectedJsonExport)); + + deleteFilter(id); filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1315,7 +1453,7 @@ private CriteriaFilter insertLineFilter(UUID id, String equipmentID, String equi insertFilter(id, filter); checkFormFilter(id, filter); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1327,13 +1465,18 @@ private CriteriaFilter insertLineFilter(UUID id, String equipmentID, String equi assertEquals(FilterType.CRITERIA, filterAttributes.get(0).getType()); assertEquals(EquipmentType.LINE, filterAttributes.get(0).getEquipmentType()); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } + + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) .andExpect(content().json(expectedJsonExport)); if (delete) { - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + deleteFilter(id); } return filter; } @@ -1362,7 +1505,7 @@ private void insertVoltageLevelFilter(UUID id, String equipmentID, String equipm checkFormFilter(id, filter); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1373,13 +1516,18 @@ private void insertVoltageLevelFilter(UUID id, String equipmentID, String equipm assertEquals(id, filterAttributes.get(0).getId()); assertEquals(FilterType.CRITERIA, filterAttributes.get(0).getType()); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) - .andExpect(content().json(expectedJsonExport)); + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) + .andExpect(content().json(expectedJsonExport)); + + deleteFilter(id); } private void insertSubstationFilter(UUID id, String equipmentID, String equipmentName, Set countries, @@ -1403,7 +1551,7 @@ private void insertSubstationFilter(UUID id, String equipmentID, String equipmen checkFormFilter(id, filter); List filterAttributes = objectMapper.readValue( - mvc.perform(get("/" + FilterApi.API_VERSION + "/filters/metadata?ids={id}", id) + mvc.perform(get(URL_TEMPLATE + "/metadata").param("ids", id.toString()) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -1414,13 +1562,18 @@ private void insertSubstationFilter(UUID id, String equipmentID, String equipmen assertEquals(id, filterAttributes.get(0).getId()); assertEquals(FilterType.CRITERIA, filterAttributes.get(0).getType()); - mvc.perform(get(URL_TEMPLATE + "/" + id + "/export?networkUuid=" + networkUuid + (variantId != null ? "&variantId=" + variantId : "")) - .contentType(APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) - .andExpect(content().json(expectedJsonExport)); + MockHttpServletRequestBuilder requestBuilder = get(URL_TEMPLATE + "/" + id + "/export") + .param("networkUuid", networkUuid.toString()); + if (variantId != null) { + requestBuilder.param("variantId", variantId); + } - mvc.perform(delete(URL_TEMPLATE + "/" + id)).andExpect(status().isOk()); + mvc.perform(requestBuilder.contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) + .andExpect(content().json(expectedJsonExport)); + + deleteFilter(id); } private void checkFormFilter(UUID filterId, CriteriaFilter criteriaFilter) throws Exception { @@ -1471,7 +1624,7 @@ private void matchIdentifierListFilterInfos(IdentifierListFilter identifierListF private void matchExpertFilterInfos(ExpertFilter expertFilter1, ExpertFilter expertFilter2) { matchFilterInfos(expertFilter1, expertFilter2); - assertTrue(new MatcherJson<>(objectMapper, expertFilter2.getRules()).matchesSafely(expertFilter1.getRules())); + Assertions.assertThat(expertFilter1).recursivelyEquals(expertFilter2, "topologyKind" /* not persisted field */); } private void checkElementUpdatedMessageSent(UUID elementUuid, String userId) { @@ -1994,4 +2147,121 @@ public void testExpertFilterLoadLinkToOtherFilterWithIsPartOfOperator() throws E checkExpertFilterExportAndMetadata(expertFilterId, expectedResultJson, EquipmentType.LOAD); checkFilterEvaluating(expertFilter, expectedResultJson); } + + @Test + public void testLineFiltersCrudInBatch() throws Exception { + UUID filterId1 = UUID.randomUUID(); + LineFilter lineFilter1 = LineFilter.builder().equipmentID("equipmentID1").equipmentName("equipmentName1") + .substationName1("substationName1") + .substationName2("substationName2").countries1(COUNTRIES1).countries2(COUNTRIES2) + .nominalVoltage1(new NumericalFilter(RangeType.RANGE, 5., 8.)) + .nominalVoltage2(new NumericalFilter(RangeType.EQUALITY, 6., null)) + .build(); + CriteriaFilter lineCriteriaFilter1 = new CriteriaFilter( + null, + new Date(), + lineFilter1 + ); + + UUID filterId2 = UUID.randomUUID(); + LineFilter lineFilter2 = LineFilter.builder().equipmentID("equipmentID2").equipmentName("equipmentName2") + .substationName1("substationName3").countries1(COUNTRIES1).countries2(COUNTRIES2) + .substationName2("substationName4") + .nominalVoltage1(new NumericalFilter(RangeType.RANGE, 4., 9.)) + .nominalVoltage2(new NumericalFilter(RangeType.EQUALITY, 5., null)) + .build(); + CriteriaFilter lineCriteriaFilter2 = new CriteriaFilter( + null, + new Date(), + lineFilter2 + ); + + Map filtersToCreateMap = Map.of( + filterId1, lineCriteriaFilter1, + filterId2, lineCriteriaFilter2 + ); + + // --- insert in batch --- // + insertFilters(filtersToCreateMap); + + // check inserted filters + lineCriteriaFilter1.setId(filterId1); + checkFormFilter(filterId1, lineCriteriaFilter1); + lineCriteriaFilter2.setId(filterId2); + checkFormFilter(filterId2, lineCriteriaFilter2); + + // --- duplicate in batch --- // + Map sourceAndNewUuidMap = duplicateFilters(List.of(filterId1, filterId2)); + + sourceAndNewUuidMap.forEach((sourceUuid, newUuid) -> filtersToCreateMap.get(sourceUuid).setId(newUuid)); + + // check each duplicated filter whether it is matched to the original + for (Map.Entry entry : sourceAndNewUuidMap.entrySet()) { + UUID sourceUuid = entry.getKey(); + UUID newUuid = entry.getValue(); + checkFormFilter(newUuid, (CriteriaFilter) filtersToCreateMap.get(sourceUuid)); + } + + // --- modify filters in batch --- // + LineFilter lineFilter3 = LineFilter.builder().equipmentID("equipmentID").equipmentName("equipmentName") + .substationName1("substationName1") + .substationName2("substationName2").countries1(COUNTRIES2).countries2(COUNTRIES1) + .nominalVoltage1(new NumericalFilter(RangeType.RANGE, 3., 10.)) + .nominalVoltage2(new NumericalFilter(RangeType.EQUALITY, 4., null)) + .build(); + CriteriaFilter lineCriteriaFilter3 = new CriteriaFilter( + null, + new Date(), + lineFilter3 + ); + GeneratorFilter generatorFilter = GeneratorFilter.builder().equipmentID("eqId1").equipmentName("gen1") + .substationName("s1") + .countries(new TreeSet<>(Set.of("FR", "BE"))) + .nominalVoltage(new NumericalFilter(RangeType.RANGE, 50., null)) + .build(); + CriteriaFilter generatorCriteriaFilter = new CriteriaFilter( + null, + new Date(), + generatorFilter + ); + Map filtersToUpdateMap = Map.of( + filterId1, lineCriteriaFilter3, + filterId2, generatorCriteriaFilter + ); + updateFilters(filtersToUpdateMap); + + // check modified filters + lineCriteriaFilter3.setId(filterId1); + checkFormFilter(filterId1, lineCriteriaFilter3); + generatorCriteriaFilter.setId(filterId2); + checkFormFilter(filterId2, generatorCriteriaFilter); + + // --- modify filters in batch with a none existing id --- // + GeneratorFilter generatorFilter2 = GeneratorFilter.builder().equipmentID("eqId1").equipmentName("gen1") + .substationName("s1") + .countries(new TreeSet<>(Set.of("FR", "BE"))) + .nominalVoltage(new NumericalFilter(RangeType.RANGE, 60., null)) + .build(); + CriteriaFilter generatorCriteriaFilter2 = new CriteriaFilter( + null, + new Date(), + generatorFilter2 + ); + + Map filtersToUpdateMap2 = Map.of( + UUID.randomUUID(), lineCriteriaFilter3, + filterId2, generatorCriteriaFilter2 + ); + updateFiltersWithNoneExistingId(filtersToUpdateMap2); + // check modified filters => filter with filterId2 should not be changed + checkFormFilter(filterId2, generatorCriteriaFilter); + + // --- delete filters in batch -- // + deleteFilters(Stream.concat(sourceAndNewUuidMap.keySet().stream(), sourceAndNewUuidMap.values().stream()).toList()); + + // check empty after delete all + List allFilters = getAllFilters(); + Assertions.assertThat(allFilters).isEmpty(); + } + } diff --git a/src/test/java/org/gridsuite/filter/server/utils/assertions/Assertions.java b/src/test/java/org/gridsuite/filter/server/utils/assertions/Assertions.java new file mode 100644 index 00000000..ddf8c5a8 --- /dev/null +++ b/src/test/java/org/gridsuite/filter/server/utils/assertions/Assertions.java @@ -0,0 +1,21 @@ +/** + * 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.server.utils.assertions; + +import org.assertj.core.util.CheckReturnValue; +import org.gridsuite.filter.expertfilter.ExpertFilter; + +/** + * @author Thang PHAM + * {@link org.assertj.core.api.Assertions Assertions} completed with our custom assertions classes. + */ +public class Assertions extends org.assertj.core.api.Assertions { + @CheckReturnValue + public static DTOAssert assertThat(T actual) { + return new DTOAssert<>(actual); + } +} diff --git a/src/test/java/org/gridsuite/filter/server/utils/assertions/DTOAssert.java b/src/test/java/org/gridsuite/filter/server/utils/assertions/DTOAssert.java new file mode 100644 index 00000000..cc6005f8 --- /dev/null +++ b/src/test/java/org/gridsuite/filter/server/utils/assertions/DTOAssert.java @@ -0,0 +1,38 @@ +/** + * 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.server.utils.assertions; + +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; + +import java.time.Instant; +import java.util.Date; +import java.util.UUID; + +/** + * @author Thang PHAM + */ +public class DTOAssert extends AbstractAssert, T> { + public DTOAssert(T actual) { + super(actual, DTOAssert.class); + } + + public DTOAssert recursivelyEquals(T other, String... fieldsToIgnore) { + isNotNull(); + usingRecursiveComparison(this.getRecursiveConfiguration(fieldsToIgnore)).isEqualTo(other); + return myself; + } + + private RecursiveComparisonConfiguration getRecursiveConfiguration(String... fieldsToIgnore) { + return RecursiveComparisonConfiguration.builder() + .withIgnoreAllOverriddenEquals(true) // For equals test, need specific tests + .withIgnoredFieldsOfTypes(UUID.class, Date.class, Instant.class) // For these types, need specific tests (uuid from db for example) + .withIgnoreCollectionOrder(true) // For collection order test, need specific tests + .withIgnoredFields(fieldsToIgnore) + .build(); + } +}