Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GridDyna] Add some batch CRUD endpoints #111

Merged
merged 24 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fdd3458
[GridDyna] Add some batch CRUD endpoints
thangqp May 7, 2024
6a29931
Set to current version of filter library
thangqp May 7, 2024
3680536
Update URI path
thangqp May 7, 2024
2549a85
Merge branch 'main' into dynamic_simulation_add_some_batch_crud_endpo…
thangqp May 15, 2024
db4c530
Merge branch 'main' into dynamic_simulation_add_some_batch_crud_endpo…
thangqp May 21, 2024
588ae16
Merge from main
thangqp Jun 5, 2024
dcebe97
Add tests for new endpoints
thangqp Jun 6, 2024
c508737
Sonar
thangqp Jun 6, 2024
4834285
merge from main
thangqp Jun 11, 2024
1190a4b
Add modify all endpoint
thangqp Jun 11, 2024
2251b0c
Add tests
thangqp Jun 11, 2024
c0ac2fc
Update to filter library 1.0.8
thangqp Jun 18, 2024
d508e0f
Merge branch 'main' into dynamic_simulation_add_some_batch_crud_endpo…
thangqp Jun 18, 2024
f551b4c
Update description for endpoints
thangqp Jun 21, 2024
54201b9
Using AssertJ recursive for test expert rules
thangqp Jul 1, 2024
e8236a8
undo set id from dto to entity
thangqp Jul 1, 2024
2f3c535
merge from main
thangqp Jul 1, 2024
0e8609f
Correct as suggest of Etienne
thangqp Jul 1, 2024
1f22f0b
Tiny correction for description
thangqp Jul 1, 2024
d11226a
Sonar Call transactional methods via an injected dependency instead o…
thangqp Jul 1, 2024
cb40b78
Enrich test for update in batch with none existing filter id
thangqp Jul 1, 2024
6c2ba17
Correct following some comments of Mathieu
thangqp Jul 3, 2024
bd50593
Merge branch 'main' into dynamic_simulation_add_some_batch_crud_endpo…
thangqp Jul 5, 2024
0713dfc
change url path in the filte-server endpoint for duplication in batch
thangqp Jul 10, 2024
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
50 changes: 44 additions & 6 deletions src/main/java/org/gridsuite/filter/server/FilterController.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,19 @@ public ResponseEntity<AbstractFilter> 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<List<AbstractFilter>> createFilters(@RequestBody Map<UUID, AbstractFilter> 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<UUID> duplicateFilter(@RequestParam("duplicateFrom") UUID filterId) {
return service.duplicateFilter(filterId).map(newFilterId -> ResponseEntity.ok()
Expand All @@ -85,18 +95,38 @@ public ResponseEntity<UUID> 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<Map<UUID, UUID>> duplicateFilters(@RequestBody List<UUID> filterUuids) {
Map<UUID, UUID> 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<Void> 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<AbstractFilter> 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<List<AbstractFilter>> updateFilters(@RequestBody Map<UUID, AbstractFilter> 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")
Expand All @@ -105,6 +135,14 @@ public ResponseEntity<Void> deleteFilter(@PathVariable("id") UUID id) {
return ResponseEntity.ok().build();
}

@DeleteMapping(value = "/filters")
Mathieu-Deharbe marked this conversation as resolved.
Show resolved Hide resolved
@Operation(summary = "delete the filters")
@ApiResponse(responseCode = "200", description = "The filters have been deleted")
public ResponseEntity<Void> deleteFilters(@RequestBody List<UUID> 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"),
Expand Down
129 changes: 84 additions & 45 deletions src/main/java/org/gridsuite/filter/server/FilterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -81,13 +54,16 @@ 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<String, AbstractFilterRepositoryProxy<?, ?>> filterRepositories = new HashMap<>();

private final NetworkStoreService networkStoreService;

private final NotificationService notificationService;

private final FilterService self;

public FilterService(final ScriptFilterRepository scriptFiltersRepository,
final LineFilterRepository lineFilterRepository,
final GeneratorFilterRepository generatorFilterRepository,
Expand All @@ -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));
Expand All @@ -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<IFilterAttributes> getFilters() {
Expand All @@ -156,53 +134,109 @@ public List<AbstractFilter> getFilters(List<UUID> ids) {
.stream()
.flatMap(repository -> repository.getFilters(ids)
.stream())
.collect(Collectors.toList());
.toList();
}

@Transactional(propagation = Propagation.REQUIRED)
@Transactional
public <F extends AbstractFilter> AbstractFilter createFilter(F filter) {
return getRepository(filter).insert(filter);
}

@Transactional
public List<AbstractFilter> createFilters(List<AbstractFilter> filters) {
if (CollectionUtils.isEmpty(filters)) {
return Collections.emptyList();
}

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

List<AbstractFilter> createdFilters = new ArrayList<>();
repositoryFiltersMap.forEach((repository, subFilters) -> createdFilters.addAll(repository.insertAll(subFilters)));
return createdFilters;
}

@Transactional
public Optional<UUID> duplicateFilter(UUID sourceFilterId) {
Optional<AbstractFilter> sourceFilterOptional = getFilter(sourceFilterId);
if (sourceFilterOptional.isPresent()) {
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<? extends AbstractFilterEntity, ? extends FilterRepository<? extends AbstractFilterEntity>> getRepository(AbstractFilter filter) {
/**
* @return Map of uuids of copied filters and uuids of new filters
*/
@Transactional
public Map<UUID, UUID> duplicateFilters(List<UUID> filterUuids) {
Mathieu-Deharbe marked this conversation as resolved.
Show resolved Hide resolved
Map<UUID, UUID> uuidsMap = new HashMap<>();

List<AbstractFilter> 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<AbstractFilterRepositoryProxy<?, ?>, List<AbstractFilter>> repositoryFiltersMap = sourceFilters.stream()
.collect(Collectors.groupingBy(this::getRepository));

repositoryFiltersMap.forEach(AbstractFilterRepositoryProxy::insertAll);

return uuidsMap;
}

private AbstractFilterRepositoryProxy<? extends AbstractFilterEntity,
? extends FilterRepository<? extends AbstractFilterEntity>> getRepository(AbstractFilter filter) {
if (!filter.getType().equals(FilterType.CRITERIA)) {
return filterRepositories.get(filter.getType().name());
}
return filterRepositories.get(((CriteriaFilter) filter).getEquipmentFilterForm().getEquipmentType().name());
}

@Transactional
public <F extends AbstractFilter> void changeFilter(UUID id, F newFilter, String userId) {
Optional<AbstractFilter> f = getFilter(id);
if (f.isPresent()) {
if (getRepository(f.get()) == getRepository(newFilter)) { // filter type has not changed
getRepository(newFilter).modify(id, newFilter);
public <F extends AbstractFilter> AbstractFilter updateFilter(UUID id, F newFilter, String userId) {
Optional<AbstractFilter> 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<AbstractFilter> updateFilters(Map<UUID, AbstractFilter> filtersToUpdateMap) {
return filtersToUpdateMap.keySet().stream()
.map(filterUuid -> self.updateFilter(filterUuid, filtersToUpdateMap.get(filterUuid), null))
.toList();
}

public void deleteFilter(UUID id) {
Expand All @@ -212,6 +246,11 @@ public void deleteFilter(UUID id) {
}
}

public void deleteFilters(List<UUID> ids) {
Objects.requireNonNull(ids);
filterRepositories.values().forEach(repository -> repository.deleteAllByIds(ids));
Mathieu-Deharbe marked this conversation as resolved.
Show resolved Hide resolved
}

public void deleteAll() {
filterRepositories.values().forEach(AbstractFilterRepositoryProxy::deleteAll);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ public interface FilterRepository<T extends AbstractFilterEntity> extends JpaRep

@Transactional
Integer removeById(UUID id);

@Transactional
void deleteAllByIdIn(List<UUID> ids);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -123,15 +122,24 @@ public AbstractFilter insert(AbstractFilter f) {
return toDto(getRepository().save(fromDto(f)));
}

public void modify(UUID id, AbstractFilter f) {
public List<AbstractFilter> insertAll(List<AbstractFilter> filters) {
List<F> 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<UUID> ids) {
getRepository().deleteAllByIdIn(ids);
}

public void deleteAll() {
getRepository().deleteAll();
}
Expand Down
Loading
Loading