diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/configuration/WebSecurityConfiguration.java b/module-server/src/main/java/de/twomartens/wahlrecht/configuration/WebSecurityConfiguration.java index 4a387a7..6416870 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/configuration/WebSecurityConfiguration.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/configuration/WebSecurityConfiguration.java @@ -22,20 +22,20 @@ public class WebSecurityConfiguration { public SecurityFilterChain securityFilterChain(@NonNull HttpSecurity http) throws Exception { // (2) http .authorizeHttpRequests() - .requestMatchers("/wahlrecht/v1/**") + .requestMatchers("/wahlrecht/v1/**", "/wahlrecht/version") .permitAll() .and() .authorizeHttpRequests() .requestMatchers("/resources/**") .permitAll() .and() - .csrf() - .disable() .authorizeHttpRequests() - .requestMatchers("/wahlrecht/v1/election", "/wahlrecht/v1/party") + .requestMatchers("/wahlrecht/v1/election", + "/wahlrecht/v1/party/**", + "/wahlrecht/v1/nomination/**") .authenticated() // (3) .and() - .formLogin(); // (7) + .httpBasic(); // (7) return http.build(); } diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/controller/VersionHtmlController.java b/module-server/src/main/java/de/twomartens/wahlrecht/controller/VersionHtmlController.java index 7296294..2218d7b 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/controller/VersionHtmlController.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/controller/VersionHtmlController.java @@ -15,7 +15,6 @@ import java.util.zip.ZipEntry; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestHeader; @@ -30,65 +29,60 @@ import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping(value = "/wahlrecht") public class VersionHtmlController { - @GetMapping(path = "/html/version.html") + @GetMapping(path = "/version") public String version() { return "version"; } - @ControllerAdvice - public static class VersionControllerAdvice { + @ModelAttribute("version") + private String getApplicationVersion() { + return getTitle() + " " + getVersion(); + } - @ModelAttribute("version") - public String getApplicationVersion() { - return getTitle() + " " + getVersion(); + @ModelAttribute("footerString") + private String getApplicationVersion(@RequestHeader("host") String hostName) { + return getTitle() + " " + getVersion() + " - " + hostName; + } + + private String getTitle() { + return Optional.ofNullable(VersionHtmlController.class.getPackage().getImplementationTitle()) + .filter(s -> !s.isBlank()) + .orElse("application"); + } + + public String getVersion() { + return Optional.ofNullable(VersionHtmlController.class.getPackage().getImplementationVersion()) + .filter(s -> !s.isBlank()) + .orElse("DEVELOPER"); + } + + @ModelAttribute("hostname") + private String getHostname() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + log.warn(e.toString(), e); } + return ""; + } - @ModelAttribute("footerString") - public String getApplicationVersion(@RequestHeader("host") String hostName) { - return getTitle() + " " + getVersion() + " - " + hostName; - } - - private String getTitle() { - return Optional.ofNullable(VersionControllerAdvice.class.getPackage().getImplementationTitle()) - .filter(s -> !s.isBlank()) - .orElse("application"); - } - - public String getVersion() { - return Optional.ofNullable(VersionControllerAdvice.class.getPackage().getImplementationVersion()) - .filter(s -> !s.isBlank()) - .orElse("DEVELOPER"); - } - - @ModelAttribute("hostname") - public String getHostname() { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - log.warn(e.toString(), e); - } - return ""; - } - - @ModelAttribute("manifest") - private Collection getManifest() { - try { - URL location = getClass().getProtectionDomain().getCodeSource().getLocation(); - String jarFileName = Paths.get(location.toURI()).toString(); - try (JarFile jarFile = new JarFile(jarFileName)) { - ZipEntry entry = jarFile.getEntry(JarFile.MANIFEST_NAME); - try (InputStream in = jarFile.getInputStream(entry)) { - return new String(in.readAllBytes(), StandardCharsets.UTF_8).lines().toList(); - } + @ModelAttribute("manifest") + private Collection getManifest() { + try { + URL location = getClass().getProtectionDomain().getCodeSource().getLocation(); + String jarFileName = Paths.get(location.toURI()).toString(); + try (JarFile jarFile = new JarFile(jarFileName)) { + ZipEntry entry = jarFile.getEntry(JarFile.MANIFEST_NAME); + try (InputStream in = jarFile.getInputStream(entry)) { + return new String(in.readAllBytes(), StandardCharsets.UTF_8).lines().toList(); } - } catch (FileNotFoundException ignored) { - // do nothing if manifest file is not available - } catch (Exception e) { - log.info(e.toString(), e); } - return List.of(getTitle() + " " + getVersion()); + } catch (FileNotFoundException ignored) { + // do nothing if manifest file is not available + } catch (Exception e) { + log.info(e.toString(), e); } - + return List.of(getTitle() + " " + getVersion()); } diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/controller/v1/PartyController.java b/module-server/src/main/java/de/twomartens/wahlrecht/controller/v1/PartyController.java index 8721a23..a2857bd 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/controller/v1/PartyController.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/controller/v1/PartyController.java @@ -1,8 +1,12 @@ package de.twomartens.wahlrecht.controller.v1; +import de.twomartens.wahlrecht.mapper.v1.ElectionMapper; import de.twomartens.wahlrecht.mapper.v1.PartyInElectionMapper; import de.twomartens.wahlrecht.model.dto.ErrorMessage; +import de.twomartens.wahlrecht.model.dto.v1.Election; import de.twomartens.wahlrecht.model.dto.v1.PartyInElection; +import de.twomartens.wahlrecht.service.ConstituencyService; +import de.twomartens.wahlrecht.service.ElectionService; import de.twomartens.wahlrecht.service.PartyService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -20,22 +24,28 @@ import org.mapstruct.factory.Mappers; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; 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 +@Controller @RequiredArgsConstructor @RequestMapping(value = "/wahlrecht/v1") @Tag(name = "Parties", description = "all requests relating to parties") public class PartyController { private final PartyInElectionMapper mapper = Mappers.getMapper(PartyInElectionMapper.class); + private final ElectionMapper electionMapper = Mappers.getMapper(ElectionMapper.class); private final PartyService service; + private final ElectionService electionService; + private final ConstituencyService constituencyService; @Operation( summary = "Returns all stored parties for given election name", @@ -85,10 +95,55 @@ public class PartyController { ) @PutMapping("/party") @SecurityRequirement(name = "basicAuth") - public ResponseEntity putElection(@RequestBody PartyInElection party) { + public ResponseEntity putParty(@RequestBody PartyInElection party) { boolean createdNew = service.storeParty(mapper.mapToDB(party)); return createdNew ? new ResponseEntity<>(HttpStatus.CREATED) : new ResponseEntity<>(HttpStatus.OK); } + + @PostMapping("/party") + public String submitCreatePartyForm(@ModelAttribute PartyInElection party, Model model) { + service.storeParty(mapper.mapToDB(party)); + model.addAttribute("party", party); + model.addAttribute("isCreate", false); + return "partyUpdate"; + } + + @GetMapping("/party") + public String createPartyForm(Model model) { + model.addAttribute("party", new PartyInElection()); + model.addAttribute("isCreate", true); + return "partyCreate"; + } + + @PostMapping("/party/{electionName}/{abbreviation}") + public String submitUpdatePartyForm( + @PathVariable String electionName, + @PathVariable String abbreviation, + @ModelAttribute PartyInElection party, + Model model) { + return submitCreatePartyForm(party, model); + } + + + @GetMapping("/party/{electionName}/{abbreviation}") + public String updatePartyForm( + @PathVariable String electionName, + @PathVariable String abbreviation, + Model model) { + PartyInElection party = mapper.mapToExternal( + service.getPartyByElectionNameAndAbbreviation(electionName, abbreviation)); + model.addAttribute("party", party); + model.addAttribute("isCreate", false); + + return "partyUpdate"; + } + + @ModelAttribute("elections") + Collection elections() { + return electionService.getElections().stream() + .map(electionMapper::mapToExternal) + .toList(); + } } diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/Nomination.java b/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/Nomination.java index 1489b03..7697ddb 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/Nomination.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/Nomination.java @@ -1,9 +1,22 @@ package de.twomartens.wahlrecht.model.dto.v1; import java.util.List; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldDefaults; -public record Nomination(String name, String partyAbbreviation, String electionName, - boolean supportVotesOnNomination, - List candidates) { +@Getter +@Setter +@EqualsAndHashCode +@FieldDefaults(level = AccessLevel.PRIVATE) +public class Nomination { + + String electionName; + String partyAbbreviation; + String name; + boolean supportVotesOnNomination; + List candidates; } diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/PartyInElection.java b/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/PartyInElection.java index a0707b5..e81e547 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/PartyInElection.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/model/dto/v1/PartyInElection.java @@ -1,9 +1,27 @@ package de.twomartens.wahlrecht.model.dto.v1; +import java.util.HashMap; import java.util.Map; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.ToString.Include; +import lombok.experimental.FieldDefaults; +import org.springframework.lang.NonNull; -public record PartyInElection(String electionName, String abbreviation, String name, - Nomination overallNomination, - Map constituencyNominations) { - +@Getter +@Setter +@EqualsAndHashCode +@ToString(onlyExplicitlyIncluded = true) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class PartyInElection { + String electionName; + @Include + String abbreviation; + String name; + Nomination overallNomination; + @NonNull + Map constituencyNominations = new HashMap<>(); } diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/service/NominationService.java b/module-server/src/main/java/de/twomartens/wahlrecht/service/NominationService.java index f0ecbc2..db61bf0 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/service/NominationService.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/service/NominationService.java @@ -17,6 +17,10 @@ public class NominationService { private final CandidateService candidateService; public Nomination storeNomination(Nomination nomination) { + if (nomination == null) { + return null; + } + Optional existingOptional = repository .findByNameAndElectionNameAndPartyAbbreviation(nomination.getName(), nomination.getElectionName(), nomination.getPartyAbbreviation()); diff --git a/module-server/src/main/java/de/twomartens/wahlrecht/service/PartyService.java b/module-server/src/main/java/de/twomartens/wahlrecht/service/PartyService.java index b5e92ec..58db41f 100644 --- a/module-server/src/main/java/de/twomartens/wahlrecht/service/PartyService.java +++ b/module-server/src/main/java/de/twomartens/wahlrecht/service/PartyService.java @@ -28,10 +28,21 @@ public class PartyService { return parties; } + public PartyInElection getPartyByElectionNameAndAbbreviation(String electionName, + String abbreviation) { + Optional optionalParty = partyRepository.findByAbbreviationAndElectionName( + abbreviation, electionName); + if (optionalParty.isEmpty()) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, + "no party found for %s and %s".formatted(electionName, abbreviation)); + } + return optionalParty.get(); + } + public boolean storeParty(PartyInElection partyInElection) { boolean createdNew = true; Optional existingOptional = partyRepository - .findByAbbreviationAndElectionName(partyInElection.getName(), + .findByAbbreviationAndElectionName(partyInElection.getAbbreviation(), partyInElection.getElectionName()); Map constituencyNominations = new HashMap<>(); partyInElection.getConstituencyNominations() @@ -40,6 +51,9 @@ public class PartyService { if (existingOptional.isPresent()) { PartyInElection existing = existingOptional.get(); existing.setName(partyInElection.getName()); + if (constituencyNominations.isEmpty()) { + constituencyNominations.putAll(existing.getConstituencyNominations()); + } partyInElection = existing; createdNew = false; } diff --git a/module-server/src/main/resources/templates/partyCreate.html b/module-server/src/main/resources/templates/partyCreate.html new file mode 100644 index 0000000..8a3cfa7 --- /dev/null +++ b/module-server/src/main/resources/templates/partyCreate.html @@ -0,0 +1,50 @@ + + + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/module-server/src/main/resources/templates/partyUpdate.html b/module-server/src/main/resources/templates/partyUpdate.html new file mode 100644 index 0000000..21f8e26 --- /dev/null +++ b/module-server/src/main/resources/templates/partyUpdate.html @@ -0,0 +1,50 @@ + + + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/module-server/src/main/resources/templates/version.html b/module-server/src/main/resources/templates/version.html index 0cdc343..67c4ad4 100644 --- a/module-server/src/main/resources/templates/version.html +++ b/module-server/src/main/resources/templates/version.html @@ -1,7 +1,7 @@ - <th:block th:text="${version}"/> +