-
Notifications
You must be signed in to change notification settings - Fork 0
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
Changes from 15 commits
fdd3458
6a29931
3680536
2549a85
db4c530
588ae16
dcebe97
c508737
4834285
1190a4b
2251b0c
c0ac2fc
d508e0f
f551b4c
54201b9
e8236a8
2f3c535
0e8609f
1f22f0b
d11226a
cb40b78
6c2ba17
bd50593
0713dfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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 provided uuids") | ||||||
@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 uuid of the new filter 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() | ||||||
|
@@ -85,8 +95,19 @@ public ResponseEntity<UUID> duplicateFilter(@RequestParam("duplicateFrom") UUID | |||||
.orElse(ResponseEntity.notFound().build()); | ||||||
} | ||||||
|
||||||
@PostMapping(value = "/filters/batch/duplicate") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding Our rest API best practices are not very precise about extra data like this. But they state that the path should only be used to identify a data. @etiennehomer shouldn't we modify the filters duplication signature in an other PR ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add "duplicate" in the path of duplicate endpoint, i.e. POST /filters/duplicate but we have to changer also in gridexplorer-app. I think we can change in another TS In fact we should have : At least I can change from /filters/batch/duplicate to /filters/duplicate/batch, what do you think??? @etiennehomer @Mathieu-Deharbe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. I think the API you propose is good Thang. So let's change POST /filters/batch/duplicate to POST /filters/duplicate/batch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DONE in 0713dfc |
||||||
@Operation(summary = "Duplicate filters from provided uuids") | ||||||
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The map of sourceUuid and newUuid of duplicated filters"), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, I have a slight change from your suggestion to "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") | ||||||
@Operation(summary = "Modify a filter from a provided fitler uuid and the whole new filter object") | ||||||
@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) { | ||||||
try { | ||||||
|
@@ -97,6 +118,15 @@ public ResponseEntity<Void> changeFilter(@PathVariable UUID id, @RequestBody Abs | |||||
} | ||||||
} | ||||||
|
||||||
@PutMapping(value = "/filters/batch", consumes = MediaType.APPLICATION_JSON_VALUE) | ||||||
@Operation(summary = "Modify filters in batch from a provided map of each filter uuid and the corresponding whole new filter object") | ||||||
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Filters have been successfully modified")}) | ||||||
public ResponseEntity<List<AbstractFilter>> changeFilters(@RequestBody Map<UUID, AbstractFilter> filtersToModifyMap) { | ||||||
Mathieu-Deharbe marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return ResponseEntity.ok() | ||||||
.contentType(MediaType.APPLICATION_JSON) | ||||||
.body(service.changeFilters(filtersToModifyMap)); | ||||||
} | ||||||
|
||||||
@DeleteMapping(value = "/filters/{id}") | ||||||
@Operation(summary = "delete the filter") | ||||||
@ApiResponse(responseCode = "200", description = "The filter has been deleted") | ||||||
|
@@ -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"), | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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; | ||||||
|
@@ -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 not found"; | ||||||
Mathieu-Deharbe marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
private final Map<String, AbstractFilterRepositoryProxy<?, ?>> filterRepositories = new HashMap<>(); | ||||||
|
||||||
|
@@ -156,14 +130,28 @@ public List<AbstractFilter> getFilters(List<UUID> ids) { | |||||
.stream() | ||||||
.flatMap(repository -> repository.getFilters(ids) | ||||||
.stream()) | ||||||
.collect(Collectors.toList()); | ||||||
.toList(); | ||||||
} | ||||||
|
||||||
@Transactional(propagation = Propagation.REQUIRED) | ||||||
public <F extends AbstractFilter> AbstractFilter createFilter(F filter) { | ||||||
return getRepository(filter).insert(filter); | ||||||
} | ||||||
|
||||||
@Transactional(propagation = Propagation.REQUIRED) | ||||||
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); | ||||||
|
@@ -177,7 +165,33 @@ public Optional<UUID> duplicateFilter(UUID sourceFilterId) { | |||||
return Optional.empty(); | ||||||
} | ||||||
|
||||||
private AbstractFilterRepositoryProxy<? extends AbstractFilterEntity, ? extends FilterRepository<? extends AbstractFilterEntity>> getRepository(AbstractFilter filter) { | ||||||
@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()); | ||||||
} | ||||||
|
@@ -205,13 +219,70 @@ public <F extends AbstractFilter> void changeFilter(UUID id, F newFilter, String | |||||
notificationService.emitElementUpdated(id, userId); | ||||||
} | ||||||
|
||||||
@Transactional | ||||||
public List<AbstractFilter> changeFilters(Map<UUID, AbstractFilter> filtersToModifyMap) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is quite complicated. As we can assume only few filters will be updated at one time, you can iterate on filtersToModifyMap and call changeFilter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 0e8609f |
||||||
List<UUID> filterUuids = filtersToModifyMap.keySet().stream().toList(); | ||||||
List<AbstractFilter> oldFilters = getFilters(filterUuids); | ||||||
|
||||||
// check whether found all | ||||||
if (oldFilters.isEmpty() || oldFilters.size() != filterUuids.size()) { | ||||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, FILTER_UUIDS_NOT_FOUND); | ||||||
} | ||||||
|
||||||
// collect filters into two lists which have repository changed or not changed | ||||||
Map<Boolean, List<AbstractFilter>> filtersReposNotChangedOrChanged = oldFilters.stream().collect(Collectors | ||||||
.partitioningBy(oldFilter -> getRepository(oldFilter) == getRepository(filtersToModifyMap.get(oldFilter.getId())))); | ||||||
List<AbstractFilter> filtersReposNotChanged = filtersReposNotChangedOrChanged.get(Boolean.TRUE); | ||||||
List<AbstractFilter> filtersReposChanged = filtersReposNotChangedOrChanged.get(Boolean.FALSE); | ||||||
|
||||||
List<AbstractFilter> changedFilters = new ArrayList<>(); | ||||||
|
||||||
// --- perform change filters which have repository not changed --- // | ||||||
Map<AbstractFilterRepositoryProxy<?, ?>, List<AbstractFilter>> repositoryFiltersMap = filtersReposNotChanged.stream() | ||||||
.map(filter -> { | ||||||
AbstractFilter newFilter = filtersToModifyMap.get(filter.getId()); | ||||||
newFilter.setId(filter.getId()); | ||||||
return newFilter; | ||||||
}) | ||||||
.collect(Collectors.groupingBy(this::getRepository)); | ||||||
repositoryFiltersMap.forEach((repository, subFilters) -> | ||||||
changedFilters.addAll(repository.modifyAll(subFilters.stream().collect(Collectors.toMap(AbstractFilter::getId, filter -> filter))))); | ||||||
|
||||||
// --- perform change filters which have repository changed --- // | ||||||
List<AbstractFilter> filtersReposChangedToModify = filtersReposChanged.stream() | ||||||
.filter(filter -> filter.getType() != FilterType.SCRIPT && | ||||||
filtersToModifyMap.get(filter.getId()).getType() != FilterType.SCRIPT) | ||||||
.toList(); | ||||||
|
||||||
repositoryFiltersMap = filtersReposChangedToModify.stream() | ||||||
.map(filter -> { | ||||||
AbstractFilter newFilter = filtersToModifyMap.get(filter.getId()); | ||||||
newFilter.setId(filter.getId()); | ||||||
return newFilter; | ||||||
}) | ||||||
.collect(Collectors.groupingBy(this::getRepository)); | ||||||
|
||||||
// delete old filters which have repository changed | ||||||
repositoryFiltersMap.forEach((repository, subFilters) -> | ||||||
repository.deleteAllByIds(subFilters.stream().map(AbstractFilter::getId).toList())); | ||||||
// create new filters with existing ids | ||||||
repositoryFiltersMap.forEach((repository, subFilters) -> changedFilters.addAll(repository.insertAll(subFilters))); | ||||||
|
||||||
return changedFilters; | ||||||
} | ||||||
|
||||||
public void deleteFilter(UUID id) { | ||||||
Objects.requireNonNull(id); | ||||||
if (filterRepositories.values().stream().noneMatch(repository -> repository.deleteById(id))) { | ||||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, FILTER_LIST + id + NOT_FOUND); | ||||||
} | ||||||
} | ||||||
|
||||||
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); | ||||||
} | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I have a slight change from your suggestion to "The filter with given id has been successfully duplicated"