Skip to content

Commit

Permalink
[GridDyna] Add some batch CRUD endpoints (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
thangqp authored Jul 12, 2024
1 parent 9287014 commit 1cb6ca5
Show file tree
Hide file tree
Showing 8 changed files with 573 additions and 156 deletions.
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")
@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) {
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));
}

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

0 comments on commit 1cb6ca5

Please sign in to comment.