Added ability to add election results to DB

This commit is contained in:
2023-07-13 19:45:22 +02:00
parent aa533f6c72
commit 0336825cd1
9 changed files with 306 additions and 0 deletions
@@ -0,0 +1,88 @@
package de.twomartens.wahlrecht.controller.v1;
import de.twomartens.wahlrecht.mapper.v1.ElectionResultMapper;
import de.twomartens.wahlrecht.model.dto.ErrorMessage;
import de.twomartens.wahlrecht.model.dto.v1.ElectionResult;
import de.twomartens.wahlrecht.service.ElectionResultService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.mapstruct.factory.Mappers;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/wahlrecht/v1")
@Tag(name = "ElectionResults", description = "all requests relating to election results")
public class ElectionResultController {
private final ElectionResultMapper mapper = Mappers.getMapper(ElectionResultMapper.class);
private final ElectionResultService service;
@Operation(
summary = "Returns election result for given election name",
responses = {
@ApiResponse(responseCode = "200",
description = "Returns stored election result for given election name",
content = {@Content(
mediaType = "application/json",
schema = @Schema(implementation = ElectionResult.class))}
),
@ApiResponse(responseCode = "404",
description = "No election result found",
content = {
@Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorMessage.class)
)
}
)
}
)
@GetMapping(value = "/electionResult/by-election-name/{electionName}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ElectionResult> getElectionByName(
@PathVariable @Parameter(description = "the election name", example = "Bezirkswahl 2019") String electionName) {
ElectionResult result = mapper.mapToExternal(service.getElectionResult(electionName));
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(result);
}
@Operation(
summary = "Stores a new election",
responses = {
@ApiResponse(responseCode = "201",
description = "Election was created"),
@ApiResponse(responseCode = "200",
description = "Election was modified"),
@ApiResponse(responseCode = "401",
description = "Unauthorized",
content = {@Content(
mediaType = "plain/text")}
)
}
)
@PutMapping("/electionResult")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<?> putElection(@RequestBody ElectionResult electionResult) {
boolean createdNew = service.storeResult(mapper.mapToDB(electionResult));
return createdNew
? new ResponseEntity<>(HttpStatus.CREATED)
: new ResponseEntity<>(HttpStatus.OK);
}
}
@@ -0,0 +1,44 @@
package de.twomartens.wahlrecht.mapper.v1;
import de.twomartens.wahlrecht.model.internal.ElectionResult;
import de.twomartens.wahlrecht.model.internal.VotingResult;
import java.util.Collection;
import java.util.Map;
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.NullValueCheckStrategy;
import org.mapstruct.ReportingPolicy;
@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface ElectionResultMapper {
de.twomartens.wahlrecht.model.dto.v1.ElectionResult mapToExternal(de.twomartens.wahlrecht.model.db.ElectionResult result);
Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult> mapToExternal(
Collection<de.twomartens.wahlrecht.model.db.VotingResult> results);
Map<Integer, Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult>> mapToExternal(
Map<Integer, Collection<de.twomartens.wahlrecht.model.db.VotingResult>> results);
@Mapping(target = "id", ignore = true)
@Mapping(target = "created", ignore = true)
@Mapping(target = "lastModified", ignore = true)
de.twomartens.wahlrecht.model.db.ElectionResult mapToDB(de.twomartens.wahlrecht.model.dto.v1.ElectionResult result);
Collection<de.twomartens.wahlrecht.model.db.VotingResult> mapToDB(
Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult> results);
Map<Integer, Collection<de.twomartens.wahlrecht.model.db.VotingResult>> mapToDB(
Map<Integer, Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult>> results);
ElectionResult mapToInternal(de.twomartens.wahlrecht.model.dto.v1.ElectionResult result);
Collection<VotingResult> mapToInternal(
Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult> results);
Map<Integer, Collection<VotingResult>> mapToInternal(
Map<Integer, Collection<de.twomartens.wahlrecht.model.dto.v1.VotingResult>> results);
}
@@ -0,0 +1,44 @@
package de.twomartens.wahlrecht.model.db;
import java.time.Instant;
import java.util.Collection;
import java.util.Map;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ElectionResult {
@EqualsAndHashCode.Exclude
@Id
ObjectId id;
@EqualsAndHashCode.Exclude
@CreatedDate
Instant created;
@EqualsAndHashCode.Exclude
@LastModifiedDate
Instant lastModified;
String electionName;
Collection<VotingResult> overallResults;
Map<Integer, Collection<VotingResult>> constituencyResults;
}
@@ -0,0 +1,29 @@
package de.twomartens.wahlrecht.model.db;
import java.util.Map;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class VotingResult {
String electionName;
String partyAbbreviation;
String nominationName;
int votesOnNomination;
int votesThroughHealing;
Map<Integer, Integer> votesPerPosition;
}
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.Collection;
import java.util.Map;
public record ElectionResult(String electionName, Collection<VotingResult> overallResults,
Map<Integer, Collection<VotingResult>> constituencyResults) {
}
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.model.internal;
import java.util.Collection;
import java.util.Map;
public record ElectionResult(String electionName, Collection<VotingResult> overallResults,
Map<Integer, Collection<VotingResult>> constituencyResults) {
}
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.repository;
import de.twomartens.wahlrecht.model.db.ElectionResult;
import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface ElectionResultRepository extends MongoRepository<ElectionResult, String> {
Optional<ElectionResult> findByElectionName(String name);
}
@@ -0,0 +1,8 @@
package de.twomartens.wahlrecht.repository;
import de.twomartens.wahlrecht.model.db.VotingResult;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface VotingResultRepository extends MongoRepository<VotingResult, String> {
}
@@ -0,0 +1,66 @@
package de.twomartens.wahlrecht.service;
import de.twomartens.wahlrecht.model.db.ElectionResult;
import de.twomartens.wahlrecht.repository.ElectionResultRepository;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
@RequiredArgsConstructor
@Service
public class ElectionResultService {
private final ElectionResultRepository repository;
private Map<String, ElectionResult> electionResults;
public ElectionResult getElectionResult(String electionName) {
if (electionResults == null) {
fetchResults();
}
ElectionResult electionResult = electionResults.get(electionName);
if (electionResult == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
"no election result found for %s".formatted(electionName));
}
return electionResult;
}
public boolean storeResult(ElectionResult result) {
if (electionResults == null) {
fetchResults();
}
String id = result.getElectionName();
ElectionResult existing = electionResults.get(id);
boolean needsUpdate = !result.equals(existing);
if (!needsUpdate) {
return false;
}
boolean createdNew = true;
if (existing != null) {
existing.setOverallResults(result.getOverallResults());
existing.setConstituencyResults(result.getConstituencyResults());
result = existing;
createdNew = false;
}
ElectionResult stored = repository.save(result);
electionResults.put(id, stored);
return createdNew;
}
private void fetchResults() {
electionResults = repository.findAll().stream()
.collect(Collectors.toMap(
ElectionResult::getElectionName,
Function.identity()));
}
}